We have published many articles about Rails benefits and how good this framework for different types of marketplaces or web applications. But there’s nothing perfect in this world. So every single programming language or a framework has specific problems to be fixed by the developers. In this article, we’ll show you security issues solutions in RoR.
Despite Ruby on Rails has many advantages, it is not possible that any framework will cover every possible scenario and there is always an application on top which is usually a source of the vulnerabilities. Every business wants to create secure web applications and websites, and as Ruby on Rails agency, we’d like to give a few tips how to prevent potential security problems.
Also, we would like to say thanks to Krzysztof Kempiński for his Preventing security issues in Ruby on Rails article we used for our material.
You may hear about OWASP (Open Web Application Security Project) organization that works on improving the security of software. In our article we will use the list of tips to improve Rails security gathered by OWASP.
So here are quick basic Ruby on Rails security tips for developers:
Command injection is not exactly RoR related but more refers to Ruby. It’s about executing, either in Ruby or in OS a command that comes from outside world (like from the UI). Be careful using it in Rails based applications. And do never run any of these if an argument comes from the user and is not validated and filtered.
eval("ruby code") - running Ruby code
`ls -al` - running OS command
Kernel.exec("rm -rf") - running OS command
eval(“ruby code”) – running Ruby code
ls -al – running OS command
Kernel.exec(“rm -rf”) – running OS command
Ruby on Rails uses ActiveRecord ORM for communication with the database. To build a query for some data we need some input from the user and this is a fragile place in the code. Imagine a piece of code like this:
Product.where("name like ‘" + params[:search] + "‘")
The search is glued to the SQL statement without any other actions on it so easily someone can do anything on our database. Prevention: use built-in ActiveRecord mechanisms for SQL injection prevention. And use caution not to build SQL statements based on user-controlled input. A list of more realistic and detailed examples is here: rails-sqli.org
Cross-site Scripting (XSS)
By default, in Rails 3.0 protection against XSS comes as the default behavior. When string data is shown in views, it is escaped prior to being sent back to the browser. This goes a long way, but there are common cases where developers bypass this protection – for example, to enable rich text editing. You can extract it using methods like
html_safe but these are all bad examples of what you should be doing. Don’t do like this:
<%= link_to "My Website", @user.website %>
Consider accepting input from the user as a markup language for rich text (markdown or textile). You can also use
#sanitize method to filter-out any markup tags that you don’t want to have. Be careful, this method has been shown to be flawed numerous times and will never be a complete solution. OWASP provides more general information about XSS in a top-level page: Cross-site Scripting (XSS).
By default, Ruby on Rails uses a Cookie based session store. What that means is that unless you change something, the session will not expire on the server. That means that some default applications may be vulnerable to replay attacks. It also means that sensitive information should never be put in the session.
The best practice is to use a database based session, which thankfully is very easy with Rails:
Store your sessions with the database backend.
Rails does not provide authentication by itself. However, most developers using Rails leverage libraries such as Devise or AuthLogic to provide authentication. To avoid vulnerabilities use existing gems that proved to be good for this, like
Devise. Also good practice will be to enhance security even more by for example changing validation for a length of password that is 6 characters for
Devise to something like 10.
Insecure Direct Object Reference or Forceful Browsing
RESTful application design is very popular in Rails and is a good practice. That means that paths are often intuitive and guessable. To protect against a user trying to access or modify data that belongs to another user, it is important to specifically control actions. Out of the gate on a vanilla Rails application, there is no such built-in protection. It is possible to do this by hand at the controller level.
It is also possible and probably recommended, to consider resource-based access control libraries such as cancancan (cancan replacement) or punditto do this. This ensures that all operations on a database object are authorized by the business logic of the application. p.s. We wrote about these gems in our article The most used RoR gems for all the time.
CSRF (Cross Site Request Forgery)
This attack method works by including a malicious link in a page that accesses your web application that the user is believed to have authenticated. Ruby on Rails has a mechanism for CSRF tokens:
class ApplicationController < ActionController::Base
Also, note that by default Rails does not provide CSRF protection for any HTTP GET request. There is a top-level OWASP page for Cross-Site Request Forgery (CSRF).
Starting from Rails 4.0 the problem of mass assignment is more or less solved. You can’t just pass input from the browser (that for example represent HTML form) and create new object in the database. Doing this in a code
User.create(params[:user]) will lead to
Make sure you always use strong parameters and explicit point out which parameters are accepted:
params[:user].require(:user).permit(:name, :email)). Don’t permit all (
Redirects and Forwards
Web applications often require the ability to dynamically redirect users based on client-supplied data. To clarify, dynamic redirection usually entails the client including a URL in a parameter within a request to the application. Once received by the application, the user is redirected to the URL specified in the request.
You should not blindly use user input as a target of page redirection.
if path = URI.parse(params[:url]).path
because it can lead to the XSS attack:
Ways to make these attacks impossible in your app:
- Avoid using redirects and forwards
- Create a list of trusted URLs to sanitize input
- Don’t allow URLs as user input. Otherwise, make sure that each supplied value and authorized for each user
- Notify users that they are leaving your site and make them confirm the action for all redirects
There is a more general OWASP resource about unvalidated redirects and forwards.
Dynamic Render Path
In Rails, controller actions and views can dynamically determine which view or partial to render by calling the “render” method. If user input is used in or for the template name, an attacker could cause the application to render an arbitrary view, such as an administrative page.
Care should be taken when using user input to determine which view to render. If possible, avoid any user input in the name or use some filtering mechanism to be sure you are allowing to render only some limited scope of files.
You should never use user input to build a path for a template (partial) to render because it’s very easy to access any file in your application preparing right input for that attack.
Cross Origin Resource Sharing
Occasionally the need to share some resources across many domains appears. For example, you want to upload a file using AJAX request and send it to the other app. The receiving side should specify a whitelist of domains that are allowed to make those requests. There are few HTTP headers that control that.
You can use
rack-cors gem and in
config/application.rb specify your configuration like this:
class Application < Rails::Application
config.middleware.use Rack::Cors do
headers: ['Origin', 'Accept', 'Content-Type'],
methods: [:post, :get]
Many Ruby on Rails apps are open source and hosted on publicly available source code repositories. Whether that is the case or the code is committed to a corporate source control system, there are certain files that should be either excluded or carefully managed.
Don’t include these files to your repository:
/config/database.yml - May contain production credentials.
/config/initializers/secret_token.rb - Contains a secret used to hash session cookie.
/db/seeds.rb - May contain seed data including bootstrap admin user.
/db/development.sqlite3 - May contain real data.
Analyze how your application deals with sensitive data in the store. There are some simple precautions:
- Avoid storing sensitive data if it’s not necessary
- Don’t put sensitive data in your server code
- Either encrypt sensitive data or send it via an encrypted channel to avoid eavesdropping
Ruby on Rails uses OS encryption, so you shouldn’t write your own solutions for encryption. Please, use only build-in libraries for encryption.
Devise by default uses bcrypt for password hashing, which is an appropriate solution. Typically, the following config causes the 10 stretches for production: /config/initializers/devise.rb
config.stretches = Rails.env.test? ? 1 : 10
In RoR Cheatsheet you can also find an information about:
- Security-related headers
- Business Logic Bugs and
- Attack Surface
Read more about security on the official RoR site.
Not to miss anything interesting subscribe to our weekly newsletter!