Skip to main content

Lack of protection against brute force attacks - Credentials

Need

Implementation of a robust and secure mechanism to prevent brute force attacks on promotional codes.

Context

  • Usage of Ruby 2.7.0 as the programming language for development
  • Usage of Ruby on Rails for web application development

Description

Non compliant code

class PromoCodesController < ApplicationController
def validate_code
code = params[:code]
promo_code = PromoCode.find_by(code: code)

if promo_code
render json: { valid: true, discount: promo_code.discount }
else
render json: { valid: false }
end
end
end

In the above code, there is a validate_code method in the PromoCodesController which is used to validate if the provided promotional code is valid or not. The method takes a code parameter from the request, and then it tries to find a promotional code in the database that matches the provided code.

If a matching promotional code is found, the method responds with a JSON object indicating that the code is valid and also includes the discount associated with the code. If no matching code is found, the method responds with a JSON object indicating that the code is invalid.

The vulnerability here is that there are no protections in place against brute force attacks. An attacker could potentially automate requests to this endpoint with different codes until they find a valid one. There are no rate limiting or captcha mechanisms in place to prevent or slow down such automated attacks.

Steps

  • Implement rate limiting to prevent multiple login attempts within a short period of time.
  • Implement account lockout after a certain number of failed login attempts.
  • Use strong and unique passwords for user accounts.
  • Implement two-factor authentication to add an extra layer of security.
  • Implement CAPTCHA or reCAPTCHA to prevent automated attacks.
  • Implement a delay or exponential backoff mechanism to slow down brute force attacks.
  • Monitor and log failed login attempts to detect and respond to potential attacks.
  • Regularly update and patch the application and its dependencies to fix any security vulnerabilities.
  • Educate users about the importance of using strong passwords and avoiding password reuse.

Compliant code

class PromoCodesController < ApplicationController
before_action :check_rate_limit, only: [:validate_code]

def validate_code
code = params[:code]
promo_code = PromoCode.find_by(code: code)

if promo_code
render json: { valid: true, discount: promo_code.discount }
else
render json: { valid: false }
end
end

private

def check_rate_limit
client_ip = request.remote_ip
limit = 5
period = 60

count = Rails.cache.fetch("promo_code/#{client_ip}", expires_in: period.seconds) do
0
end

if count >= limit
render json: { error: 'Too many requests' }, status: :too_many_requests
else
Rails.cache.write("promo_code/#{client_ip}", count + 1, expires_in: period.seconds)
end
end
end

The updated code now includes a before_action filter that checks the rate limit before the validate_code action is executed. This filter is defined in the check_rate_limit private method.

In the check_rate_limit method, we first get the client's IP address. We then define a limit for the number of requests (5 requests) and a period for the limit (60 seconds).

We use Rails' caching mechanism to store the number of requests from each IP address. If the cache for the IP address does not exist, we initialize it with 0.

If the number of requests from the IP address has reached the limit, we return a 'Too many requests' error with a 429 status code (Too Many Requests). If the limit has not been reached, we increment the count in the cache.

This way, we limit the number of requests to the validate_code action from each IP address to 5 requests per minute, effectively preventing brute force attacks.

References