OWASP Top 10 & Ruby on Rails

talk at #parisrb by @DorianLupu

 

slides available online : slides.com/kundigo

5 April 2016

OWASP 

 

  • Open Web Application Security Project

 

  • A reference guide of good and bad practices relative to web application security

OWASP TOP 10 2013

  • A consensus on the most critical security risks

 

  • Those vulnerabilities are dangerous as they can allow an attacker to take control over your application, steal data or prevent its functioning

 

  • Those vulnerabilities are, in general, easy to find by an attacker and easy to exploit

A1 - Injection

SQL Injection - example 

> params[:column] = "age) FROM users WHERE name = 'Bob';"
> Order.calculate(:sum, params[:column])


SELECT SUM(age) FROM users WHERE name = 'Bob';) FROM "orders"
62




> User.find_by(:name => 'Bob').age

SELECT  "users".* FROM "users" WHERE "users"."name" = ? LIMIT 1  [["name", "Bob"]]
62

SQL injection - Protect yourself

 

Learn :

rails-sqli.org - SQL injection examples with rails 3 & 4 

 

Play :

presidentbeef/inject-some-sql - Rails 3 and 4 apps 

 

Detect : 

presidentbeef/brakeman - Static analysis security vulnerability scanner

 

A2 –Weak authentication and session management

Devise & Co

It's fine but...

 

Stop using CookieStore for sessions

* by default, Rails sessions never expire on the server-side

 

* you should use : rails/activerecord-session_store

 

A3 - Cross -Site Scripting (XSS)

# ne faites pas ça dans les fichier .erb %> 
raw @product.name
@product.name.html_safe   

 link_to “Personal Website”, @user.website
 # <a href=”javascript:alert(‘XSS’)”>Personal Website</a>

* It is very easy to bypass the template.erb sanitization mechanisms. (The example below should not be done)

 

 

 

* By default, no sanitization for json responses

 

* Another attack vehicle : the href attribute

 

 

A4 – Insecure Direct Object References    

A5 – Security Misconfiguration

Do not put any critical information about security inside the code

 

* secret key base for cookies

* third party API tokens (mailjet, S3, and so on)

 

Use environment variables instead

A6 – Sensitive Data Exposure

Always use HTTPS

josh/rack-ssl - force ssl connection

     * mark all cookies as "Secure"

     * add the Strict-Transport-Security (HSTS) header

Devise - be paranoid (paranoid mode)

  

 

Cache Control header

 

* The default value is :  "max-age=0, private, must-revalidate" 

     *  sensible data can be access simply by clicking on the back button (even after a logout)

 

* you should use "no-cache, no-store"

 

 

 

 

plus d'infos

You should use UUID instead of auto increment IDs  

 

Be careful with json responses, by default, all database fields are exposed

A7 – Missing Function Level Access Control

class PostController < ApplicationController  
  

  # PATCH/PUT /posts/1
  # PATCH/PUT /posts/1.json
  def update
    @post =  Post.find(params[:id])
    if @post.update(post_params)
      redirect_to post_path(@post), notice: 'Post was successfully updated.' } 
    else
      render :edit  
    end
  end


end


class PostController < ApplicationController  
  
  before_action :authenticate_user!, unless: :devise_controller?   # !!!

  # PATCH/PUT /posts/1
  # PATCH/PUT /posts/1.json
  def update
    @post =  Post.find(params[:id])
    if @post.update(post_params)
      redirect_to post_path(@post), notice: 'Post was successfully updated.' } 
    else
      render :edit  
    end
  end


end


class PostController < ApplicationController  
  
  before_action :authenticate_user!, unless: :devise_controller? 

  # PATCH/PUT /posts/1
  # PATCH/PUT /posts/1.json
  def update
    @post =  current_user.posts.find(params[:id])    # !!!
    if @post.update(post_params)
      redirect_to post_path(@post), notice: 'Post was successfully updated.' } 
    else
      render :edit  
    end
  end
end

# app/controllers/posts_controller.rb
class PostController < ApplicationController  
  
  before_action :authenticate_user!, unless: :devise_controller? 

  # PATCH/PUT /posts/1
  # PATCH/PUT /posts/1.json
  def update
    @post =  current_user.posts.find(params[:id])
    authorize! :update, @post     # !!! helper from cancancan gem
    
    if @post.update(post_params)
      redirect_to post_path(@post), notice: 'Post was successfully updated.' } 
    else
      render :edit  
    end
  end
end


# app/models/ability.Rb
class Ability
  include CanCan::Ability

  def initialize(current_user)
    current_user ||= User.new # guest user (not logged in)
    
    can :update, Post do |post|
        post.user.organization_id == current_user.organization_id || 
           current_user.super_admin?
    end
  end
end

A8 - Cross Site Request Forgery (CSRF)



   class ApplicationController < ActionController::Base
       protect_from_forgery
   end

By default, Rails offers a very strong protection

The protection can be bypass by any XSS vulnerability

A9 - Using Components with Known Vulnerabilities

Add the following two lines on your CI

bundle exec bundle-audit update
bundle exec bundle-audit check

example on a "recent" application (december 2015)

A10 – Unvalidated Redirects and Forwards

Useful links

Any question ?

Thank you

talk at #parisrb by @DorianLupu

slides available online : slides.com/kundigo

OWASP TOP 10 & Ruby on Rails

By Dorian Lupu

OWASP TOP 10 & Ruby on Rails

If you are hacked via OWASP Top 10, you are not allowed to call it "sophisticated"

  • 1,005
Loading comments...

More from Dorian Lupu