mirror of
https://github.com/samsonjs/rack-attack.git
synced 2026-04-27 15:07:41 +00:00
Move Wiki examples into version control. Closes #324
This commit is contained in:
parent
452ea1b849
commit
b0da52a1bd
3 changed files with 180 additions and 4 deletions
|
|
@ -88,8 +88,8 @@ __IMPORTANT__: By default, rack-attack won't perform any blocking or throttling,
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
*Tip:* The example in the wiki is a great way to get started:
|
*Tip:*If you just want to get going asap, then you can take our [example configuration](docs/example_configuration.md)
|
||||||
[Example Configuration](https://github.com/kickstarter/rack-attack/wiki/Example-Configuration)
|
and tailor it to your needs, or check out the [advanced configuration](docs/advanced_configuration.md) examples.
|
||||||
|
|
||||||
Define rules by calling `Rack::Attack` public methods, in any file that runs when your application is being initialized. For rails applications this means creating a new file named `config/initializers/rack_attack.rb` and writing your rules there.
|
Define rules by calling `Rack::Attack` public methods, in any file that runs when your application is being initialized. For rails applications this means creating a new file named `config/initializers/rack_attack.rb` and writing your rules there.
|
||||||
|
|
||||||
|
|
@ -378,7 +378,7 @@ The Rack::Attack middleware compares each request against *safelists*, *blocklis
|
||||||
* Otherwise, if the request matches any **throttle**, a counter is incremented in the Rack::Attack.cache. If any throttle's limit is exceeded, the request is blocked.
|
* Otherwise, if the request matches any **throttle**, a counter is incremented in the Rack::Attack.cache. If any throttle's limit is exceeded, the request is blocked.
|
||||||
* Otherwise, all **tracks** are checked, and the request is allowed.
|
* Otherwise, all **tracks** are checked, and the request is allowed.
|
||||||
|
|
||||||
The algorithm is actually more concise in code: See [Rack::Attack.call](https://github.com/kickstarter/rack-attack/blob/master/lib/rack/attack.rb):
|
The algorithm is actually more concise in code: See [Rack::Attack.call](lib/rack/attack.rb):
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
def call(env)
|
def call(env)
|
||||||
|
|
@ -399,7 +399,7 @@ end
|
||||||
|
|
||||||
Note: `Rack::Attack::Request` is just a subclass of `Rack::Request` so that you
|
Note: `Rack::Attack::Request` is just a subclass of `Rack::Request` so that you
|
||||||
can cleanly monkey patch helper methods onto the
|
can cleanly monkey patch helper methods onto the
|
||||||
[request object](https://github.com/kickstarter/rack-attack/blob/master/lib/rack/attack/request.rb).
|
[request object](lib/rack/attack/request.rb).
|
||||||
|
|
||||||
### About Tracks
|
### About Tracks
|
||||||
|
|
||||||
|
|
|
||||||
93
docs/advanced_configuration.md
Normal file
93
docs/advanced_configuration.md
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
## Advanced Configuration
|
||||||
|
|
||||||
|
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](https://github.com/kickstarter/rack-attack/issues/106) for more discussion.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# 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](https://github.com/kickstarter/rack-attack/issues/73) for more discussion.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
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](https://github.com/kickstarter/rack-attack/issues/110) for more discussion.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
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](https://github.com/kickstarter/rack-attack/issues/113) for more discussion.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
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](https://github.com/kickstarter/rack-attack/issues/111) for more discussion.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# 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](https://github.com/kickstarter/rack-attack/issues/47) for more discussion.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# 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
|
||||||
|
```
|
||||||
83
docs/example_configuration.md
Normal file
83
docs/example_configuration.md
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
## Example Configuration
|
||||||
|
|
||||||
|
If you just go ahead and copy this to `/config/initializers/rack_attack.rb`, then 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. If this isn't enough for you, check out [Advanced Configuration](advanced_configuration.md) too.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class Rack::Attack
|
||||||
|
|
||||||
|
### 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 blacklisting and
|
||||||
|
# whitelisting). 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) do |req|
|
||||||
|
req.ip # unless req.path.start_with?('/assets')
|
||||||
|
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.now.to_i/:period}:logins/ip:#{req.ip}"
|
||||||
|
throttle('logins/ip', limit: 5, period: 20.seconds) do |req|
|
||||||
|
if req.path == '/login' && req.post?
|
||||||
|
req.ip
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Throttle POST requests to /login 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' && req.post?
|
||||||
|
# return the email if present, nil otherwise
|
||||||
|
req.params['email'].presence
|
||||||
|
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
|
||||||
|
```
|
||||||
Loading…
Reference in a new issue