# frozen_string_literal: true
require “digest/md5”
# Configuration for rack_attack class Rack::Attack # rubocop:disable Style/ClassAndModuleChildren <%- if devise? -%>
LOGIN_PATH = "/users/sign_in" REGISTRATION_PATH = "/users"
<%- end -%>
### Configure Cache ### # If you don't want to use Rails.cache (Rack::Attack's default), then # configure it here. # # Note: The store is only used for throttling (not blocklisting and # safelisting). It must implement .increment and .write like # ActiveSupport::Cache::Store # Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new ### Throttle Spammy Clients ### # If any single client IP is making tons of requests, then they're # probably malicious or a poorly-configured scraper. Either way, they # don't deserve to hog all of the app server's CPU. Cut them off! # # Note: If you're serving assets through rack, those requests may be # counted by rack-attack and this throttle may be activated too # quickly. If so, enable the condition to exclude them from tracking. # Throttle all requests by IP (60rpm) # # Key: "rack::attack:#{Time.now.to_i/:period}:req/ip:#{req.ip}" throttle("req/ip", limit: 300, period: 5.minutes, &:ip) def self.pentesting_request?(path, query_string) CGI.unescape(query_string) =~ %r{/etc/passwd} || path.include?("/etc/passwd") || path.include?("wp-admin") || path.include?("wp-login") || /\.php$/.match?(path) end # Block suspicious requests for '/etc/passwd' or wordpress specific paths. # After 3 blocked requests in 10 minutes, block all requests from that IP for 5 minutes. blocklist("fail2ban/pentesters") do |req| # `filter` returns truthy value if request fails, or if it's from a previously banned IP # so the request is blocked Rack::Attack::Fail2Ban.filter("pentesters-#{req.ip}", maxretry: 3, findtime: 10.minutes, bantime: 5.minutes) do # The count for the IP is incremented if the return value is truthy pentesting_request?(req.path, req.query_string) end end
<%- if devise? -%>
### 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 /users/sign_in by IP address # # Key: "rack::attack:#{Time.now.to_i/:period}:logins/ip:#{req.ip}" throttle("logins/ip", limit: 5, period: 20.seconds) do |req| req.ip if req.path == LOGIN_PATH && req.post? end # Throttle POST requests to /users/sign_in by email param # # Key: "rack::attack:#{Time.now.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 |req| if req.path == LOGIN_PATH && req.post? # return a filtered email if present, nil otherwise email_address = req.params.dig("user", "email").presence email_address && Digest::MD5.hexdigest(email_address) end end ### Prevent Brute-Force Email Harvesting ### # For most authentication endpoints, we attempt to hide the presence of an # email address from any outside users. # # However our registration form will not allow known email addresses to be used. # Which can be used as a potential method of verifying email addresses. # # Throttling this endpoint is an attempt to limit the effectiveness of this farming. # Throttle POST requests to /users by IP address # # Key: "rack::attack:#{Time.now.to_i/:period}:registrations/ip:#{req.ip}" throttle("registrations/ip", limit: 5, period: 20.seconds) do |req| req.ip if req.path == REGISTRATION_PATH && (req.post? || req.put? || req.patch?) end
<%- end -%>
### Custom Blocklist Response ### self.blocklisted_response = lambda do |request| if pentesting_request?(request.fetch("PATH_INFO"), request.fetch("QUERY_STRING")) [301, { "Location" => "/" }, []] else [302, { "Location" => "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }, []] end end ### Custom Throttle Response ### # By default, Rack::Attack returns an HTTP 429 for throttled responses, # which is just fine. # # If you want to return 503 so that the attacker might be fooled into # believing that they've successfully broken your app (or you just want to # customize the response), then uncomment these lines. # self.throttled_response = lambda do |env| # [ 503, # status # {}, # headers # ['']] # body # end
end