securing ruby on rails cis 6939 web engineering with ruby on rails university of north florida...
TRANSCRIPT
Securing Ruby on Rails
CIS 6939 Web Engineering with Ruby on RailsUniversity of North FloridaStephen Jones 8 July 2007
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 InputRegular form fields
Hidden form fields
Cookies
URL Parameters
POST data
HTTP headers
AJAX requests
Scoped Queries
Securing Ruby on Rails
class User < ActiveRecord::Basehas_many :contacts
endclass Contact < ActiveRecord::Base
belongs_to :userend
class ContactsController < ApplicationController before_filter :require_signindef new
@contact = Contact.newenddef create
contact = Contact.new params[:contact]contact.user_id = session[:user_id]contact.saveredirect_to contact_url(contact)
end
Securing Ruby on Rails
def show @contact = Contact.find params[:id]end # accessed in URL path like /contacts/42privatedef require_signin return false unless session[:user_id]end
end
Record IDs used right in the URL?
class ContactsController < ApplicationController # gives us a @current_user objectbefore_filter :require_signin # safely looks up the contactbefore_filter :find_contact, :except =>
[ :index, :new, :create ]def index
@contacts = @current_user.contacts.find :allenddef new @contact = @current_user.contacts.newenddef create @current_user.contacts.create params[:contact] redirect_to contacts_urlenddef showenddef editend
Securing Ruby on Railsdef update @contact.update_attributes params[:contact] redirect_to contact_urlenddef destroy @contact.destroy redirect_to contacts_urlendprivatedef require_signin @current_user = User.find session[:user_id] redirect_to(home_url) and return false unless@current_userenddef find_contact @contact = @current_user.contacts.find.params[:id]endend
Record IDs in URLs verified? (HTTP authentication) Is the ID guessable? How about a token?
class User < ActiveRecord::Basedef before_create token = Digest::SHA1.hexdigest("#{id}#{rand.to_s}")[0..15] write_attribute 'token', tokenendendclass FeedsController < ApplicationControllerdef show @user = User.find_by_token(params[:id]) or raise ActiveRecord::RecordNotFoundendend
Securing Ruby on Rails
Mass Assignment
contact = current_user.contacts.create params[:contact]contact.update_attributes params[:contact]
class UsersController < ApplicationControllerdef edit @user = current_userenddef update current_user.update_attributes params[:user] redirect_to edit_user_urlendend
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 %>
Securing Ruby on Rails
require 'net/http'http = Net::HTTP.new 'localhost', 3000http.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 :contactsend
class User < ActiveRecord::Base attr_accessible :login, :password has_many :contactsend
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 Injectionpassing input directly from user to database
malicious users hijack your queries
Securing Ruby on Rails
# unsafeUser.find(:first, :conditions => "login = '#{params[:login]}' ANDpassword = '#{params[:password]}'")
SELECT * FROM users WHERE (login='alice' and password='secret') LIMIT 1
' or login='bob' and password != ‘
SELECT * FROM users WHERE (login='' andpassword='' or login='bob' and password !=‘ ‘ ) LIMIT 1 #Logs in as any user
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 Fixationcross-site cooking
Mitigationuse reset_session in your sign-in and sign-out methods
# signindef 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_urlelse render :action => 'new'endend
Securing Ruby on Rails
Securing Ruby on Rails
Cross-site Scripting (XSS)
unescaped user data included in HTML output
What’s 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]}] endend
<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 &, " >, and <
<p>Your search for <em><%= h @q %></em><%= link_to h(@user.name), user_url(@user) %>
Securing Ruby on RailsHashing Passwords
MD5 or SHA1require '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))endprotecteddef hash_password return if password.blank? self.hashed_password = Digest::SHA1.hexdigest(login+password)enddef password_required? hashed_password.blank? || !password.blank?endend
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
Attributation1. The Ghost In The Browser: Analysis of Web-based Malware
Niels Provos, Dean McNamee, Panayiotis Mavrommatis, Ke Wang and Nagendra Modadugu Google, Inc.
2. Ajax on Rails by Scott Raymond
3. Sans Institute Internet Security Attack Targets http://www.sans.org/top20/
4. Rails Security Mailing List: http://groups.google.com/group/rubyonrails-security
Securing Ruby on Rails