Created
May 6, 2019 14:50
-
-
Save czj/bbe8724d217ce7498846530bc80012c6 to your computer and use it in GitHub Desktop.
Sample Rack::Attack configuration file
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class AppConfig | |
class << self | |
# Lookup via | |
# https://www.ultratools.com/tools/ipWhoisLookupResult | |
# https://www.whatismyip.com/ip-whois-lookup/ | |
BLOCKED_IPS = Set.new( | |
[ | |
"6.5.4.3", | |
"5.4.3.2", | |
"4.3.2.1", | |
] | |
).freeze | |
def blocked_ips | |
BLOCKED_IPS | |
end | |
SAFE_IPS = Set.new( | |
[ | |
"6.6.6.6", # your IP | |
"1.3.3.7", # another IP | |
# Remove this IP if you need to test Rack::Attack | |
"127.0.0.1", | |
] | |
).freeze | |
def safe_ips | |
SAFE_IPS | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# In development env, this middleware will only work with caching | |
# enabled (it is required to store count of access per IP). | |
# | |
# In test env, testing this middleware requires : | |
# - activate Rails' cache (in-memory) | |
# - remove 127.0.0.1 from AppConfig's list of safe ips | |
# - run an integration test many times | |
# - add back 127.0.0.1 | |
# - clear cache and de-activate it | |
class Rack::Attack | |
AppConfig.safe_ips.each { |e| safelist_ip(e) } | |
AppConfig.blocked_ips.each { |e| blocklist_ip(e) } | |
# Cache the file on boot to make responses quick | |
THROTTLED_BODY = File.read(Rails.root.join("public", "429.html")).squish.freeze | |
# Be nice, reply with a legitimate alert | |
self.throttled_response = lambda do |_env| | |
[429, { "Content-Type" => "text/html" }, [THROTTLED_BODY]] | |
end | |
# Create the non-changing responses body, on boot, once and for all | |
BLOCKLISTED_BODY = SecureRandom.base64(1337).freeze | |
# Return status 200 to make their crawler think all went well | |
self.blocklisted_response = lambda do |_env| | |
[200, { "Content-Type" => "text/html" }, [BLOCKLISTED_BODY]] | |
end | |
# - only throttle a specific domain | |
# | |
# - guard condition excludes assets from throttling | |
# (imperative with a 12factor app that serves assets itself) | |
# | |
# - allow 100 requests in 5 minutes = 1 action every 3s | |
# a (crazy) bot will do that, a human probably never | |
# | |
# - do not use a too small windows (like 1 minute) beause a customer | |
# in a hurry might go quickly back and forth pages during checkout | |
# for a minute, then settle down (and using a browser's back button | |
# might reload the page) | |
throttle("request/ip", limit: 100, period: 5.minutes) do |request| | |
if request.host == HOST_TO_PROTECT && !request.path.start_with?("/assets/", "/packs/") | |
request.ip | |
end | |
end | |
# The most crawled URLs could be treated specifically : | |
# | |
# throttle("request/bots", limit: 1, period: 15) do |request| | |
# request.ip if AppConfig.blocked_ips.include?(request.ip) | |
# end | |
# | |
# prefix = %w(/a/ /b/ /c/ /d/ /e/ /compos/).freeze | |
# if request.path.start_with?(*THROTTLED_URLS_PREFIX) | |
# ... | |
# end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment