Ruby on Rails Security: Best Practices
Frameworks are primarily designed to help web developers build web applications. In spite of numerous frameworks available, the probability of discovering low-hanging vulnerabilities such as CSRF, or content spoofing (not to talk of XML Entity and Parameter Pollution) is highly possible.
Although some frameworks help developers to secure web applications, developers should not always rely on in-built security features of frameworks to secure apps from unauthorized access. Frankly, securing a web application is an ongoing process. Thus, no security mechanism is entirely good enough to sandbox an application. Yet webmasters can minimize risks with the aid of frameworks.
This article illustrates Ruby on Rails best practices with some of its built-in features, and explains why it is not advisable to rely solely on built-in security features. This tutorial will also show how web developers can use Rails gems such as secure_headers
by Twitter to minimize risks. This piece will also show how hijacking and injection attacks are carried against Ruby application and how webmasters can minimize session hijacking and SQL injection. The concluding part includes links to Ruby on Rails security resources and blogs to help developers increase their knowledge with regards to securing Ruby application.
Brief Overview of Ruby on Rails
Ruby on Rails is simply a web framework for building web applications in Ruby, and the framework helps web developers to secure Ruby application. Moreover, it makes web development less stressful as it allows developers to write less code. Last but not least, Rails prefer convention to configuration. It includes the following guiding principles:
The DRY Principle:
The Don’t Repeat Yourself (DRY) principle simply states that developers should not conform to the idea of writing the same information over and over again. By not writing the same information, the code becomes more maintainable.
Convention over Configuration:
This principle should tell us that Rails is simply an opinionated framework. It assumes that the best way to do many things is to default to its convention rather than dealing endlessly with configuration files.
Read more: 6 Ruby Best Practices Beginners Should Know
Brief overview of Ruby on Rails built-in security features.
First of all, Ruby on Rails is secure by default. But as stated initially, relying on in-built features is not advisable.
For example, although the framework provides Active Record, a library which provides abstraction accessing databases, some query methods and options such as the Calculation Method, Delete All Method, Destroy All Method, Exists? Method, Joins Method, Order Method, and other methods do not sanitize or examine raw SQL arguments. These methods can easily open up to SQL injection. Be careful when using these methods.
In spite of some Ruby on Rails methods to sanitize raw SQL arguments, web developers can still optimize Ruby on Rails’ clever helper methods to minimize SQL injection. There are also Rails gems that can perform far better than in-built Rails features. Although the framework can help developers to build secure apps, security is not plug-and-play. Security depends on developers and the development method in use.
1. Model View Controller (MVC):
Cross-site scripting is exploited when a website fails to sanitize input (i.e Html , Javascript, or VBScript) from users. In Rails, it is easy to sanitize users’ input with the Model View Controller because any data retrieved or stored is passed through a model.
We can also sanitize input or output in our view using the sanitize method. The sanitize method encodes all tags and strips all tags that are blacklisted.
For instance, let’s assume we have common XSS payloads, such as:
<img src=x onerror=prompt(1)>
<img/src=`%00` onerror=this.onerror=confirm(1)
<img src=`%00`&Newline; onerror=alert(1)

