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 | 65 +++++++++++++++++++----------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb index adefee312d07..6c9c3754a17d 100644 --- a/config/initializers/rack_attack.rb +++ b/config/initializers/rack_attack.rb @@ -24,6 +24,12 @@ def allowed_ip? 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,38 +48,29 @@ def allowed_ip? 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? - 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? - 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? + ### 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 - ## 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? + 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 - throttle('super_admin_login/email', limit: 20, period: 5.minutes) do |req| - if req.path == '/super_admin/sign_in' && req.post? + # ### 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 @@ -81,12 +78,32 @@ def allowed_ip? 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 == '/auth/password' && req.post? + 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: 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_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_without_extentions == '/api/v1/widget/contacts' && (req.patch? || req.put?) + end end # Log blocked events