From 9525d4f0346a2fdac13a0253f9180d20104a72d3 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Fri, 2 Sep 2022 23:09:03 +0530 Subject: [PATCH] chore: Improve rack-attack configuration (#5389) fixes: https://github.com/chatwoot/product/issues/540 --- config/initializers/rack_attack.rb | 83 ++++++++++++++++++------------ 1 file changed, 50 insertions(+), 33 deletions(-) diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb index adefee312..6c9c3754a 100644 --- a/config/initializers/rack_attack.rb +++ b/config/initializers/rack_attack.rb @@ -24,6 +24,12 @@ class Rack::Attack allowed_ips = ['127.0.0.1', '::1'] allowed_ips.include?(remote_ip) end + + # Rails would allow requests to paths with extentions, so lets compare against the path with extention stripped + # example /auth & /auth.json would both work + def path_without_extentions + path[/^[^.]+/] + end end ### Throttle Spammy Clients ### @@ -42,50 +48,61 @@ class Rack::Attack throttle('req/ip', limit: 300, period: 1.minute, &:ip) - ### Prevent Brute-Force Login Attacks ### - throttle('login/ip', limit: 5, period: 20.seconds) do |req| - req.ip if req.path == '/auth/sign_in' && req.post? + ### Prevent Brute-Force Super Admin Login Attacks ### + throttle('super_admin_login/ip', limit: 5, period: 5.minutes) do |req| + req.ip if req.path_without_extentions == '/super_admin/sign_in' && req.post? + end + + throttle('super_admin_login/email', limit: 5, period: 15.minutes) do |req| + if req.path_without_extentions == '/super_admin/sign_in' && req.post? + # NOTE: This line used to throw ArgumentError /rails/action_mailbox/sendgrid/inbound_emails : invalid byte sequence in UTF-8 + # Hence placed in the if block + # ref: https://github.com/rack/rack-attack/issues/399 + email = req.params['email'].presence || ActionDispatch::Request.new(req.env).params['email'].presence + email.to_s.downcase.gsub(/\s+/, '') + end + end + + # ### Prevent Brute-Force Login Attacks ### + throttle('login/ip', limit: 5, period: 5.minutes) do |req| + req.ip if req.path_without_extentions == '/auth/sign_in' && req.post? + end + + throttle('login/email', limit: 10, period: 15.minutes) do |req| + if req.path_without_extentions == '/auth/sign_in' && req.post? + # ref: https://github.com/rack/rack-attack/issues/399 + # NOTE: This line used to throw ArgumentError /rails/action_mailbox/sendgrid/inbound_emails : invalid byte sequence in UTF-8 + # Hence placed in the if block + email = req.params['email'].presence || ActionDispatch::Request.new(req.env).params['email'].presence + email.to_s.downcase.gsub(/\s+/, '') + end + end + + ## Reset password throttling + throttle('reset_password/ip', limit: 5, period: 30.minutes) do |req| + req.ip if req.path_without_extentions == '/auth/password' && req.post? + end + + throttle('reset_password/email', limit: 5, period: 1.hour) do |req| + if req.path_without_extentions == '/auth/password' && req.post? + email = req.params['email'].presence || ActionDispatch::Request.new(req.env).params['email'].presence + email.to_s.downcase.gsub(/\s+/, '') + end end ## Prevent Brute-Force Signup Attacks ### - throttle('accounts/ip', limit: 5, period: 5.minutes) do |req| - req.ip if req.path == '/api/v1/accounts' && req.post? + throttle('accounts/ip', limit: 5, period: 30.minutes) do |req| + req.ip if req.path_without_extentions == '/api/v1/accounts' && req.post? end ## Prevent Conversation Bombing on Widget APIs ### throttle('api/v1/widget/conversations', limit: 6, period: 12.hours) do |req| - req.ip if req.path == '/api/v1/widget/conversations' && req.post? + req.ip if req.path_without_extentions == '/api/v1/widget/conversations' && req.post? end ## Prevent Contact update Bombing in Widget API ### throttle('api/v1/widget/contacts', limit: 60, period: 1.hour) do |req| - req.ip if req.path == '/api/v1/widget/contacts' && (req.patch? || req.put?) - end - - # ref: https://github.com/rack/rack-attack/issues/399 - throttle('login/email', limit: 20, period: 5.minutes) do |req| - if req.path == '/auth/sign_in' && req.post? - # NOTE: This line used to throw ArgumentError /rails/action_mailbox/sendgrid/inbound_emails : invalid byte sequence in UTF-8 - # Hence placed in the if block - email = req.params['email'].presence || ActionDispatch::Request.new(req.env).params['email'].presence - email.to_s.downcase.gsub(/\s+/, '') - end - end - - throttle('super_admin_login/email', limit: 20, period: 5.minutes) do |req| - if req.path == '/super_admin/sign_in' && req.post? - # NOTE: This line used to throw ArgumentError /rails/action_mailbox/sendgrid/inbound_emails : invalid byte sequence in UTF-8 - # Hence placed in the if block - email = req.params['email'].presence || ActionDispatch::Request.new(req.env).params['email'].presence - email.to_s.downcase.gsub(/\s+/, '') - end - end - - throttle('reset_password/email', limit: 5, period: 1.hour) do |req| - if req.path == '/auth/password' && req.post? - email = req.params['email'].presence || ActionDispatch::Request.new(req.env).params['email'].presence - email.to_s.downcase.gsub(/\s+/, '') - end + req.ip if req.path_without_extentions == '/api/v1/widget/contacts' && (req.patch? || req.put?) end end