CSRF(Cross-site Request Forgery Attack) and ways to combat it in Rails
While I was working on making a website with a form that accepts user responses using rails framework and testing to see if the requests and responses as well as the routes and controller actions are working properly, an unexpected error occurred during my testing session. This curious thing is called the “Invalid Authenticity Token.”
This error is basically telling me that I didn’t include the piece of code with “authenticity token” in it. As you can see in the below screenshot, I have included this piece of code with input type as hidden to hide it from the user.
A valid authentication token will be randomly generated by Rails app and stored in the session. When the user submit the form, the request along with session data containing the “authenticity_token” will be sent to the server. Rails will then look for the “authenticity_token” and compare it to the one hidden in the session to determine whether or not the request from the user is legitimate.
However, why do we need this authentication token to be sent with the request we make, the answer is to combat the type of hacker attacks known as “Cross-site Request Forgery.”
Cross-site Request Forgery
“Cross-site Request Forgery,” also known as CSRF, Sea Surf, or XSRF, is a type of confused deputy attack whereby an attacker tricks a victim into performing actions that cause change of state such as transferring funds from the victim’s bank account to that of the attacker. Leveraging the user’s authentication and authorization to send a forged web request to the target web server by misleading the browser into thinking that it’s a legitimate request made by the user.
Cross-site request forgery attack is most effective when the use is authenticated on the domain which means that the user must be logged in to the website or applications for the attack to succeed. When the user is authenticated by entering the account and password, the web app will create a session with a session id that is unique for the user and store it in the cookie that is sent back to the client. If the attacker hijacks the unique session id, the hijacker will then be able to trick the server into thinking that the request made with this particular session id is indeed authorized by the user.
Here are some ways that the attacker can manipulate the users into sending false requests that they do not mean to make.
HTTP GET Request Attack
The HTTP GET request is one way that the attacker can leverage the session id that is stored on the user’s computer to trick the user into sending unsolicited request through social engineering. The forged request is usually embedded underneath a link or image link that is sent to the user by the attacker. Once the user clicks on the link, the cookie that is stored on the user’s computer for the target domain will be sent to the server along with the request. Since the session id stored in the cookie is identified as a valid id, the server will accept the request and initiate the action that is requested. The code examples below demonstrate what these kind of malicious links can look like.
HTTP POST Request Attack
However GET requests are not the only HTTP method that the attacker can abuse, POST request is another way that an attack can be initiated on the user. Most of the state-changing requests are typically done through the POST request so websites and apps are more likely to accept state-changing requests through POST request such as submitting a form. POST request attack is slightly harder to do, since the attacker must append a request body which contains the actual form data to the request. Below is an example piece of code that demonstrates the use of form to launch HTTP POST request attack.
How to Combat CSRF Attacks
These are actually web security threats that we need to really guard against and there are some counter measures that we as can employ in our code to combat the CSRF attacks.
One of the most used method in safeguarding against Cross-site Request Forgery attack is the used of an authentication token that is associated with the user and is sent with every state-changing request. This line of code below should be included in every form in order to generate an authenticity token for every form.
form_authenticity_token is a method that is being called by the web server to generate the authenticity token and store it as a hidden field for the form. This approach is called the “synchronizer token pattern.” What this code does is to randomly generate csrf_token that is hidden in each form. When a POST request is made, such token will be sent with the session data and the application will compare the token that it stored against the one that is sent with the request. In order to make a successful request, the attacker must guess the same token that is stored in the current user session to be able to trick the server into handling the false request. Therefore it is highly unlikely that the attacker will not be able to pass the correct token to initiate the forged request.
When working with Rails, the ApplicationController that is inherited from the ActionController should include this below piece of code to turn on the request forgery protection.
The countermeasure will raise an exception when the token sent by the attacker is not matched with the token that is hidden in the form.
As I have transitioned from writing HTML code with HTML tags to using ActionView helper methods to write embedded Ruby in tags mingled with HTML. I have noticed that I no longer need to explicitly add the CSRF token to the form when I am working with form_tag and form_for to generate forms for user to submit requests.
The magic lies in this highlighted line of code that exists in the application.html.erb file below.
When csrf_meta_tags is inlcuded in the application.html.erb inside the views folder, the CSRF token is then written into the page via the csrf_meta_tags helper method in any forms that is generated by using form_for or form_tag.
We know it is magical that csrf_meta_tags does what it does but what is behind the magic work exactly.
Underneath the hood this piece of code shown above is where the magic is being manifested. csrf_meta_tags is a helper method that is being called on to insert these two meta tags, one for the parameters and the other one for the token of the Cross-site Request Forgery protection, into the dynamic form as hidden fields. Therefore, we do not need to manually add these tags into the forms generated by form_tag and form_for helpers.
In Rails 4, the csrf_token was used to be a single randomly generated token per session. In Rails 5, the Per-form CSRF Tokens was introduced as a way for developer to generate a unique token that is specific to a HTTP action and method for each form which means that each token will only be valid for the action and method that the token is stored in. In order to enable this functionality, a couple lines of code need to be included in the controller and the application.rb file.
However, adding self.per_form_csrf_tokens = true to every controller can be a tedious task. If only there is a way to add it to all the controllers at once. Well, there is. Adding this piece of code shown below in the application.rb file inside the config folder can automatically enable per_form_csrf_tokens on every controller.
CSRF attacks are most relevant with cookies because they are automatically sent with every request. Since most Rails applications use cookie-based sessions, they are intrinsically vulnerable to CSRF attacks. It is common practice to use persistent cookies to store user information for an extended period of time. As such, the cookies will not be cleared, the CSRF countermeasures will not be as effective. One way to remedy this issue is to add this piece of code into the ActionController.
The above code will be called when a CSRF token is not present or is invalid on any non-GET requests. When the above exception is thrown, it pretty much destroys any cookies that are currently store on the user’s computer.
Cross-site Request Forgery attacks are most effective on cookie-based sessions. Since using cookies to store session information is a common practice among many web applications, it is vital for us to have the awareness and tools to guard against such malicious attacks. For Rails applications, authentication tokens are an essential component in preventing the attackers from using our session information to trick us into sending unwanted requests. The creation of Per-form CSRF token in Rails 5 has also given us the ability to improve the countermeasures to safeguard against the threats that are present on the web. Improving and leveraging the tools we have better should be a step in the right direction in combating Cross-sit Request Forgery attacks.