imedo Development Blog

there is no charge for awesomeness

Archive for the ‘ruby on rails’ tag

Secure coding with Ruby on Rails 7: Cross-site request forgery (CSRF)

without comments

Although discovered already in 1988 by Norm Hardy, cross-site request forgery (CSRF) has been the shooting star of web attacks in 2008. As a result it has become one of the 2009 CWE/SANS Top 25 Most Dangerous Programming Errors.

The idea behind CSRF is that an attacker sends a malicious request to the target application using a trusted connection which he expects to have been established by an innocent user. In case of web applications this could be done by hiding the request in a web page with harmless content. If the user visits the malicious web page while he is logged in to the target application in another browser tab, the dangerous request is send to the target over the trusted connection between browser and web application. Two basic scenarios are possible. In the first scenario the attacker forges a request to a commonly used web application so that it doesn’t matter who opens the malicious web page. The other scenario is to trick somebody who is known to have special privileges for one web application to open the prepared web page.

In both scenarios the malicious web page of the attacker is crafted so that it sends a hidden request to the target application. For example a HTTP GET request could be executed by using the src attribute of an img tag like in the following line of code. In this case it would delete one user account.

<img src="http://victim.example.com/user/destroy/1" />

Another possibility to hide a request in a web page is displayed in the next example. It’s a hidden HTTP POST request which changes the name and email address of the logged in user. It sends the request automatically on page load. With the changed email address the attacker can use the “forgot password?” function in order to receive a newly generated password and capture the account.

<SCRIPT>
  function SendAttack() {
    var form = document.createElement("form");
    form.style.display = "none";
    this.parentNode.appendChild(form);
    form.method = "POST";
    form.action = "http://victim.example.com/profile.php";
    form.first_name = "Bad";
    form.last_name = "Guy";
    form.email = "attacker@example.com";
    form.submit();
  }
</SCRIPT>
<BODY onload="javascript:SendAttack();"></BODY>

These two examples demonstrate that HTTP GET as well as HTTP POST are vulnerable by CSRF. Hence it is not enough to allow only POST requests to alter data. Other measure need to be applied, too. An obvious client-side countermeasures is to use separate browsers for surfing and administration, but Rails provides a server-side measure as well. The method protect_from_forgery() automatically inserts a security token in all forms and Ajax request generated by Rails. The token is calculated from the current session and the server-side secret. If the session storage is not CookieStorage the secret needs to be passed as option to the method. If an attacker is trying to send a request without the valid token to the application an ActionController::InvalidAuthenticityToken error is raised.

In Rails versions newer than 2.0 protect_from_forgery() is called for all HTTP POST requests by default. Hence it only has to be taken care of that HTTP GET  and HTTP POST requests are used appropriately (see W3C checklist) and that this is enforced. For assuring that actions are only called by HTTP POST requests Rails provides the verify() method which can be added to a controller as demonstrated in the following example. It is also shown how to define actions which should be callable by other HTTP methods than POST with the except option.

class MyController < ApplicationController
  verify :method => "post", :except => :index
end

In older Rails versions the protect_from_forgery() method can be used like shown in the following lines of code. In this example CSRF protection is disabled for the index action.

class MyController < ApplicationController
  protect_from_forgery :except => :index
end

For further information on cross-side request forgery see www.cgisecurity.com/csrf-faq.html.

Popularity: 1% [?]

Written by ehartung

December 4th, 2009 at 4:45 pm

Secure coding with Ruby on Rails 6: TLS

without comments

The transmission of sensitive information in cleartext is the number six of the 2009 CWE/SANS Top 25 Most Dangerous Programming Errors. Sending data without encryption becomes a security issue when nobody but the receiver is supposed to be able to read the included information and the data is send over an open network like the Internet. Login information for closed areas on websites is a typical example for this kind of data.

How easy it is to sniff for sensitive information on the Internet has been demonstrated with the  Tor-pishing attack not long time ago. As a result it is highly recommended to encrypt the whole communication between login and logout on access controlled web pages.

