ruby on rails security - jaoo.dkjaoo.dk › ... › slides ›...
TRANSCRIPT
Heiko Webers
Ruby On Rails Security Project: www.RoRsecurity.info
E-Book „Ruby On Rails Security“Ruby On Rails Security Audits
RubyFools Copenhagen, 2008 2
Software company
Attack trendsCracker‘s motivation: Make money, it’s amulti-billion dollar businessRecently: Attacks on trusted news or sports sites
Break into the server: Apache, cPanel, CMS
RubyFools Copenhagen, 2008 3
Break into the server: Apache, cPanel, CMS holesAdvertisement with malwareInject specific exploits or entire attackframeworks: MPack, 500-1,000$, available inthe Russian underground, guaranteed 40-50% success rate
Here: Security of the (upper) application
Injection
The wrong wayDirectly use external input
The right wayConsider external input malicious, until proven
RubyFools Copenhagen, 2008 4
Consider external input malicious, until provenotherwiseSanitize or (preferably) escape it according tothe context it‘s being used in
Injection - Contexts
SQL – remember SQLi in find_by_sql
SGML (HTML, XML, RSS…) – Escapeagainst XSS, SafeErb plug-inJavaScript – Escape possible code in a
RubyFools Copenhagen, 2008 5
JavaScript – Escape possible code in aRJS contextescape_javascript()
Injection - Contexts
CSS – This is how the Samy wormbrought down MySpace<div id=mycode style="BACKGROUND: url('javascript: eval(document.all.mycode.expr)')" expr="..." />
RubyFools Copenhagen, 2008 6
Command line parameter: No | or ;allowed, use system() instead of `command`
`ls #{dir}` # dir #= "whatever | rm *"
Injection - Contexts
Textile – Definitely sanitize the result withRails’ sanitize()
!bunny.gif(Bunny" onclick="alert(1))!
RubyFools Copenhagen, 2008 7
<p><img src="bunny.gif" title="Bunny" onclick=" alert(1) " alt="Bunny" onclick=" alert(1) " /></p>
Whitelists vs. Blacklists
Use before_filter :only instead of :except
Attr_accessible instead of attr_protected
Against XSS: Allow <b> instead of removing <script>
RubyFools Copenhagen, 2008 8
removing <script>
Don’t try to “correct” user input by blacklists:
"<sc <script> ript>".gsub("<script>", "")
But reject malformed input
Cross-Site Reference Forgery (CSRF)
Browser sends domain cookie for every request
© SecureNet
RubyFools Copenhagen, 2008 9
Browser sends domain cookie for every request to that domainClient is logged in, authentication information in the cookieVictim clicks a link or views a page with a special image:
<img src="http://www.example.com/project/1/destroy" />
He doesn‘t even notice the attack
CSRF in Rails
The wrong wayGET /project/1/destroy
The right wayGET /project/1/show
POST /project/1/destroy
RubyFools Copenhagen, 2008 10
POST /project/1/destroy
verify :method => :post, :only => [:destroy]
CSRF & POST<a href=" http://www.harmless.com/ " onclick="var f =
document.createElement (' form '); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = ' POST'; f.action = ' http://www.example.com/account/destroy '; f.submit();return false;"> To the survey </a>
RubyFools Copenhagen, 2008 11
<img src=" http://www.harmless.com/img " width="400" height="400" onmouseover="..." />
CSRF in Rails
The right wayprotect_from_forgery :secret => "very_long_secret"
Includes a security token in non-GETrequests, automatically generated in
RubyFools Copenhagen, 2008 12
requests, automatically generated inform_for() and others
<input name="authenticity_token" type="hidden" value="3a1e11299eff1fa5cbc724ca32978448098af0" />
Administration Interface
The wrong wayVulnerable to XSS (steal a privileged cookie)Vulnerable to CSRF<img src="/admin/user/destroy/1" />
The same cookies used for the application
RubyFools Copenhagen, 2008 13
The same cookies used for the applicationand the admin interface
Administration Interface
The right wayTake security even more serious (especially XSS & CSRF)Take precautions for the worst case:Someone else takes control of the
RubyFools Copenhagen, 2008 14
Someone else takes control of theadministration interfaceRequire to log in to the interface despite of avalid session, might even be special adminlogin credentialsIntroduce user roles: Different permissions fordifferent admins
Administration Interface
The right wayPut it to admin.example.com instead ofwww.example.com/ admin: A (stolen admin)cookie from www.example.com doesn‘t workon admin.example.com
RubyFools Copenhagen, 2008 15
on admin.example.comCheck the remote IP: Administration allowedfrom a certain IP (check request.remote_ip )
Session Fixation
Instead of stealing a cookie, an attacker fixes a user‘ssession identifier known to him
RubyFools Copenhagen, 2008 16
©acros
Session Fixation
Change the victim‘s cookie, for example with HTML/JS: document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9";
The wrong way
RubyFools Copenhagen, 2008 17
The wrong wayVulnerable to XSS (the most obvious way to fixate sessions)Allow users to log in with the same session ID for years
Session Fixation
The right wayIf the application is non-public: Turn offcookies for the public parts, so an attackermay not obtain a valid session IDIssue a new session ID after a succesful login
RubyFools Copenhagen, 2008 18
Issue a new session ID after a succesful loginclass SessionsController < ApplicationController
def create
reset_session
…
end
end
Session Fixation
The right wayExpire sessions, frequency based on howcritical the application isAn attacker may write an automated script tokeep a session alive: Check the session‘s
RubyFools Copenhagen, 2008 19
keep a session alive: Check the session‘screated_at, as well (for ActiveRecordStore)
Login
The wrong wayNot updating plug-ins (e.g. restful_authentication)
The right way
RubyFools Copenhagen, 2008 20
Check for updates: There was a security holein it, you could log in without login credentialsGET /activate?id=
User.find_by_activation_code(params[:id])SELECT * FROM users WHERE (users.`activation_code` IS NULL )LIMIT 1
See http://www.rorsecurity.info/2007/10/28/restful_authentication-login-security/
User Management
The wrong wayMake changing password and e-mail address easy
The right way
RubyFools Copenhagen, 2008 21
Make it harder to seize an accountRequire to enter the old password when changingOr the answer to a security question
User Management
The wrong waySpecific error messages enable usernameenumeration (for login and “send-forgotten-password” pages)
The right way
RubyFools Copenhagen, 2008 22
The right wayArmed with a list of usernames and adictionary for the passwords, a bot mightbrute-force accountsFirst step: Possibly disable an account orrequire to enter a CAPTCHA after a certainamount of failed logins
CookieStore
What you store in the session can be seen by the client<base64 encoded session>-<digest>
The wrong wayStore secrets, more than 4K of data, entire
RubyFools Copenhagen, 2008 23
Store secrets, more than 4K of data, entire objectsUse a trivial secret
config.action_controller.session = {
:secret => ‘trivial’
}
CookieStore
The right wayOk if you store a user_id and flash message onlyMake the secret at least 30 characters long
RubyFools Copenhagen, 2008 24
config.action_controller.session = {
:session_key => ‘_app_session’,
:secret => ‘0x0dkfj3927dkc7djdh36rkckdfzsg’
}
CookieStore
Be aware of replay attacks1. User receives credits, stored in his session2. User buys something3. User gets his new, lower credits stored in his
RubyFools Copenhagen, 2008 25
session4. Cracker takes his saved cookie from step #1
and pastes it back in his browser’s cookie.Now he’s gotten his credits backNormally solved using a nonce, but that‘svery hard for multiple app servers(mongrels)
Files
The wrong waysend_file '/var/www/uploads/' + params[:filename]
GET /download?filename=../../../etc/passwd
PUT /upload?filename=../../../etc/passwd
The right way
RubyFools Copenhagen, 2008 26
The right wayStore filenames in the DB, name the files afterthe record ID, just as the attachment_fu plug-in doesVerify the downloaded file to be in the correctdirectoryraise if DOWNLOAD_DIR =!
File.dirname(filename)
Files
The wrong wayStore file uploads in Rails‘ public directoryUpload: /public/uploads/file.fcgi
The right way
RubyFools Copenhagen, 2008 27
The right wayDo not store it in Apache's DocumentRoot directory tree
Hate CAPTCHAS?
Say Hello to negative CAPTCHAsDon‘t ask the user to proof he‘s human, butreveal that the spam/login bot is a botInclude a honeypot field that is hidden fromthe user by CSS
RubyFools Copenhagen, 2008 28
the user by CSSIf this field contains any text, it must be a botOr make it more sophisticated
http://nedbatchelder.com/text/stopbots.html
Thanks
RubyFools Copenhagen, 2008 29
Thanks