Welcome Guest, Not a member yet? Register   Sign In
XSRF Prevention library "Crumbs.php", RFC
#1

[eluser]Jon W[/eluser]
Hi folks,

Have been using CodeIgniter for a while now, and the lack of XSRF protection was worrying me. So I wrote a lib.

What's the attack?

You run some kind of social network site. Attacker "A" has a problem with target "B". "A" knows the email address of "B", or can contact them via your site.

Now consider this URL;
http://your.site/app/account/delete_profile

If "A" can get "B"'s browser to follow this link, "B" gets their account deleted.

"A" could;
a) send "B" an email containing <img src="http://your.site/app/account/delete_profile"> If they have their email client set up to load external images & are logged-in to your site, on viewing the email their client requests the URL, sends the your.site cookies along with the request, and the request deletes their account. No need for any XSS or JavaScript.
b) send a message to "B" via your site doing the above (same caveats apply).
c) trick "B" into visiting a web page controlled/XSS'ed by "A".
d) etc. etc.

If you use a "keep me logged in" function on your authentication software, your users are much more vulnerable to this type of attack, as the user is effectively logged-in for a much longer length of time and so there is a much bigger window of time for this attack to succeed in.

Let's say you introduce a confirmation step, the "request confirmation" page. Now all "A" has to to is send "B" the URL of the "approve confirmation" page.

What is required is a way of tracking the steps of the client through pages 1 & 2, and introducing a time limit between requests 1 & 2. This is done via a unique token, usually referred to as a "crumb" or "nonce". I'm British, so I call them "crumbs".

This is what the Crumbs library tries to address.


Doesn't CI's session "mutation" behaviour guard against this?

A little, but not much. Only by expiring the session altogether once the $config['sess_expiration'] limit has been reached does it help, and that has to be a long time limit or it would be inconvenient to most users. The mutation seems to be guarding against session fixation attacks, not XSRF specifically.


Why not use CI flashdata?

Would be ideal, but imagine a user has 2 tabs open on your site. If they make a request from the other tab before making the confirmation request in the first tab, the flashdata will die. So I use the db sessions repository.


Why not use client-side cookies exclusively?

This attack involves "A" forcing "B" to unknowingly send their cookies to the server, so this might leave us open to a double-barrelled version of the attack -- "A" could force "B" to load the "request confirmation" page, then the "approve confirmation" page in sequence. Both cookies will look legit and the request will go through. To prevent this, there must be a link between data POST-ed to the "confirmation page" and the token. The relationship cannot be stored exclusively in the cookies.


So, correctly used, does this make my app 100% secure against XSRF?

Sadly not, but it's no longer a trivial attack. To get around this;
a) the attacker must find a XSS hole in a page served from the same top-level domain as your site.
b) The victim has to visit that URL and execute evil JavaScript. Fortunately nowadays not mail email clients execute JavaScript, so at least the email vector is relatively plugged.

But you do get for free; Smile

1) A verifiable link between two stateless HTTP requests bound by time & client but not request sequence, which can have many applications.
2) A useability enhancement to critical steps in your application flow whereby a user has to complete the next step in the flow within a certain time-frame.
3) Once the TTL has passed -- some proof against "URL auto-completion in browser" or history accidents, proof against activation via content cached by web spiders, via web server referer logs, intermediate network caches, some replay attacks etc. etc.

I think it's worth it. This idea has been adopted wholesale by Yahoo! and other major players on their applications.

Phew!

Next, the code and usage guidelines...
#2

[eluser]Jon W[/eluser]
Requirements

That you use the CI database sessions.
http://ellislab.com/codeigniter/user-gui...sions.html

As for PHP versions, I have no idea if it will work with PHP 4, any help there appreciated!

How does it work?

When building a "request confirmation" page, call;

Code:
$this->load->library('crumbs');
$this_token = $this->crumbs->get_crumb( 'YOUR_TIMER_NAME', 60 );

// standard form-creation stuff follows...
$this->load->helper('form');
$html  = form_open('http://192.168.1.5/app/index.php/confirmation');
$html .= form_hidden('tok',$this_token );
$html .= form_submit('mysubmit', 'Submit');
$html .= form_close();
echo $html;


'YOUR_TIMER_NAME' is an arbitrary text label for the event which should be unique within the application, e.g. 'DELETE_ACCOUNT', 'PURCHASE_APPROVED'.

The second parameter is the time for the token to live (TTL) in seconds.

$this_token is a string (an MD5 hash) which you write into a hidden form field on your "request confirmation" page. You could write it into a link with a dynamic URL as a target, but really you want to use POST.

On the "confirmation page" you get the token variable from the environment, assign it to $input_token and check it thusly;

Code:
function confirmation()
{

  // get our token from the POSTdata and run it through the XSS filter
  $input_token = $this->input->post('tok', TRUE);
    
  if ( ! $this->crumbs->validate_crumb( 'YOUR_TIMER_NAME', $input_token ) )
  {
    // something bad happened
  }


Notes:

- Each user can have more than one token assigned to them simultaneously.

- Tokens are stored in the CI sessions db table, so no extra db read required.

- Make sure the token is NEVER written to JavaScript! Do not use JavaScript to write the token into the page/DOM! By doing so you leave a large door open for the attacker.

- CI deletes the users' session data every $config['sess_expiration'] seconds from last activity. When this happens, all timers are cleared. This is a good thing because I don't need to do any housekeeping in this code. Like removing set timers that never get checked, ferinstance.

Anyway, that's it, comments welcome!
#3

[eluser]ray73864[/eluser]
Why not when the account is first created, store a hash of the account details (perhaps, username, date_created, last_logged on date, and a whole swag of other fields) in the database, and then when the 'delete_account' link is clicked on, it requires that the URL contains this hash in it.

Since the hash is in the database, no attacker would know what an individual users hash would be (and it could update all the time if you use 'last_logged_on_date'), and if the attacker attempts to send the user to the delete_account page without including the hash in the URL it simply gets rejected.

No need for a confirmation page, and the delete_account page becomes a lot more secure.
#4

[eluser]Jon W[/eluser]
Sure, that's the idea -- a "throw and catch" of some reasonably unguessable value bound to user and time.

The example I gave is probably way overblown Smile I appreciate the topic may be new to some so I was being verbose. The technique lends itself to lots of situations apart from the one outlined of course.

Despite that massive preamble, the library barely a dozen lines of actual logic Wink

Is just an attempt to facilitate easy creation of the kind of hash you mention, in a light and hopefully generic way, and to bring the risk to people's attention. All feeback is good!
#5

[eluser]stencil[/eluser]
...also being British I've never felt that comfortable calling it a 'nonce' either - as it doesn't just mean 'number used once' Wink

I'm surprised something to help combat CSRF hasn't already found it's way into the CI core.

As Jon W correctly mentioned, this is about checking User intent.

- cheers for the code, saves me bashing out my own incarnation




Theme © iAndrew 2016 - Forum software by © MyBB