# These settings and configuration were copied from # https://github.com/kickstarter/rack-attack/wiki/Example-Configuration # # When copied, they stated: # "You'll be safe from 95% of bad requests. This won't stop sophisticated # hackers, but at least you can sleep more soundly knowing that your application # isn't going to be accidentally taken down by a misconfigured web scraper in # the middle of the night." # class Rack::Attack # # Disable +Rack::Attack+ for Kubernetes health checks. Kubernetes uses # a non-standard IP address to access the app server internally, and # causes errors when being loaded into `IPAddr`. This safelist will # prevent the IPs from going through `IPAddr` at all, thus preventing # the error. # # Key: "rack::attack:ignore/k8s" KUBERNETES_IP_ADDRESS = '127.0.0.1:0' safelist('ignore/k8s') do |request| request.ip == KUBERNETES_IP_ADDRESS end # # Disable +Rack::Attack+ when an admin is signed in. Prevents issues # while large amounts of admins (such as a customer service # department) are accessing the site with the same IP. # # Key: "rack::attack:ignore/cookies/admin" safelist('ignore/admins') do |request| request.cookies['cache'] == 'false' end # # Disable +Rack::Attack+ for configured IP addresses # # Key: "rack::attack:ignore" if ENV['WORKAREA_RACK_ATTACK_IGNORE_IP_ADDRESSES'].present? IGNORED_IP_ADDRESSES = ENV['WORKAREA_RACK_ATTACK_IGNORE_IP_ADDRESSES'] .split(',') .map { |s| IPAddr.new(s) } safelist('ignore') do |request| IGNORED_IP_ADDRESSES.any? { |ip| ip.include?(request.ip) } end end ### Throttle Spammy Clients ### # # Throttle all requests by IP (60rpm) # # Key: "rack::attack:#{Time.current.to_i/:period}:req/ip:#{req.ip}" throttle('req/ip', limit: 300, period: 5.minutes) do |request| white_listed_paths = %w(/assets /product_images /content_blocks /admin) request.ip unless request.path.start_with?(*white_listed_paths) end ### Prevent Brute-Force Login Attacks ### # The most common brute-force login attack is a brute-force password # attack where an attacker simply tries a large number of emails and # passwords to see if any credentials match. # # Another common method of attack is to use a swarm of computers with # different IPs to try brute-forcing a password for a specific account. # Throttle POST requests to /login by IP address # # Key: "rack::attack:#{Time.current.to_i/:period}:logins/ip:#{req.ip}" throttle('logins/ip', limit: 5, period: 20.seconds) do |request| if request.path == '/login' && request.post? request.ip end end # Throttle POST requests to /login by email param # # Key: "rack::attack:#{Time.current.to_i/:period}:logins/email:#{req.email}" # # Note: This creates a problem where a malicious user could intentionally # throttle logins for another user and force their login requests to be # denied, but that's not very common and shouldn't happen to you. (Knock # on wood!) throttle('logins/email', limit: 5, period: 20.seconds) do |request| if request.path == '/login' && request.post? # return the email if present, nil otherwise request.params['email'].presence end end # Throttle POST requests to /users/account by IP address # # A malicious user can abuse the signup form to determine email addresses # associated to accounts. # # Key: "rack::attack:#{Time.current.to_i/:period}:signups/ip:#{req.ip}" throttle('signups/ip', limit: 5, period: 1.minute) do |request| if request.path == '/users/account' && request.post? request.ip end end # Throttle POST requests to /contact by IP address # # Key: "rack::attack:#{Time.now.to_i/:period}:contact/ip:#{req.ip}" throttle('contact/ip', limit: 3, period: 1.minute) do |request| if request.path == '/contact' && request.post? request.ip end end # Throttle POST requests to /email_signup by email address # # Key: "rack::attack:#{Time.now.to_i/:period}:email_signup/email:#{req.email}" throttle('email_signup/email', limit: 10, period: 20.minutes) do |request| if request.path == '/email_signup' && request.post? # return the email if present, nil otherwise request.params['email'].presence end end # Throttle POST requests to /email_signup by IP address # # Key: "rack::attack:#{Time.now.to_i/:period}:email_signup/ip:#{req.ip}" throttle('email_signup/ip', limit: 10, period: 20.minutes) do |request| if request.path == '/email_signup' && request.post? request.ip end end # Throttle POST requests to /forgot_password by email address # # Key: "rack::attack:#{Time.now.to_i/:period}:password_reset/email:#{req.email}" throttle('password_reset/email', limit: 10, period: 20.minutes) do |request| if request.path == '/forgot_password' && request.post? # return the email if present, nil otherwise request.params['email'].presence end end # Throttle POST requests to /forgot_password by IP address # # Key: "rack::attack:#{Time.now.to_i/:period}:password_reset/ip:#{req.ip}" throttle('password_reset/ip', limit: 10, period: 20.minutes) do |request| if request.path == '/forgot_password' && request.post? request.ip end end # Block IP addresses that are hammering credit card endpoints # # This can happen when credit card fraudsters are trying to use checkout # and/or saved credit card functionality as a way to check whether a card # number is usable. # blocklist('req/credit_cards') do |request| key = [request.ip, request.cookies['user_id']].reject(&:blank?).join('/') Rack::Attack::Allow2Ban.filter(key, maxretry: 10, findtime: 1.hour, bantime: 1.day) do request.path =~ /place_order|credit_cards/ && !request.get? end end # Block IP addresses that are hammering promo code endpoints # # We don't want people trying to brute force promo codes. # blocklist('req/promo_codes') do |request| key = "promo_codes:#{request.ip}" Rack::Attack::Allow2Ban.filter(key, maxretry: 10, findtime: 1.hour, bantime: 1.day) do !request.get? && request.path =~ /promo_code/ end end end