Rails is a very healthy and mature platform for developers to use. Its prolific community and extensive documentation keep developers’ work sharp and robust. This robustness is essential when keeping platforms protected against potential vulnerabilities and exploits. Cross-site request forgery, also known as CSRF or XSRF, is one such vulnerability. 

You might be wondering how you can keep your platform secure to make sure no bad actors have an opportunity to exploit vulnerabilities in the system. 

Our intention with this post is to inform you about CSRF vulnerabilities and how to mitigate them in Rails applications. 

By the end, you can expect to know what they look like, why it’s crucial to be aware of them, and how to prevent them from affecting your business. 

What Is CSRF?

If you’ve never heard of CSRF, XSRF, cross-site request forgery, or any variation of what this acronym describes, then let us give you a quick rundown. 

Cross-site request forgery is an attack that takes advantage of the end user’s privileges over their session. As the name suggests, the attacker sidesteps most security measures by tricking the user into submitting a forged request (GET, POST, or PUT, for example) with malicious content from outside the target site. 

Most CSRF attacks take advantage of the user’s inability to discern if a harmless element contains malicious code. Bad actors mask these elements using social engineering tactics like chat or emails. These exploits come from seemingly trusted sources, hijacking the user’s trust. 

When the user clicks on one of these links, a targeted URL can execute a change in the database (email change, bank transfer, etc.) to a vulnerable web application where the user currently has a session open. This could be as simple as just having a tab open with the target website while you’re logged in. 

CSRF attacks are some of the most widespread forms of malicious exploits on the web. Web platforms are exposed to them daily, and countless unsuspecting users fall prey to their predatory tricks. 

Moreover, no platform can be entirely impervious since the attack can always hijack a valid end user’s authentication to reach the system. We can’t close all the doors of access without taking away convenience from a valid end user. 

For this reason, it’s imperative to make sure that we as developers, take all measures necessary to reduce the avenues of attack. 

Examples of Attacks

Now, you might be wondering what a CSRF attack looks like in the wild. Well, let’s explore some examples. 

The Horny Fish

An unsuspecting bank account user receives an email from a seemingly trusted party while they have an open session in the browser. 

Rails CSRF attack 1

Well, this seems innocent enough, right? Let’s take a closer look at that link. 

Rails CSRF attack 2

Uh-oh! That’s a no-no. 

As you can see, average users seldom bother to check the actual URL of a hyperlink—regardless of its origin. This makes them easy targets to this kind of attack. 

These exploits are widespread in phishing attacks and more sophisticated social engineering attacks. Even the most trained engineers can fall victim to simple attacks like this.

The Curious Seller

In a similar situation, a sales representative of some company receives an email at work — while they have their system tools open — with an attractive headline and promise of reward. 

Rails CSRF attack 3

All right, that seems legitimate. Let’s check it out. 

Rails CSRF attack 4

Our victim sees this email, and at first glance, it seems legitimate. They then proceed to click on the link, only to see a blank screen. 

Wait, where’s the content? What happened? 

It might seem like this is a case of “Oops! The CRM system sent an invalid link to a broken page.” However, once you start checking things like the sender’s email address, this seemingly trustworthy email starts falling apart on close examination. 

Let’s check what’s under the hood. 

Rails CSRF attack 6

No! Roxxx strikes again! And this time, the victim didn’t even see it coming. 

This exploit is especially concerning because the attacker could be targeting administrative users and cause significant harm to a business and its infrastructure with a little more sophistication. 

How to Prevent CSRF Attacks

All right, we’ve seen how creative attackers can find ways to exploit other people’s valid sessions by hiding their malicious code in harmless-looking elements and inviting users to trigger an action. And all this is done by hijacking people’s trust with sly social engineering tactics. 

So, how can we protect our systems and our users from these exploits? 

There are several ways we can mitigate the risk of exploitation from the platform perspective. Let’s go through them in the context of a Rails website. 

Routing Structure

First, we need to reevaluate how we design the routing structure of our website. The first attack was successful because the system accepted a state-changing action with a GET request. This structure is already a bad practice that should be avoided even outside the scope of security. 

To fix this, change the route file and set the application’s action to accept a POST, PUT, or any other protocol depending on the type of action that will be performed. 

Rails.application.routes.draw do    
    root 'home#index'    

    # get 'transfer', to: 'transactions#create' # BAD    
    put 'transfer', to: 'transactions#create' # GOOD
