Ruby on Rails Security Updated (Rails 3) at RailsWayCon

24
Ruby on Rails Security Updated Heiko Webers, bauland42

Transcript of Ruby on Rails Security Updated (Rails 3) at RailsWayCon

Ruby on Rails Security Updated

Heiko Webers, bauland42

Heiko Webers

CEO of bauland42: Secure and innovative web applications, security code audits:http://www.bauland42.de http://www.werkstatt42.de

Ruby on Rails Security Project: Blog and Book at http://www.rorsecurity.info

Cross-Site Scripting in Rails 3

Before: <%= h @project.name %>@project.name #=> <script>h(@project.name) #=> &lt;script&gt;

After: <%= @project.name %>

Unless you want to allow HTML/JS: <%= raw @project.name %>

Cross-Site Scripting in Rails 3

@project.name.html_safe? #=> false h(@project.name).html_safe? #=> true link_to(...).html_safe? #=> true "<br />".html_safe # => "<br />"

4

Cross-Site Scripting in Rails 3

safe + safe = safe safe.concat(safe) = safe (safe << safe) = safe

safe + unsafe = unsafe...

5

Cross-Site Scripting in Rails 3

String interpolation <%= "#{link_to(@product.title, @product)}

#{link_to(@product.title, @product)}" %> Deliberately unsafe

6

Cross-Site Scripting in Rails 3

textilize() and simple_format() do not return safe stringstextilize(‘*bold*‘) #=><strong>bold</strong>

<%= textilize(@product.description) %> NO <%=raw textilize(@product.description)%> OK <%=sanitize textilize(@product.description)

%>

7

Cross-Site Scripting in Rails 3

Know what you‘re doing <%= auto_link(@product.description) %>

# => unsafe, so escaped <%= raw auto_link(@product.description) %>

# => safe, but may contain HTML sanitize() it

8

Cross-Site Scripting in Rails 3

Know what you‘re doing Strings aren't magic:

value = sanitize(@product.description)value.html_safe? #=> truevalue.gsub!(/--product_name--/, @product.title)value.html_safe? #=> true<%= value %>

9

Cross-Site Scripting in Rails 3

Rails helper are becoming stable now There were problems with content_tag(), tag(),

submit_tag(), ... SafeErb plugin doesn‘t work yet/anymore

10

Cross-Site Scripting in Rails 3

xml.instruct!xml.description do xml << "The description: " xml << @product.descriptionend

Use xml.description @product.description to automatically escape

11

Ajax and XSS

No automatic escaping in RJS templates page.replace_html :notice,

"Updated product #{@product.title}"

12

Sanitization

Don‘t write it on your own:value = self.description.gsub("<script>", "")<scr<script>ipt>

sanitize(), strip_tags(), ... use the HTML::Tokenizer

Based on regular expressions Doesn‘t always render valid HTML Last vulnerability in Rails 2.3.5 regarding non-

printable ascii characters13

Sanitization

Use parsers like Nokogiri or Woodstox (JRuby) Gem sanitize: http://github.com/rgrove/sanitize

Sanitize.clean(unsafe_html) Gem Loofah: http://github.com/flavorjones/

loofahLoofah.fragment(unsafe_html).scrub!(:strip)

14

Sql-Injection in Rails 3

No find() anymore, no :conditions hash, ...But: Product.find(params[:id])

User.order('users.id DESC').limit(20).all NO: Product.where("id = #{params[:id]}") Product.where(["id = ?", params[:id]]) Product.where({:id => params[:id]})

15

Sql-Injection in Rails 3

NO: User.order(params[:order]).all raise "SQLi" unless ["id asc", "id desc"].include?

(params[:order]) Escape it yourself:

Product.order(Product.connection.quote(params[:order])).all

16

Other changes in Rails 3

config/initializers/session_store.rbRails.application.config.session_store :cookie_store, :key => "_app_name_session"

config/initializers/cookie_verification_secret.rbRails.application.config.cookie_secret = 'somereallylongrandomkey'

Don‘t keep it in your SCM

17

Other changes in Rails 3

Keep a value in a signed cookie:cookies.signed[:discount] = "12"

filter_parameter_logging deprecated config.filter_parameters << :password

in config/application.rb

18

Respond_with in Rails 3

class ProductsController < ApplicationController respond_to :html, :xml, :json def index respond_with(@products = Product.all) endend

How to define what attributes to render in [email protected]_xml(:only => [:id])

19

Bits and pieces

You can deploy with a SSH key:ssh_options[:keys] = ["/path/to/id_rsa.ppk"]

Secure the admin panel with a client SSL certificate

Remove secrets from your SCM: database.yml, ssh_config.rb

20

Bits and pieces

Check what they‘re downloadingFile.dirname(requested_filename) == expected_directory

/download?file=../config/database.yml validates_format_of :filename,

:with => /^[a-z\.]+$/i hello.txt

<script>alert(1)</script> Use \A and \z

21

Privilege escalation

def update @doc = Doc.find(params[:id]) end

before_filter :load_project before_filter :deny_if_not_full_access before_filter :load_doc

@doc = @project.docs.find(params[:id]) before_filter :deny_if_no_access_to_doc

22

Authorization

def deny_if_no_access_to_doc @doc.may_edit?(current_user) end

def may_edit?(usr) self.creator == usr end

<%= link_to(“Edit“,...) if @doc.may_edit?(current_user) %>

23