1. Computing

A Security Attack On Ruby Code

Example 1: CSRF without XSS


Alice boots her computer and begins work for the day. Her previous session to Site A has expired, so she'll have to log in again. At this point, Alice has no session data and is not authenticated. As far as Site A is concened, she's a normal user of the site.

To log in, Alice points her web browser to http://sitea/account/login. This initial GET request to the login action displays a form. Since Rails sees that Alice has no valid session, it automatically creates one for her. Initially this session is empty; it carries no session variables or credentials. Alice fills out the form and clicks the submit button. Below is a simplified version of the form displayed by the login action.

<form action="/account/login" method="post">
    <input id="login" name="login" type="text" />
    <input id="password" name="password" type="password" />
    <input name="commit" type="submit" value="Log in" />

Her web browser now makes a POST request to the login action. The login action generated by the acts_as_authenticated plugin does two different things--depending on the method of the request. A GET request will display the login form, while a POST request will attempt to log in. If the log in attempt was successful, a session variable will be set storing Alice's user ID. At the beginning of every action, using the user ID stored in the session, her User ActiveRecord object will be retrieved from the database for authentication. Alice's login attempt was successful, so her session now represents an authenticated user.

Alice makes a post to her blog. To do this, she opens the new action in the Posts controller in her web browser by going to http://sitea/posts/new and is presented with a form much like the one below. She fills out the form with her blog post for the day and hits submit. Her web browser makes a POST request to the create action of the Posts controller. Since the create action is protected by the login_required before filter, before any blog posts are created, Rails checks Alice's session variables for a user ID. As Alice is logged in and has a user ID in her session variables, the blog post is created.

<form action="/posts/create" class="new_post" id="new_post" method="post"> <input id="post_title" name="post[title]" size="30" type="text" />
    <textarea cols="40" id="post_body" name="post[body]" rows="20"></textarea>
    <input name="commit" type="submit" value="Create" />

Hidden Forms and Javascript

Mallory logs into Site B. In this example, Site B is Mallory's blog, but Mallory makes a slightly different blog post than Alice. In addition to the legitimate blog post she's hoping Alice will read, Mallory has included a hidden form and javascript to the end of the post. When Alice's web browser loads the page, the form will be instantly submitted and a new blog post called "DEFACED" will be created. Since Alice's web browser has a session cookie with credentials for Site A, the Posts controller thinks this is a legitimate request and creates the blog post.

<h1>Ate Peanutbutter and Jelly Sandwich</h1>
<p>I ate a peanutbutter and jelly sandwich today.
I love peanutbutter and jelly sandwiches, and I
know you do too Alice (my most loyal blog reader)!</p>

<form action="http://sitea/posts/create" method="POST">
  <input type="hidden" name="post[title]" value="DEFACED" />
  <input type="hidden" name="post[body]" value="I just defaced your blog" />


A Few Problems

While this is a successful attack (Site A was defaced), there are a few problems. Firstly, Alice will immediately know something is wrong. As soon as she opens Site B with the hidden form, her web browser will be redirected to her own Site A displaying the "DEFACED" blog entry. Assuming she has sufficient knowledge of how the World Wide Web and Web browsers work, she'll also know that Mallory or someone else at Site B is responsible for the attack. Below is a more sophisticated and sneaky attack Mallory could have used.

<h1>Ate Peanutbutter and Jelly Sandwich</h1>
<p>I ate a peanutbutter and jelly sandwich today.
I love peanutbutter and jelly sandwiches, and I
know you do too Alice (my most loyal blog reader)!</p>

  iframe = document.createElement("iframe");
  iframe.style.visibility = "hidden";

  form = frames[0].document.createElement("form");
  form.action = "http://sitea/posts/create";
  form.method = "POST";

  inp = frames[0].document.createElement("input");
  inp.name = "post[title]";
  inp.value = "DEFACED";

  inp = frames[0].document.createElement("input");
  inp.name = "post[body]";
  inp.value = "I just defaced your blog";


IFRAME Elements

The first part of this Javascript creates an IFRAME element. An IFRAME element acts as a mini-browser window embedded in a webpage. In that browser window you can open any location on the Web and it will be displayed in the mini-window. In this case, the IFRAME's visibility is set to hidden, meaning it won't be displayed at all.

The middle sections of the Javascript create a form tag and two input tags as with the form in the previous example. The final tag submits the form in the IFRAME. The big difference between this example and the previous example is that Site A's response after creating a new blog post will be displayed in the IFRAME. Since the IFRAME is hidden, Alice won't notice anything at all, but since the request is still made to Site A, the request will still contain Alice's session cookie with credentials.

This method of attack relies on the fact that Site A doesn't use protect_from_forgery. Since Rails isn't configured to check for authentication IDs, forms from Site B can be submitted to actions on Site A.

  1. About.com
  2. Computing
  3. Ruby
  4. Ruby on Rails
  5. Security
  6. A Security Attack on Ruby Code

©2014 About.com. All rights reserved.