Preventing security issues in Ruby on Rails (based on OWASP cheatsheet)

Krzysztof Kempiński
kkempin’s dev blog
5 min readJun 28, 2017

--

Although core team and the community behind Ruby on Rails is working very hard to ensure that this framework is providing high level of security, 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.

Ruby on Rails provides security guidelines worth reading. You can find it here http://guides.rubyonrails.org/security.html

In that article I would like to give you few tips how to prevent potential security problems. This is a part of a list gathered by OWASP organisation for Ruby on Rails framework.

OWASP is worldwide not-for-profit charitable organisation focused on improving the security of software. Their most known initiative is OWASP Top 10 Project — it’s a document about the top ten most critical web application security risks. It’s across all frameworks or libraries, but they have some specific cheatsheets for most known frameworks, languages and technologies (like Ruby on Rails for example).

Now, let’s analyze that cheatsheet for RoR and let’s see how we can actually prevent these security issues.

Command Injection

Not exactly RoR related. Has roots in Ruby itself. It’s about executing, either in Ruby or in OS a command that comes from outside world (like from the user interface).

eval("ruby code") - running Ruby code
`ls -al` - running OS command
Kernel.exec("rm -rf") - running OS command

Prevention: You should never run any of these if an argument comes from the user and is not validated and filtered.

SQL Injection

Ruby on Rails uses ActiveRecord ORM for communication with database. We query for some data and in many cases to build that query we need some input from the user (like what is typed in search field). This is a very fragile place in the code. Imagine a piece of code like this:

Product.where("name like ‘" + params[:search] + "‘")

search comes directly from the outside world and can be anything. It’s glued to the SQL statement without any other actions on it so easily someone can do anything on our DB.

Prevention: use build-in ActiveRecord mechanisms for SQL injection prevention. If you need to build your own query and use user input as a part of it, you can first prepare the query and use proper parameterization ( ActiveRecord::Base.connection.raw_connection.prepare).

Cross-site Scripting (XSS)

Imagine code like this in your view template:

<%= link_to "My Website", @user.website %>

If @user.website contains JavaScript code, someone can steal data from your browser!
Rails starting from version 3.0 has some build-in mechanisms to prevent XSS attacks. By default string data that are shown in the templates is escaped. You can extract it using methods like raw, content_tag or html_safe but these are all bad examples of what you should be doing.

Prevention: 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.

Sessions

Default Ruby on Rails backend for sessions are cookies stored in the browser. And be default there is no expiration date set which means that session will never expire. It means that you should not store any sensitive information in that default configuration

Prevention: use database backend to store sessions. (YourApp::Application.config.session_store :active_record_store).

Authentication

Rails doesn’t provide any default mechanism for authentication. A lot of young developers try to write something which is usually very vulnerable to attacks.

Prevention: 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. But it also means that paths to resources are intuitive and guessable and it’s very easy to access resources that should not be exposed to everybody.

Prevention: use some access control gems like cancan or pundit to restrict access to specific resources using some rules.

CSRF (Cross Site Request Forgery)

This attack method works by including 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
protect_from_forgery

Worth notice is that GET HTTP requests are not secured with it.

Prevention: don’t make an exceptions to skip that mechanism like protect_from_forgery except :show .

Mass Assignment

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 ActiveModel::ForbiddenAttributesError .

Prevention: always use strong parameters and explicite point out which parameters are accepted (params[:user].require(:user).permit(:name, :email)). Don’t permit all (params[:user].require(:user).permit!) !

Redirects and Forwards

You should not blindly use user input as a target of page redirection.

if path = URI.parse(params[:url]).path
redirect_to path
end

because it can lead to the XSS attack:

redirect_to params[:to]

http://example.com/redirect?to[status]=200&to[protocol]=javascript:alert(0)//

Prevention: instead use a whitelist of possible URLs or domains and try user input against that list before making any redirect.

Dynamic Render Paths

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.

Prevention: avoid having user input in the paths or use some filtering mechanism to be sure you are allowing to render only some limited scope of files.

Cross Origin Resource Sharing

Sometimes you need to share some resources accross many domains. 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.

Prevention: use rack-cors gem and in config/application.rb specify your configuration like this:

module Sample
class Application < Rails::Application
config.middleware.use Rack::Cors do
allow do
origins 'someserver.example.com'
resource %r{/users/\d+.json},
headers: ['Origin', 'Accept', 'Content-Type'],
methods: [:post, :get]
end
end
end
end

Sensitive Files

Many projects nowadays are hosted on GitHub or any other publicly available servers. There are some fragile files in every Ruby on Rails application that should not be a part of version controlling.

Prevention: 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.

Encryption

Ruby on Rails uses OS encryption. You should almost never write your own solutions for encryption.

Prevention: use only build-in libraries for encryption.

Want to know first about new articles from that blog?

Subscribe to my newsletter now! — http://eepurl.com/cVPm_v

If you like this article and consider it useful for you, please support it with 💚

--

--

Krzysztof Kempiński
kkempin’s dev blog

IT expert. Ruby on Rails/iOS/Elixir programmer. Blogger. Podcaster.