Using (and Testing) Rack::Attack to Improve the Security of Your Rails App
05 Jun 2015
Rack::Attack is a rack middleware intended to protect Rails
applications through customized throttling and blocking. I started using it
after attending a talk from the person who created it, and I thought it was
a brilliant option for developers who want to increase the security of their
applications with minimal effort.
You can prevent attempts at blunt-forcing passwords by throttling requests with
the email or username being attacked, or thwart troublesome scrapers or other
offenders by throttling requests from IP addresses making large volumes of requests.
Rack::Attack makes protecting your applications easy but still provides quite a
bit of freedom to choose what to throttle, block, whitelist, or blacklist.
Using Rack::Attack
Setting up Rack::Attack is relatively easy and well explained in the
documentation, but I’ll include
the entire setup process here. First, of course, add gem "rack-attack".
Next, set up production caching. Add the gem(s) you’d like to use
and configure in config/environments/production.rb. I’m using “memcachier”
and “dalli”. If you’re using Heroku, which I am, you’ll
also need to add the
MemCachier add-on, which
will provide your environmental variables.
Now that caching is set up, you need to set up Rack::Attack itself. First, add
config.middleware.use Rack::Attack to config/application.rb. Then create the
file rack-attack.rb under config/initializers. This is where you can
customize your use of Rack::Attack. Here’s my configuration, based on the
example
from the Rack::Attack documentation.
My version is different from the configuration mainly because I ran into a lot
of problems when I tried to test it. First, I discovered that using longer time
periods like 300 requests per 5 minutes is not feasible for testing. Second,
I’d assumed that for multiple login pages, it would simply work to make separate
throttle blocks. Testing said otherwise, which does make sense, and thus the
elsif statements. Finally, I discovered while clicking around locally that
assets do, in fact, need to be excluded, if just to avoid headaches while in
development.
Testing Rack::Attack
So, testing this is pretty important, but how do you test it? Thanks to
this post,
I was able to figure out how this is possible. First and most importantly,
you need to add the Rack::Test gem:
gem 'rack-test', require: 'rack/test'.
Then, I placed my tests in spec/config/initializers/rack-attack_spec.rb.
However you want to test, the setup for Rack::Test should be the same (though
changed accordingly if you’re not using RSpec):
When it comes to the actual tests, you have to be careful with email addresses
and IP address to keep the tests from interfering with each other, either by
causing other tests with the same credentials to fail or by causing other tests
to falsely pass by throttling by IP address instead of by email or vice versa.
So - warning, large block of code ahead! - these are the tests I wrote:
It was pretty exciting when all these tests passed, and now anyone trying to
brute-force my application or cause other problems is going to face a bit more
of a challenge. So, if you’re interested in having a little more security for
your Rails applications, I encourage you to definitely check out Rack::Attack,
and to also try testing it using Rack::Test!