Secure Your Rails Application

xdite, Rocodev

Secure Your Rails Application

The Basics


Top #1 Rails Blog in Chinese

Rails developer since 2007

“Rails” “Girl”

Rocodev @ Taiwan

Secure Your Rails Application

Rails security


SQL injection


From Hacker’s view



Is Rails safe?

Rails is (relatively) SAFE

compare to other web frameworks

Secure by default

HTML Escape

XSS attack

Since Rails 3.0+

SQL Escape

SQL injection

(from the beginning)

Authenticity Token

Cross Site Request Forgery

(from the beginning)

Exception Page

( stupid PHP debug mode )

(from the beginning)

Password Encrypted

plaintext password

(default by 99% Rails auth gem)

Sensitive data filtered from log

password from log

(from the beginning)

Filename Sanitization

path attack

(default by popular Rails gem)

PHP way won’t work in Rails

But … is Rails safe from hacker?

…probably not.

Rails is a FRAMEWORK

it has hackable patterns

framework’s defeat

developer’s mistakes

COMMON mistakes

#1. massive assignment


Rails provide effective way to design forms

<%= f.text_field :title %><%= f.text_field :body %>


It also prvoide easy guessable convetion to hacked in.

<input id="topic_title" name="topic[title]" size="30" type="text"><input id="topic_body" name="topic[body]" size="30" type="text">

If ….

<input id="topic_title" name="topic[title]" size="30" type="text"><input id="topic_body" name="topic[body]" size="30" type="text"><input id="topic_user_id" name="topic[user_id]" size="30" type="text">

Fake DOM in Chrome Inspector

Most controller

class TopicsController < ApplicationController    def edit     @topic = Topic.find(params[:id])     if @topic.update_attributes(params[:topic])       redirect_to topic_path(@topic)     else       render :edit     end    endend



class User < ActiveRecord::Base  has_many :rolesend

role_ids => Getter / Setter

Hack this way

<input id="user_title" name="user[title]" size="30" type="text"><input id="user_body" name="user[body]" size="30" type="text"><input id="user_role_ids" name="user[role_ids]" size="30" type="text">

everyone likes to set admin as “1”

Where to look :

Possible Solutions

whitelist attribute ( remove in Rails 4)

Advanced Solution


Advanced Solution

def create  @form =, artist:   if @form.validate(params[:song_request])     ....

Advanced Solution

require 'reform/rails' class UserProfileForm < Reform::Form  include DSL  include Reform::Form::ActiveRecord   property :email,        on: :user   model :user   validates :email, presence: trueend

#2. admin



Basic Solution

Basic Solution

def admin?; is_admin; end

Basic Solution

#3. bypass RESTful




Rails even provide conveninent example!!

# config/routes.rb 
# This is a legacy wild controller route that's not recommended for   RESTful applications.# Note: This route will make all actions in every controller  accessible via GET requests.# match ':controller(/:action(/:id(.:format)))'

Possible solutons

#4. match in routing


Where to look


1. refactor to RESTful

2. using right verb : get, post, put , delete

delete ‘/article/delete/:id’, :to => “articles#destroy” :as => “delete_article”

3. add via

match ‘/article/delete/:id’, :to => “articles#destroy” :as => “delete_article”, :via => :delete

#5. bypass HTML Escape

Safe Buffer

Rails provide html escape by default

// SAFEdef render_post_title(post)  link_to(post.title, post_path(post))end

Complex helper

Easy happened on list, breadcrumb..etc.

// UNSAFEdef render_post_title(post)  str = “”  str += “<li>”  str += link_to(post.title, post_path(post))  str += “</li>”  return raw(str) // unescape...orzend

Where to look

Bad Smell

might be vulnerable

Basic Solution

Break into partials

// SAFEdef render_post_title(post)  render :partial => "posts/title_for_helper", :locals => { :title => post.title }end

5.1 TinyMCE on UGC

Basic Solution

sanitize the tags

def s(html)  sanitize( html, :tags => %w(table thead tbody tr td th ol ul li div span font   img sup sub br hr a pre p h1 h2 h3 h4 h5 h6),   :attributes => %w(style src href size color) )end

#6.bypass SQL escape



SQL escape in ORM by default

// SAFEUser.where([“name LIKE ?”, params[:q])


// UNSAFEUser.find_by_sql("name LIKE &rsquo;%#{params[:q]}%&rsquo;")

People like drop plain SQL


// UNSAFEUser.where(“email = ‘#{params[:email]}’”).first// won’t escape

=> SLELECT “users”.* From “users” WHERE (email = ‘’ OR ‘1’) LIMIT 1

They just don’t know how to use “where” in right ways.

Where to look

Basic Solution

#7. same secret token


to verify signed cookies.


Where to look & Solution

#8. scopes

EDIT action (UNSAFE)

class TopicsController < ApplicationController  before_filter :login_required  before_filter :check_permission, :only => [:edit]  def edit    @topic = Post.find(params[:id])  endend

EDIT action (SAFE)

class TopicsController < ApplicationController  before_filter :login_required   def edit    @topic = current_user.posts.find(params[:id])  endend

Where to look & Solution

# 9. Upgrades

Remote Code Execution


Nuke Cloud

Wallpaper from “”

Upgrade to 3.2.11+



Fork me on Github