Weak credential policy - Password Change Limit - Ruby

Weak credential policy - Password Change Limit - Ruby

Need

Enforcement of password change limits

Context

  • Usage of Ruby version 2.7.2 for Ruby programming and development
  • Usage of Rails framework for building web applications

Description

Non compliant code

        class UsersController < ApplicationController
  def update_password
    @user = User.find(params[:id])
    if @user.update(user_params)
      redirect_to @user, notice: 'Password was successfully updated.'
    else
      render :edit
    end
  end

  private
    def user_params
      params.require(:user).permit(:password, :password_confirmation)
    end
end
        
        

In the above code, we have a UsersController with an update_password method. This method is used to update the password of a user. It first finds the user with the provided id from the parameters, then it attempts to update the user's password with the new password provided in the parameters.

The vulnerability here is that there is no limit to the number of times a password can be updated in a day. This means that an attacker could potentially use a script to continuously change a user's password, causing a denial of service for that user as they would constantly need to reset their password. This could also potentially allow an attacker to guess a user's password if they are able to change it enough times.

Steps

  • Implement a password change limit policy.
  • Add a counter to track the number of password change requests made by a user in a day.
  • Set a maximum limit for the number of password change requests allowed in a day.
  • Check the counter before allowing a password change request to proceed.
  • If the counter exceeds the maximum limit, reject the password change request and display an appropriate error message.
  • Consider implementing additional security measures such as rate limiting or CAPTCHA to prevent abuse of the password change functionality.

Compliant code

        class UsersController < ApplicationController
  MAX_PASSWORD_CHANGES_PER_DAY = 3

  def update_password
    @user = User.find(params[:id])
    if @user.password_changes_today < MAX_PASSWORD_CHANGES_PER_DAY
      if @user.update(user_params)
        @user.increment!(:password_changes_today)
        redirect_to @user, notice: 'Password was successfully updated.'
      else
        render :edit
      end
    else
      flash[:error] = "You have reached the maximum number of password changes allowed for today."
      render :edit
    end
  end

  private
    def user_params
      params.require(:user).permit(:password, :password_confirmation)
    end
end
        
        

The updated code introduces a new constant MAX_PASSWORD_CHANGES_PER_DAY which is set to 3. This constant represents the maximum number of times a user can change their password in a day.

In the update_password method, before updating the user's password, we check if the user has already reached the maximum number of password changes for the day by comparing @user.password_changes_today with MAX_PASSWORD_CHANGES_PER_DAY.

If the user has not reached the limit, we proceed to update the password and increment the password_changes_today counter by 1 using the increment! method.

If the user has reached the limit, we set a flash error message and render the edit page again. The error message informs the user that they have reached the maximum number of password changes allowed for the day.

Please note that you need to add a password_changes_today column to your User model and reset this counter to 0 every day for each user. This can be done with a daily scheduled task.

This solution helps to prevent abuse of the password change functionality by limiting the number of password changes a user can make in a day.

References