Advanced Configuration

Tee Parham edited this page Jul 20, 2018 · 7 revisions

If you're feeling ambitious or you have a very particular use-case for Rack::Attack, these advanced configurations may help.

:beetle::warning: Much of this code is untested. Copy-paste at your own risk!

Exponential Backoff

By layering throttles with linearly increasing limits and exponentially increasing periods, you can mimic an exponential backoff throttle. See #106 for more discussion.

# Allows 20 requests in 8  seconds
#        40 requests in 64 seconds
#        ...
#        100 requests in 0.38 days (~250 requests/day)
(1..5).each do |level|
  throttle("logins/ip/#{level}", :limit => (20 * level), :period => (8 ** level).seconds) do |req|
    if req.path == '/login' && req.post?
      req.ip
    end
  end
end

Rack::Attack::Request Helpers

You can define helpers on requests like localhost? or subdomain by monkey-patching Rack::Attack::Request. See #73 for more discussion.

class Rack::Attack::Request < ::Rack::Request
  def localhost?
    ip == "127.0.0.1"
  end
end

Rack::Attack.safelist("localhost") { |req| req.localhost? }

Blocklisting From ENV Variables

You can have Rack::Attack configure its blocklists from ENV variables to simplify maintenance. See #110 for more discussion.

class Rack::Attack
  # Split on a comma with 0 or more spaces after it.
  # E.g. ENV['HEROKU_VARIABLE'] = "foo.com, bar.com"
  # spammers = ["foo.com", "bar.com"]
  spammers = ENV['HEROKU_VARIABLE'].split(/,\s*/)

  # Turn spammers array into a regexp
  spammer_regexp = Regexp.union(spammers) # /foo\.com|bar\.com/
  blocklist("block referer spam") do |request|
    request.referer =~ spammer_regexp
  end
end

Reset Specific Throttles

By doing a bunch of monkey-patching, you can add a helper for resetting specific throttles. The implementation is kind of long, so see #113 for more discussion.

Rack::Attack.reset_throttle "logins/email", "user@example.com"

Blocklisting From Rails.cache

You can configure blocklists to check values stored in Rails.cache to allow setting blocklists from inside your application. See #111 for more discussion.

# Block attacks from IPs in cache
# To add an IP: Rails.cache.write("block 1.2.3.4", true, expires_in: 2.days)
# To remove an IP: Rails.cache.delete("block 1.2.3.4")
Rack::Attack.blocklist("block IP") do |req|
  Rails.cache.read("block #{req.ip}")
end

Throttle Basic Auth Crackers

An example implementation for blocking hackers who spam basic auth attempts. See #47 for more discussion.

# After 5 requests with incorrect auth in 1 minute,
# block all requests from that IP for 1 hour.
Rack::Attack.blocklist('basic auth crackers') do |req|
  Rack::Attack::Allow2Ban.filter(req.ip, :maxretry => 5, :findtime => 1.minute, :bantime => 1.hour) do
    # Return true if the authorization header not incorrect
    auth = Rack::Auth::Basic::Request.new(req.env)
    auth.credentials != [my_username, my_password]
  end
end
Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.