When you pass one of the above:
<%= sanitize ‘img src=x onerror=prompt(1)>’ %>
By default, Rails will decide for us by allowing the img src=x
and probably removing the event attribute. Rails will decide for us because we have not blacklisted any XSS payload. I don’t believe in blacklisting payloads—it is better to adhere to secure coding practice. We can rely on Rails_best_practices gem to ensure secure coding practices.
The output will look like this:
<img src=’’x’’>
Rails webmasters should be extra careful when blacklisting and whitelisting XSS payloads. Unlike SQL, JavaScript is a loosely dynamic language. Thus, it is possible to blacklist almost all XSS payloads. Also, there are XSS payloads which have the ability to bypass validation mechanism, but that is beyond the scope of this article.
2. Html_escape() or h()
In Rails, Html_escape()
or h()
—which is an alias of the html_escape() —
is a method for escaping and encoding HTML tag characters. It is the main encoding output method in Rails. For encoding strings like urls, you can use the url_encode(s)
or u(s)
.
In the first example, let the same XSS payload pass to the Html_escape()
:
<%= html_escape ‘<img src=x onerror=prompt(1)>’ %>
The Html_escape()
will make the output appear as:
< ; img src=x onerror=prompt(1)> ;
The Html_escape()
method has converted the < and > characters into HTML entities.
3. Authencity_token
Rails protect applications against CSRF – Cross Site Request Forgery— by including a token named authencity_token
in HTML responses. This token is stored in a user’s session cookie. A session is made up of a hash of values and session IDs. Session is embedded or included in cookies. Thus, every cookie sent to the user’s browser includes the session ID – usually a 32-character string.
In Rails, you can use the following method to save and retrieve values:
Session[:user_id]
= @current_user.id
User.find(session [ : user_id])
Ruby on Rails Security Gems
Although Rails offers many in-built applications to protect against input validation flaws and other web-based attacks, these security mechanisms can’t extend beyond certain limit—they are limited. Personally, I do not believe in Rails’ in-built security features to protect Ruby applications. I prefer to implement diverse security mechanisms to minimize session hijacking, cross-site request forgery, and SQL injection via Rails gems.
Let’s look of some of the most popular security gems that can protect web applications against session hijacking, cross-site request forgery, and SQL injection.
Devise
It is a popular authentication solution for applications in Rails. It provides a number of features such as offering secure password storage using bcrypt to hash salted passwords, user registration, and forgotten password functionality.
The devise gem is built on top of Warden. Warden provides a mechanism for authentication in Rack-based Ruby application. Warden provides the cookie handling that verifies the identity of a logged-in user via session string, in which the ID of a user is stored and masked away from public view. For considerable knowledge on how to use devise effectively, Devise’s README contains all the information to get you started.
Devise Utility Methods:
-
authenticate_user!
—authenticate_user!
method is used for applications that require a user to log in to access their pages. -
current_user!
— Thecurrent_user!
method simply returns the model class relating to the signed in or logged in user. If a user is not yet signed in, it returns nil. -
user_signed _in ?
— This query method just checks if thecurrent_user
method returns a value that isn’t nil. -
sign_in(@user)
— This method is useful to allow a user to log in to a newly created account. -
sign_out(@user)
— This method is useful to allow a user to log out successfully. -
user_session
— This method returns a meta data on a logged in user.
secure_headers
This was developed by the Twitter security team. It is a gem that implements security related HTTP headers into your application’s HTTP responses. secure_headers by Twitter includes security headers, such as a content security policy, to protect against cross-site request forgery, and HSTPS to restrict a browser from communicating with a remote server via https only.
This gem automatically applies the following security headers in the application’s HTTP responses. Bear in mind the following is not a complete list of security headers of secure_headers
.
- Content Security Policy — This security header helps prevent cross-site scripting attacks against applications.
- HTTP Strict Transport Security — Allows the browser to communicate with a remote server via https only.
- X-Frame – Options (XFO) — Prevents your content from being framed and potentially click-jacked.
Now let’s find out how a clever attacker can hijack a session, inject malicious SQL queries, and implement cross-site request forgery in Rails.
Practical Security Best Practices with Ruby on Rails
Session Hijacking and Prevention:
Session hijacking happens when a malicious user steals a legitimate user’s session ID in order to log in to a web application via the victim’s name. This form of attack is possible whether or not a user connects or makes an HTTP request to a remote web server via HTTP or HTTPS. The only difference between HTTP and HTTPS is that an HTTPS connection has extra setup at the beginning. It negotiates a secure channel, and then it sends normal HTTP over that channel.
When a user logs in to a web application, a user ID is saved for future authentication in the session token. It looks like this:
session [: user_id] = user
The attacker can steal this cookie to log in to a web application via the user’s name. Attackers can hijack sessions in diverse ways such as predicting the session token, man-in-the-middle attack, or the infamous XSS.
We can protect our cookies by using a random value in them. Let’s say attackers log in to a web application, they can easily predict the next or previous cookies by observing previous IDs. Thus, randomness of IDs is a must.
Moreover, web developers should not store IDs in the following way:
cookies[:secure_session]
Rather, web developers should call the signed method on cookies to encrypt the value like this:
cookies.signed[: secure_session]
In spite of unpredictable session IDs, we need to secure the way the client makes HTTP requests to remote web servers. Without SSL, cookies can be intercepted in transit. In Rails, there are a few ways to implement SSL.
The best method is to ensure a secure connection for every HTTP request by adding the following to your config file: config/ application.rb
. Doing so ensures that cookies are transmitted in an encrypted manner.
(config.force_ssl = true)
Cross-Site Request Forgery and Prevention:
In session hijacking, cross-site scripting is one method used by attackers to steal cookies and modify session IDs. Unlike XSS, CSRF does not steal cookies to log in via the user’s name. It allows a user to log in to www.mybank.com
. Unknown to the user, mybank.com is vulnerable to CSRF. Later the same user clicks on a new tab to make an HTTP request to www.cyber54.com
to read a post on how to do online banking safely.
A malicious user who happens to be a tutorial writer has set up a CSRF attack on cyber54.com
to transfer a huge amount of money from a user’s account number. The malicious code may look like this:
<iframe src="http://examplebank.com/app/transfermoney?
amount=2200&attackersAccount">
When the user uploads that iframe, the browser makes a POST request to www.mybank.com
It will be processed and the amount of money would be transferred to account no: 247890345.
In Rails we can prevent CSRF by authenticity_token
in HTML responses. This token is also stored within the user’s session cookie. Forms generated in Rails may contain the following code:
<input
Name= "authenticity_token"
type= “hidden”
value=”ghtyu7asdvnTojibBNYY67BshjyerUA+81
+ DD=”/>
The value of authentication_token
differs. When the form and authentication_token
are submitted, Rails verify the request to decide whether the request should be processed or not.
SQL Injection and Prevention
SQL injection is one famous method used by attackers to access the database of a website. SQL injection allows attackers to bypass logins, transfer important files/folders to attackers’ mailbox, and other malicious events.
Let’s say an attacker wants to perform a SQL injection on www.example.com. The attacker might check whether the site is vulnerable to SQL injection by the following code:
http://www.example.com/index.php?id=2'
When the remote web server serves an error page or message suggesting there is an error in our SQL syntax, then example.com is probably vulnerable to SQL injection.
In Rails, applications interact with a database through activerecord, an object-relational mapping (ORM) which by default comes with Rails. Although ORM provides database abstraction, careless handling of input can lead to SQL injection.
Initially, I advised web developers to be extra careful when using Rails activerecord. You may disagree with me but you can check out ActiveRecord::FinderMethods#exists? to understand why it is disastrous to use Rails activerecord against SQL injection.
Wrapping up
Finally, there are many resources detailing other specific mechanisms to minimize and prevent injection attacks, redirection, and hijacking. Below is a list of URLs from where you can learn about Rails security and best coding practices.
- Rails SQL Injection
- Ruby on Rails Security Guide
- RequestForgeryProtection
- SanitizeHelper
- html_escape(s)
Related tutorials you might be interested in:
- Performing a Security Audit for your Code: The Basics
- Using Vault to Secure Your Deployment Secrets
- Tutorial: Automatically Authenticate Email Login Tokens With Rails & Devise
- Check My Code: Tips to Keep Ruby Code Smell-Free
- Preparing & Securing Your Ubuntu Box for Deployment
Author’s Bio
Michael is a budding Cybersecurity Engineer and a technical writer based in Ghana, Africa. He works with AmericanEyes Security as a part-time WordPress security consultant. He is interested in Ruby on Rails and PHP security.