Secure Your Rails Application

xdite, Rocodev

Secure Your Rails Application

The Basics

xdite@rocodev.com

@xdite

Top #1 Rails Blog in Chinese

Rails developer since 2007

“Rails” “Girl”

Rocodev @ Taiwan

Secure Your Rails Application

Rails security

XSS

SQL injection

Phishing…

From Hacker’s view

Anonymous

Overview

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

Background

Rails provide effective way to design forms

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

Background

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

role_ids

Or

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

Reform

Advanced Solution

def create  @form = SongRequestForm.new(song: Song.new, artist: Artist.new)   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

http://example.org/admin

99%

Vulnerability

Basic Solution

Basic Solution

def admin?; is_admin; end

Basic Solution

#3. bypass RESTful

Reason

But..

Vulnerability

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

Background

Where to look

Solution

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

 

xkcd-327

SQL escape in ORM by default

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

But…

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

People like drop plain SQL

Or

// 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

http://rails-sqli.org/

#7. same secret token

secret_token.rb

to verify signed cookies.

But…

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 “wallpaperstock.net”

Upgrade to 3.2.11+

Recap

https://gist.github.com/5618045

Thanks

Fork me on Github