You are on page 1of 16

Securing Ruby on Rails

Sanjay Kumar Sharma HMRITM, Delhi

Securing Ruby on Rails


SANS Top-20 Internet Security Attack Targets (2006 Annual Update) Top of the list for the Cross-platform Applications category is: C1 Web Applications C1.1 Description Applications such as Content Management Systems (CMS), Wikis, Portals, Bulletin Boards, and discussion forums are being used by small and large organizations. Every week hundreds of vulnerabilities are being reported in these web applications, and are being actively exploited. The number of attempted attacks every day for some of the large web hosting farms range from hundreds of thousands to even millions. All web frameworks (PHP, .NET, J2EE, Ruby on Rails, ColdFusion, Perl, etc) and all types of web applications are at risk from web application security defects, ranging from insufficient validation through to application logic errors.

Securing Ruby on Rails


User Input
Regular form fields
Hidden form fields Cookies URL Parameters

POST data
HTTP headers AJAX requests

Scoped Queries

Securing Ruby on Rails


Record IDs used right in the URL?
def show @contact = Contact.find params[:id] end # accessed in URL path like /contacts/42 private def require_signin return false unless session[:user_id] end end
class User < ActiveRecord::Base has_many :contacts end class Contact < ActiveRecord::Base belongs_to :user end

class ContactsController < ApplicationController before_filter :require_signin def new @contact = Contact.new end def create contact = Contact.new params[:contact] contact.user_id = session[:user_id] contact.save redirect_to contact_url(contact) end

Securing Ruby on Rails


class ContactsController < ApplicationController # gives us a @current_user object before_filter :require_signin # safely looks up the contact before_filter :find_contact, :except => [ :index, :new, :create ] def index @contacts = @current_user.contacts.find :all end def new @contact = @current_user.contacts.new end def create @current_user.contacts.create params[:contact] redirect_to contacts_url end def show end def edit end def update @contact.update_attributes params[:contact] redirect_to contact_url end def destroy @contact.destroy redirect_to contacts_url end private def require_signin @current_user = User.find session[:user_id] redirect_to(home_url) and return false unless@current_user end def find_contact @contact = @current_user.contacts.find.params[:id] end end

Securing Ruby on Rails


Record IDs in URLs verified? (HTTP authentication) Is the ID guessable? How about a token?
class User < ActiveRecord::Base def before_create token = Digest::SHA1.hexdigest("#{id}#{rand.to_s}")[0..15] write_attribute 'token', token end end class FeedsController < ApplicationController def show @user = User.find_by_token(params[:id]) or raise ActiveRecord::RecordNotFound end end

Securing Ruby on Rails


Mass Assignment
contact = current_user.contacts.create params[:contact] contact.update_attributes params[:contact]

class UsersController < ApplicationController def edit @user = current_user end def update current_user.update_attributes params[:user] redirect_to edit_user_url end end edit.rhtml: <% form_for :user, :url => user_url, :html => { :method => :put } do |u| %> <p>Login: <%= u.text_field :login %></p> <p>Password: <%= u.password_field :password %></p> <p><%= submit_tag "Save Account Settings" %> <% end %>

require 'net/http' http = Net::HTTP.new 'localhost', 3000 http.post "/users/1", 'user[is_administrator]=1&_method=put', { 'Content-Type' => 'application/x-www-form-urlencoded' }

class User < ActiveRecord::Base attr_protected :is_administrator has_many :contacts end

class User < ActiveRecord::Base attr_accessible :login, :password has_many :contacts end

Securing Ruby on Rails


Form Validation Client-side validation with javascript immediate feedback The data should still be validated on the server side as well.

Securing Ruby on Rails


SQL Injection
passing input directly from user to database malicious users hijack your queries
# unsafe User.find(:first, :conditions => "login = '#{params[:login]}' AND password = '#{params[:password]}'") SELECT * FROM users WHERE (login='alice' and password='secret') LIMIT 1 ' or login='bob' and password != SELECT * FROM users WHERE (login='' and password='' or login='bob' and password != ) LIMIT 1 #Logs in as any user

Securing Ruby on Rails


SQL Injection
# safe (pass a hash to :conditions) User.find(:first, :conditions => { :login => params[:login], :password => params[:password] })

# safe (shorter form)


User.find(:first, :conditions => [ "login = ? AND password = ?", params[:login], params[:password] ])

Securing Ruby on Rails


Session Fixation
cross-site cooking Mitigation
use reset_session in your sign-in and sign-out methods # signin def create if u = User.find_by_login_and_password(params[:login], params[:password]) reset_session # create a new sess id, to thwart fixation session[:user_id] = u.id redirect_to home_url else render :action => 'new' end end

Securing Ruby on Rails


Cross-site Scripting (XSS) unescaped user data included in HTML output Whats the problem? Javascript!
http://example.com/search?q=%3Cscript%3Ealert('XSS')%3B%3C%2Fscript%3E

Securing Ruby on Rails


Cross-site Scripting (XSS)
#unsafe
<%= start_form_tag search_url, :method => :get %> <p><%= text_field_tag :q %> <%= submit_tag "Search" %> <% end %> class SearchController < ApplicationController def index @q = params[:q] @posts = Post.find :all, :conditions => ["body like :query", { :query => params[:q]}] end end <p>Your search for <em><%= @q %></em> returned <%= pluralize @posts.size, "result" %>:</p> <% @posts.each do |post| %> <li><%= link_to post.title, post_url(:id => post) %>: <%= exerpt post.body, @q %></li> <% end %>

Solution: h helper, also known as html escape. converts &, ", >, and < into &amp;, &quot; &gt;, and &lt;
<p>Your search for <em><%= h @q %></em> <%= link_to h(@user.name), user_url(@user) %>

Hashing Passwords
MD5 or SHA1

Securing Ruby on Rails

require 'digest/sha1' class User < ActiveRecord::Base attr_accessor :password validates_uniqueness_of :login validates_presence_of :password, :if => :password_required? validates_confirmation_of :password, :if => :password_required? before_save :hash_password # Authenticates a user by login/password. Returns the user or nil. def self.authenticate login, password find_by_login_and_hashed_password(login, Digest::SHA1.hexdigest(login+password)) end protected def hash_password return if password.blank? self.hashed_password = Digest::SHA1.hexdigest(login+password) end def password_required? hashed_password.blank? || !password.blank? end end

Securing Ruby on Rails


Silencing Logs
class OrdersController < ApplicationController filter_parameter_logging :cc_number, :cvv, :cc_date # ...

end

Securing Ruby on Rails


Advertising

Third party widgets

You might also like