To deal with this, Rails has the
RequestForgeryProtection module that gives access to
protect_from_forgery. It’s now set by default when you create a new Rails project and takes the form of a single line of code in the application controller:
with parameter is actually the
forgery_protection_strategy parameter, it tells Rails how to behave when a CSRF attack is identified.
The Different Forgery Protection Strategies
There are 3 strategies currently built into the
RequestForgeryProtection ::ProtectionMethods module of
null_session strategy is the default one. The gotcha here is that by default Rails 5 generates the
ApplicationController file with the
exception strategy, but the default strategy inside ActionPack is actually the
null_session one, which can be confusing.
This is the one Rails 5 sets up by default. It will raise an exception if a CSRF attack occurs:
This strategy ensures that the execution stops right after the
verify_authenticity_token check if the request is fraudulent.
This strategy will not cause the app to crash but will instead nullify the session for the duration of the request.
Note that while this is now the default, Rails 3 didn’t generate the
ApplicationController file with the
with: :exception parameter, so you didn’t touch a thing and have an old app that you kept on updating, you might have the
null_session strategy set up and not even know it.
The third strategy,
reset_session, simply calls the
@controller as you can see here.
Important Note On Security
It is very important to keep in mind that fraudulent requests will go through with the
reset_session strategies. The only action taken by these strategies is to make sure the session is empty during the request.
As an illustration here are the logs for a fraudulent request:
Started POST "/posts" Processing by PostsController#create as */* Can't verify CSRF token authenticity. Completed 200 OK in 2ms (Views: 1.0ms | ActiveRecord: 0.0ms)
I think that it is quite counter intuitive and might cause serious security concerns depending on your implementation. Brakeman even considers using other strategies to be an issue.
If you want to learn more about this, Jason Yeo explains it in depth in his great article, “When Rails’ protect from forgery Fails”. If you’re unsure of your implementation, the safe approach is to use the
Building A Custom Strategy
In some cases it can be interesting to build your own strategy. Let’s say you don’t want to return 500s when a CSRF attack occurs, but you still want to be warned or at least have better logs.
How It Works
First, let’s take a look at how all this works:
The requests goes through
verify_authenticity_token. If there is an issue, it then logs a warning and then calls the
handle_unverified_request method of the
Getting An Exception
First, let’s get a
InvalidAuthenticityToken exception. If you want to do it properly, you probably want to do this writing tests, but to demonstrate this I’ll be using curl.
Start your server and call a POST action that has
protect_from_forgery activated using curl. This way you’re sure that you don’t have the proper authenticity token.
$ curl -X POST -I http://localhost:3000/secure_post_action
You should see this in your logs:
Can't verify CSRF token authenticity. Completed 422 Unprocessable Entity in 1ms (ActiveRecord: 0.0ms) ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken)
Adding A Custom Forgery Protection Strategy
Now that we know how to quickly test, let’s add our new strategy.
Any forgery propection strategy needs to be initalized with a controller and respond to
handle_unverified_request. So the bare minimum here is:
Then you can use it by changing your controller code:
Now you can get your strategy to do whatever you’d like and check the result by running the curl command. To give you ideas, here’s what I implemented on a project to get a bit more logs while still falling back on the
Since you scrolled this far, you might be interested in some other things I wrote!