One way to secure communication over the Internet is to activate the transport layer security (TLS), formerly known as secure socket layer (SSL), for web applications. TLS encrypts the transmitted data between web browser and web server which denies unauthorized access to the sensitive data. Recently a vulnerability in TLS has been discovered and exploited. But common implementations like OpenSSL have already been fixed.

With the help of the SSL requirement plugin (github fork of Ian Warshak) secure communication can be easily added to Rails applications. It can be used opt-in or opt-out. The plugin is added to an application with the following lines of code.

class ApplicationController < ActiveRecord::Base
  include SslRequirement
end

In order to implement encryption opt-in, the plugin should be used like in the following example. In this example the actions signup and login are set to require TLS which means that non-TLS requests are forwarded to TLS. The index action can be accessed with or without TLS.

class AccountController < ApplicationController
  ssl_required :signup, :login
  ssl_allowed :index
end

For applications which are supposed to have encryption enabled by default and only some actions should be accessable without TLS, the plugin provides opt-out for TLS as well. In the following example all actions but index require TLS. Calling ssl_exceptions() with no action enables TLS for all actions.

class AccountController < ApplicationController
  ssl_exceptions :index
end

The SSL requirement plugin provides TLS support in Ruby on Rails by checking whether the HTTP header X_FORWARDED_PROTO of the request is set to ‘https’. This is done with the method request.ssl? of Rails. The plugin does not implement TLS. Hence further configuration of the used web server is needed. How to configure a web server with TLS is beyond the focus of this article, but be sure to consider the case that an attacker might set X_FORWARDED_PROTO to ‘https’ in an ordinary HTTP request. Rails depends on the underlying web server to create and handle the actual TLS connection.

UPDATE: Further information about TLS is provided on OWASP’s TLS cheat sheet.

Popularity: 1% [?]

Written by ehartung

November 23rd, 2009 at 12:46 pm

Secure coding with Ruby on Rails 5: OS command injection

without comments

OS command injection is ranked number five on the 2009 CWE/SANS Top 25 Most Dangerous Programming Errors listing. It means to extend input, which is meant to be used as parameter for a shell command, with malicious shell commands. This is possible since operating systems like UNIX support the execution of several commands in one line by separating the commands with a ”;”.

The following line of code could be part of a simple web interface to Subversion. It downloads a copy of the repository given by the user by executing a system call to the svn-checkout command with the provided repository as parameter.

system "svn checkout #{params[:repository]}"

While this code is doing what it is supposed to, it also represents a great risk for the system’s security, i.e. its non-repudiation. Web applications which pass input values to system calls unchecked, enable an attacker to easily access the command line on the web server’s host with the privileges of the web server. E.g. if an attacker enters the string “https://svn.mydomain.org/code; rm -rf /var/www/*” for the above example he might delete all web pages on the host.

A countermeasure against OS command injection in Ruby on Rails is the usage of the system(command, parameter) method which ensures that no part of the parameter is handled as a shell command.

system("svn", "checkout #{params[:repository]}")

Popularity: 1% [?]

Written by ehartung

October 7th, 2009 at 4:24 pm

Secure coding with Ruby on Rails 4: Cross-site scripting (XSS)

without comments

A Failure to Preserve Web Page Structure, which is number four of the 2009 CWE/SANS Top 25 Most Dangerous Programming Errors, is a programming error which makes an application vulnerable to cross-site scripting (XSS). For XSS an attacker injects malicious content into the output of a web application loading code from an external resource which is executed by the client’s web browser.

For example an attacker writes the following piece of code in a forum post. This line loads malicious JavaScript code from an external host which is executed when ever somebody reads the forum post.


<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRIPT>

While in order to prevent SQL injection it is important to sanitize the input data, for XSS especially the output of a web application should be sanitized. In order to achieve this, dynamic content could be displayed as followed.