end

However, it’s essential to point out that this strategy will not protect us from the second attack since it comes from a form tag submitting a POST automatically by JavaScript. In this case, our user didn’t even get a chance to understand what was going on. 

Additionally, the attacker can adapt this kind of exploit to work with a JavaScript Ajax request and submit any form of protocol or parameters necessary to achieve the attacker’s goal. 

Action Confirmation

Second, it’s recommended—but not always necessary—to implement a confirmation mechanism into any critical state-changing action. This design-based security feature is generally adopted on admin systems and account management portals. 

For this fix, implement a middle step between critical actions to require the user to confirm the action to perform. 

This mechanism would typically allow the victim to have a chance to cancel a potentially malicious attack from submitting to the server. However, some users might find this multistep process cumbersome and tedious in systems that require frequent changes from the user. That’s why some systems choose to do without it. 

Origin Confirmation

Third, it goes without saying, but you must always confirm the origin of any request submitted to your server. Be it by the REFERRER info found on the request HEADER or any other method, you can use this simple tactic to weed out much potential damage. 

To enable this, you can use gems like rack-cors that work as gatekeeper middleware, preventing invalid requests from being processed. 

Gem file
gem 'rack-cors'

config/initializers/cors.rb
use Rack::Cors do
    allow do origins 'localhost:3000',
        '127.0.0.1:3000',
        /\Ahttp:\/\/192\.168\.0\.\d{1,3}(:\d+)?\z/
        # regular expressions can be used here
        resource '/file/list_all/',
            headers: 'x-domain-token'
        resource '/file/at/*',
            methods: [:get, :post, :delete, :put, :patch, :options, :head],
            headers: 'x-domain-token',
            expose: ['Some-Custom-Response-Header'],
            max_age: 600
        # headers to expose
    end

    allow do origins '*'
        resource '/public/*',
            headers: :any,
            methods: :get
        # Only allow a request for a specific host
        resource '/api/v1/*',
            headers: :any,
            methods: :get,
            if: proc { |env| env['HTTP_HOST'] == 'api.example.com' }
    end
end

Incidentally, your system might require you to accept requests from origins outside your domain, which is very common. Still, you can have a set of whitelisted sources the server validates and mitigate the risks. 

Rails CSRF Token

Finally, the most robust mitigation strategy available in our arsenal is using CSRF tokens. The server generates these tokens, links them to the user session, and stores them in the database. 

This token is then injected into any form presented to the client as a hidden field. When the client correctly submits the form for validation, it passes the token back to the server. 

Rails already has this security mechanism included in its ActionController module under the RequestForgeryProtection class. 

To activate it, you need to include the following line in the ApplicationController class: 

protect_from_forgery with: :null_session

You can choose what kind of action to perform when the server finds an offending request: 

  • null_session: Provides an empty session during request but doesn’t reset it completely. (This is the default action if none is specified.)
  • reset_session: Resets the session.
  • exception: Raises an ActionController::InvalidAuthenticityToken exception.

After doing this, go to the base template view file in your application, usually the Application.html.erb. Include this line in the header: 

<%= csrf_meta_tags %>

And that’s it. 

With this simple method, Rails’ JavaScript framework will update every form tag on the page with the generated token. Subsequently, any invalid requests arriving from outside our platform will be flagged and ignored. 

Make Your Security Sustainable

Having proper security is one of those intangible values you can appreciate only when put to the test. Most managers will find it challenging to convince stakeholders of the financial investment and time needed to provide a solid level of security. 

Yet in this day and age, ever more sophisticated attacks are becoming more widespread. So, it’s difficult not to justify the investment in reassurance and peace of mind provided by adequate security measures. 

Thankfully, nowadays, modern platforms like Rails are making it more accessible and manageable to keep security standards high. That way, you can focus on what matters: bringing value to your clients. 

This post was written by Juan Reyes. Juan is a full-stack engineer by profession and a dreamer by heart who crossed the seas to reach Japan following the promise of opportunity and challenge. Initially working for a big IT company as a developer Juan decided to expand his circles and reach out to different people with a desire to bring something to the world. With his company, and expertise in search platforms, micro-services, and machine learning he is working to revolutionize the recruitment process and help people find jobs and opportunities in Japan more easily while supporting the industries that need the talent to achieve their goals.