diff --git a/README.md b/README.md index f08c44c..b33c64a 100644 --- a/README.md +++ b/README.md @@ -258,10 +258,12 @@ Rack::Attack.throttle("requests by ip", limit: 5, period: 2) do |request| end # Throttle login attempts for a given email parameter to 6 reqs/minute -# Return the email as a discriminator on POST /login requests +# Return the *normalized* email as a discriminator on POST /login requests Rack::Attack.throttle('limit logins per email', limit: 6, period: 60) do |req| if req.path == '/login' && req.post? - req.params['email'] + # Normalize the email, using the same logic as your authentication process, to + # protect against rate limit bypasses. + req.params['email'].to_s.downcase.gsub(/\s+/, "") end end diff --git a/docs/example_configuration.md b/docs/example_configuration.md index 069f04e..e1aaa38 100644 --- a/docs/example_configuration.md +++ b/docs/example_configuration.md @@ -53,7 +53,7 @@ class Rack::Attack # Throttle POST requests to /login by email param # - # Key: "rack::attack:#{Time.now.to_i/:period}:logins/email:#{req.email}" + # Key: "rack::attack:#{Time.now.to_i/:period}:logins/email:#{normalized_email}" # # Note: This creates a problem where a malicious user could intentionally # throttle logins for another user and force their login requests to be @@ -61,8 +61,9 @@ class Rack::Attack # on wood!) throttle("logins/email", limit: 5, period: 20.seconds) do |req| if req.path == '/login' && req.post? - # return the email if present, nil otherwise - req.params['email'].presence + # Normalize the email, using the same logic as your authentication process, to + # protect against rate limit bypasses. Return the normalized email if present, nil otherwise. + req.params['email'].to_s.downcase.gsub(/\s+/, "").presence end end diff --git a/examples/rack_attack.rb b/examples/rack_attack.rb index 43f1348..7423f39 100644 --- a/examples/rack_attack.rb +++ b/examples/rack_attack.rb @@ -13,8 +13,10 @@ Rack::Attack.throttle "logins/ip", limit: 2, period: 1 do |req| end # Throttle login attempts per email, 10/minute/email +# Normalize the email, using the same logic as your authentication process, to +# protect against rate limit bypasses. Rack::Attack.throttle "logins/email", limit: 2, period: 60 do |req| - req.post? && req.path == "/login" && req.params['email'] + req.post? && req.path == "/login" && req.params['email'].to_s.downcase.gsub(/\s+/, "") end # blocklist bad IPs from accessing admin pages