escapeHTML(<SCRIPT SRC=http://ha.ckers.org/xss.js></SCRIPT>)

The output escaper of ERB html_escape() has an alias called h() which makes it easier to use. For those this is still too much overhead or who want to be sure should use the Rails plugin SafeERB which ensures that all strings in a rhtml template are escaped properly.

When using a HTML output escaper it is important to verify that it is using a whitelist, since XSS attacks do not necessarily look like the example above. It can also have the form of the following strings which are hard to filter with a blacklist. The first attack string uses the IMG tag to insert the malicious JavaScript code which is encoded in UTF-8. Older browsers like IE6, IE7, and Firefox2 which are-for several reasons-still in use, are known to support such encodings and treat them as valid HTML code.


<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>

This example can also be written the following way, which does not even use the semicolons at the end of an UTF-8 character encoding, since an UTF-8 character is encoded in 7 digits. The syntax with the semicolon is only a short form.


<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>

For these and more cross-site scripting attack examples visit the XSS cheat sheet.

Although not direct measures against XSS, input sanitation and input validation can help to prevent malicious code from getting into the application. So the usage of sanitize() and Ruby on Rails validation methods is highly recommended to provide an in-depth defense strategy against cross-site scripting. A really useful plugin is acts_as_sanitized which provides XSS input sanitation.

Popularity: 1% [?]

Written by ehartung

October 2nd, 2009 at 9:41 am

Secure coding with Ruby on Rails 3: SQL injection

with 2 comments

Number three of the 2009 CWE/SANS Top 25 Most Dangerous Programming Errors is Improper Sanitization of Special Elements used in an SQL Command which could lead to the possibility of SQL injection.

If an application is vulnerable to SQL injection an attacker can add SQL commands to input values in order to manipulate the behavior of this application or to gain unauthorized access. The classic example for SQL injection is the circumvention of authorization by adding an always true condition like ‘or 1=1’ to the passed password in a login dialog as shown in the following example.


User.find(:first, "login = '#{params[:user_name]}' AND password = '#{params[:password]}'")

Entering ’ OR ‘1’=’1 as password will result in the following MySQL query.


SELECT * FROM users WHERE login = 'asdf' AND password = '' OR '1'='1' LIMIT 1

This query will return the first user in the database and therefore the authorization will always succeed as long as there is at least one user in the database.

Besides getting unauthorized access to an application, SQL injection can be used to manipulate the displayed information so that sensitive information may be revealed. In the following example lazy SQL querying of non-sensitive data opens a door to the complete database and an attacker is able to read the user table by extending the input with SQL commands.


Article.find(:all, :conditions => "author = '#{params[:author]}'")

The database for this example consists of two tables: articles and users. While the articles table has four columns (id, author, title, text) the users table has only three (id, login, password). Here’s the attacker’s input for this scenario.


') UNION SELECT id,login AS author,password AS title,1 FROM users --

The input provided by the attacker generates the following MySQL query, which result will not contain any article but all entries from the users table masqueraded as articles.


SELECT * FROM articles WHERE (author = '') UNION SELECT id,login AS author,password AS title,1 FROM users -- ')

In order to prevent SQL injection the build in filters of Ruby on Rails like Model.find(id) or Model.find_by_*() should be used. They have sanitizers included which escape special SQL characters. For the first example this could look like the following.


user = User.find_by_login(params[:login])
raise "Access denied!" if user.password != params[:password]

If the conditions parameter is needed, e.g. in order to filter for several criteria, sanitizers like sanitize_sql_for_conditions in ActiveRecord::Base will create safe condition statements.


Article.find(:all, :conditions => sanitize_sql_for_conditions(:author => params[:author], :title => params[:title]))

Popularity: 1% [?]

Written by ehartung

September 23rd, 2009 at 7:36 am

Secure coding with Ruby on Rails 2: Output escaping

without comments

While input validation prevents the malicious manipulation of an application by users, proper output data escaping prevents failures in other applications which receive the generated data, but lack input validation.

Improper encoding or escaping of output is therefor the number two of the 2009 CWE/SANS Top 25 Most Dangerous Programming Errors. A popular example for this kind of vulnerability are browsers which are manipulated by malicious JavaScript code injected into user-generated content of web applications. This kind of attack is called cross-site scripting (XSS).

In client-server environments applications communicate over APIs using more or less well defined protocols. Server and client applications can be developed by different parties unaware of the other side’s source code. A server which expects input data in a certain format can run into serious problems when a client application sends data in a wrong encoding or does not escape SQL, OS, or other commands. This is even more dangerous if the client application constructs the output from user input, e.g. a chat client which does not escape control commands.

Of course, it should be common knowledge that input data needs to be validated, but as long as it cannot be verified, e.g. by reviewing the source code, proper output encoding and escaping should be provided.

The most common output formats of a Rails web application are HTML and JSON. For escaping those formats Ruby on Rails provides the following mechanisms.

  • The ERB method html_escape() or its alias h() for HTML output
  • The SanitizeHelper methods to escape HTML output
  • The SafeERB plugin for enforcing the escaping of strings in rhtml templates
  • The ERB method json_escape() or its alias j() for JSON output

If other output formats are needed it is highly recommended to use provided escape methods or to write a custom output escaper.

Popularity: 1% [?]

Written by ehartung

September 21st, 2009 at 6:31 pm

Secure coding with Ruby on Rails 1: Input validation

without comments

Improper input validation is the number one error according to the 2009 CWE/SANS Top 25 Most Dangerous Programming Errors listing. It can be exploited by changing input values in such a way so as to cause the application to crash, confidential information to be revealed or the flow of the program to be changed in an unexpected way. This kind of exploit is called input validation attack. The following example demonstrates how lazy input handling can lead to unwanted effects. It shows a piece of code which calculates the price of a sale by multiplying a product’s fixed price with the quantity provided by the user. Afterwards it charges the user’s account with the total price.


class Sale < ActiveRecord::Base
 belongs_to :user

 def buy
   total_price = single_price * quantity
   user.account.charge(total_price)
 end

end

sale = Sale.new(:single_price => 10, :quantity => params[:quantity], :user => buying_user)
sale.buy

Consider the case a user provides a negative value for the quantity. The total price would be negative as well and therefore the user or attacker is able to increase the amount on his account for free.

In order to prevent misuse, input data which is provided by the user should never be trusted. Proper input validation is absolutely necessary. For the example above a check for integer values greater or equal to zero would prevent the exploit of the code. Ruby on Rails already brings a set of easy to use input validation methods which usage is demonstrated in the following.


class Sale < ActiveRecord::Base
 belongs_to :user
  validates_numericality_of :quantity,
    :o nly_integer => true
    :greater_than_or_equal_to => 0

 def buy
   total_price = single_price * quantity
   user.account.charge(total_price)
 end

end

sale = Sale.new(:single_price => 10, :quantity => params[:quantity], :user => buying_user)
sale.buy

More Rails validation methods can be found under Module
ActiveRecord::Validations::ClassMethods
.

Popularity: 1% [?]

Written by ehartung

September 1st, 2009 at 7:06 am

Rails “try these” fallback mechanism

without comments

If you ever wrote something similar like this (or more complex constructs):


<%= @jadda.name rescue @jadda.nickname rescue 'unknown' %>

and then wished you had a construct like try(*these) in prototype? Surrender not, for there’s light at the end of the tunnel!

I present you try these which I found on Chu Yeow’s Blog today.

Here’s the mixins code:

1
2
3
4
5
6
7
8
module Kernel
  def try(*these)
    raise ArgumentError, 'try requires at least 2 arguments' if these.size <= 1
    fallback = these.pop unless these.last.respond_to?(:call)
    these.each { |candidate| begin return candidate.call rescue next end }
    fallback || raise(RuntimeError, 'None of the given procs succeeded')
  end
end

That’s all. Use it like this:


<%= try(lambda{ @jadda.name }, lambda{ @jadda }, lambda{ 'unknown' }) %>

and it will call all these blocks until one will yield no exception or the end of the array is reached. If one yields a result it will end the loop and return the value yielded by that block.

Hope this is useful for someone.

Popularity: 1% [?]

Written by mscherf

July 31st, 2008 at 6:39 pm