Comments:"Rack::Attack: protection from abusive clients » Backing & Hacking — Kickstarter"
URL:http://www.kickstarter.com/backing-and-hacking/rack-attack-protection-from-abusive-clients
I'm excited to introduce Rack::Attack, a ruby rack middleware for throttling abusive requests. We depend on it to keep Kickstarter fast and reliable.
If you've looked at web server logs, you know there are some weird clients out there. Malicious scripts probe for exploits. Scrapers download the same page dozens of times each second, or request the 10,000th page of comments for a post with only 2 comments.
Tackling each curious anomaly that threatens your site's reliability saps developer productivity and happiness. Rack::Attack lets you throttle abusive requests with just a few lines of code. Check out the README for more details about how it works. Seriously, the README does a great job explaining how to use it. Okay, I'm going to assume you've skimmed the README. Moving on.
We limit the number of requests that can be made per IP address in a short time period like this:
Rack::Attack.throttle('ip', limit: x, period: y) do |req| req.ip end
Pro tip: to allow occasional bursts, set the limit and period to an higher multiple. Instead of limit: 1, period: 1
(1 req/s), do limit: 10, period: 10
. The long-term average still can't exceed 1req/s.
Typical visitors never come close to our limit. But aggressive scrapers often do. Of course we graph it.
Those shark fin-shaped spikes are our database thanking us.
For the security of our users, we have a stricter throttle for login attempts. This makes it very time consuming for attackers to guess users' passwords.
# Throttle logins per ip Rack::Attack.throttle("login_ip", limit: x, period: y) { |req| req.ip if req.post? && req.path == "/session" } # Throttle logins per email param (regardless of ip) Rack::Attack.throttle("login_email", limit: x, period: y) { |req| req.params['email'].presence if req.post? && req.path == "/session" }
We also use the IPCat ruby library to detect requests from well-known datacenters. You could block login attempts from datacenters with this:
Rack::Attack.blacklist('bad_login_ip') { |req| req.post? && req.path == "/session" && IPCat.datacenter?(req.ip)) }
Rack::Attack can also track requests without blocking them. On Feb 14, we launched our iPhone app, and wanted an easy way to monitor the HTTP requests it generates. Since the app uses a special header, it was simple to track with Rack::Attack:
Rack::Attack.track("ios_app") { |req| req.env.key?("HTTP_OUR_CUSTOM_HEADER") }
We are very happy with how it went:
We rely on Rack::Attack to let developers quickly track and throttle requests. It helps keep our site reliable, so we can spend more energy building better features. We're glad to make it publicly available to the open source community.