Rails

Security

The main goals

  • Common Vulnerabilities
  • How to awoid them with Rails?

Security depends on the people using the framework!

In order to prevent attacks, minimize their impact and remove points of attack, first of all, you have to fully understand the attack methods in order to find the correct countermeasures.

Command Injection

While the power of these commands is quite useful, extreme care should be taken when using them in a Rails based application. Usually, its just a bad idea.

Example 1


def some_action
  path = "#{Rails.root}/public/#{params[:user_path]}"
  `ls #{path}`
end
						

Example 2


def some_action
  str = params[:user_input]
  eval(str)
end
						

Why is it dangerous and how to prevent?

Example 1


params[:user_path] => '; echo '' > ./config/database.yml'

solution => Use 'system' instead of ``=> system("ls", path)
						

Example 2


params[:user_path] => "`echo '' > ./config/database.yml`"

solution => Validate input with RegExp, length, use params whitelist, etc...
						

Mass Assignment

Although the major issue with Mass Assignment has been fixed by default in base Rails specifically when generating new projects, it still applies to older and upgraded projects so it is important to understand the issue and to ensure that only attributes that are intended to be modifiable are exposed.

What are the potential problems of the code below?


def signup
  @user = User.new(params[:user])
end
						

Solution

Use Strong Parameters.


def create
  @user = User.create(user_params)
end

def user_params
  params.require(:user).permit(:name, :age, ...)
end
						

SQL Injection

Ruby on Rails is often used with an ORM called ActiveRecord. Many use cases protect for SQL Injection out of the box. However, it is possible to write code that allows for SQL Injection.

Why it is dangerous?


User.where("username = '#{params[:username]}' AND encrypted_password = '#{params[:password]}'")
						

What if we inject this into input?


Vasyl' OR 1=1)--
						

Parameters sanitizing


ActiveRecord =>User.where("username = ? AND encrypted_password = ?", params[:username], params[:password])
or
ActiveRecord =>User.where(username: params[:username], encrypted_password: params[:password])
						

Cross Site Scripting(XSS)

By default, in Rails 3.0 protection against XSS comes as the default behavior.

Let's look at the code below


<%= raw @product.name %>   
<%= @product.name.html_safe %>
<%= content_tag @product.name %>
						

Now try to inject some simple JS code


< script type="text/javascript"> alert("Your hard disk is corrupted."); < /script >
or more private
< script> document.write(document.cookie); < /script >
						

Possible solutions


<%= strip_tags("some<script>alert('hello')</script>") %>
<%= html_escape '< img src=x onerror=prompt(1) >' %>

<%= sanitize '< img src=x onerror=prompt(1) >' %>
<%= sanitize '< img src=x onerror=prompt(1) >', attributes: %w(src onerror) %>
						

Cross Site Request Forgery(CSRF)

Ruby on Rails has specific, built in support for CSRF tokens. Modern versions of Rails protect against CSRF attacks by default by including a token named authenticity_token within HTML responses. This token is also stored within the user’s session cookie - when a request is received by Rails it checks one against the other. If they do not match, an error is raised. To enable it, or ensure that it is enabled, find the base ApplicationController and look for a directive such as the following


class ApplicationController < ActionController::Base
  protect_from_forgery
						

!! Rails does not provide CSRF protection for any HTTP GET request !!

Example

Bad link example


%a{href: "http://www.google.com/", onclick:"
  var f = document.createElement('form');
  var input = document.createElement('input');
  input.type = 'hidden';
  input.name = '_method';
  input.value = 'delete';
  f.appendChild(input);
  f.style.display = 'none';
  this.parentNode.appendChild(f);
  f.method = 'POST';
  f.action = '/products/12';
  f.submit();
  return false;
"}= 'Go to Google'
						

Cross Origin Resource Sharing(CORS)

Occasionally, a need arises to share resources with another domain. For example, a file-upload function that sends data via an AJAX request to another domain.

The receiving site should whitelist only those domains allowed to make such requests. Whitelist in Rails


gem 'rack-cors', :require => 'rack/cors'

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
					

Usefull links

OWASP(Open Web Application Security Project) Cheatsheet
RoR Security Guide

Brakeman

Use brakeman, an open source code analysis tool for Rails applications, to identify many potential issues.

Ruby on Rails Code Quality Gems

Q&A
No questions? No answers!

Created by Vasyl Lasiak / @vlasiak