Welcome Guest, Not a member yet? Register   Sign In
CSRF Protection - token values not unique to each request?
#1

[eluser]Joey Dendron[/eluser]
I came across an issue in an application I'm developing which caused me to have a look at the code that generates CI2.0 CSRF-protection tokens.

I've read a couple of times that CSRF protection relies on a new token being generated for each HTTP request. But it looks like CI only generates a new token if no token exists in the session cookie (code from system/core/Session.php below).

Doesn't that mean the CSRF protection is compromised? If the token's the same from one request to another (because it's pulled from a cookie that persists between requests), that feels like it subverts the principles of CSRF protection.

Have I misunderstood something?

Thanks

Code:
protected function _csrf_set_hash()
{
  if ($this->_csrf_hash == '')
  {
   // If the cookie exists we will use it's value.
   // We don't necessarily want to regenerate it with
   // each page load since a page could contain embedded
   // sub-pages causing this feature to fail
   if (isset($_COOKIE[$this->_csrf_cookie_name]) &&
    preg_match('#^[0-9a-f]{32}$#iS', $_COOKIE[$this->_csrf_cookie_name]) === 1)
   {
    return $this->_csrf_hash = $_COOKIE[$this->_csrf_cookie_name];
   }
   return $this->_csrf_hash = md5(uniqid(rand(), TRUE));
  }

  return $this->_csrf_hash;
}
#2

[eluser]jojo777[/eluser]
I use the method used by Ion Auth.

Like this:
- Create a hidden input id=id and value= id_user for example
- Create a random name + random value for a hidden input in your form and send it to the view
- Create 2 flash data with those 2 values.

So you have in your view something like this:

Code:
<input type="hidden" name="id" value="32" />
<input type="hidden" name="Fr049aJa" value="qJJ3TAuEz7huQKImfEHx" />

You know flashdata is only accesible once for next http request so when the users post the form we check the hidden inputs

Code:
if ($this->input->post()){
         // Valid Request??
   if ($this->_valid_csrf_nonce() === FALSE || $this->session->userdata('id') != $this->input->post('id'))
   {
    show_error('Security checks not passed');
   }
#...
}

The _valid_csrf_nonce() function checks if the value of the random key inside the flashdata('key) y passed and is equal to the flashdata('value') and returns TRUE if so.

If you need more explanation ask, I'll do my best.
#3

[eluser]WanWizard[/eluser]
If you generate a token per request, you need to be able to track a token-stack centrally, otherwise it goes horribly wrong when you have multiple (concurrent or not) requests.
#4

[eluser]Joey Dendron[/eluser]
Thanks guys, that definitely gives me some options.

The flashdata approach sounds like it'll work for standard HTTP POST requests, and I guess I need to think more carefully about the impact of AJAX on the system.
#5

[eluser]jojo777[/eluser]
[quote author="Joey Dendron" date="1351163282"]Thanks guys, that definitely gives me some options.

The flashdata approach sounds like it'll work for standard HTTP POST requests, and I guess I need to think more carefully about the impact of AJAX on the system.[/quote]

I normally use that system for post, without AJAX, but take a look here, maybe it helps you with AJAX AJAX with CSRF Protection
#6

[eluser]Narf[/eluser]
There are configuration settings that affect how often and if the CSRF token is regenerated.
#7

[eluser]WanWizard[/eluser]
POST or not, concurrent access is always an issue with a single token that rotates on every request.

Say you open a page with a form, then open a second page in a new browser window with a form. You submit the second page (token validates), close the window, go back to the first, and submit that. Which fails because the token in the form is no longer valid.
#8

[eluser]jojo777[/eluser]
[quote author="WanWizard" date="1351162809"]If you generate a token per request, you need to be able to track a token-stack centrally, otherwise it goes horribly wrong when you have multiple (concurrent or not) requests.[/quote]

I think this is related with my previous post. Have you got something that can help me to improve CSRF protection? or some guidelines.

Thanks!
#9

[eluser]WanWizard[/eluser]
I don't really do CI development anymore.

As narf said, there are several config options you can try. If you really need tokens unique per request (or even per form), and possibly also with an expiry (invalidate the token after time even if not used), you have to replace the related core code.
#10

[eluser]jojo777[/eluser]
[quote author="WanWizard" date="1351175870"]I don't really do CI development anymore.

As narf said, there are several config options you can try. If you really need tokens unique per request (or even per form), and possibly also with an expiry (invalidate the token after time even if not used), you have to replace the related core code.
[/quote]

You are right! If I open 2 forms I only can submit the second one cause the flash data of the first one has gone (to a better life...)

I'm using something like this, I took it from Ion Auth and I tested in Ion Auth and the same is happening...

Code:
function _get_csrf_nonce()
{
   $this->load->helper('string');
   $key   = random_string('alnum', 8);
   $value = random_string('alnum', 20);
   $this->session->set_flashdata('csrfkey', $key);
   $this->session->set_flashdata('csrfvalue', $value);

   return array($key => $value);
}

So this is creating the name and value for the hidden input that is going to be inserted inside the form and assign those values to 2 flashdata...

Code:
<?php echo form_hidden($csrf); ?>
// this will show in source code this...
<input type="hidden" name="0sejtUkd" value="QNTPLp7qx0Wv5pHFRajb" />
// name and value changes every time a form is loaded

So, as I said before, you are right only the last form can be submitted. Now, how can I set a secure script for CSR? I've read there are problems with CI csrf built-in protection .

Any tip would be appreciated!




Theme © iAndrew 2016 - Forum software by © MyBB