Why generated CSRF protection token is not saved and used via SESSION like suggested here? Currently in CI2, the CSRF protection mechanism (in Security class) is such:
1.generate a unique value for CSRF token in _csrf_set_hash() function:
$this->csrf_hash = md5(uniqid(rand(), TRUE));
2.Insert that token into form hidden field (using form_open helper)
3.A user submits the form and a server gets the token via POST. The CI performs token verification in "_sanitize_globals()" function in Input class:
$this->security->csrf_verify();
4.The function "csrf_verify" of Security class just checks is POST['token'] set and is POST['token'] equal to COOKIE['token'];
public function csrf_verify(){
// If no POST data exists we will set the CSRF cookie
if (count($_POST) == 0)
{
return $this->csrf_set_cookie();
}
// Do the tokens exist in both the _POST and _COOKIE arrays?
if ( ! isset($_POST[$this->_csrf_token_name]) OR
! isset($_COOKIE[$this->_csrf_cookie_name]))
{
$this->csrf_show_error();
}
// Do the tokens match?
if ($_POST[$this->_csrf_token_name] != $_COOKIE[$this->_csrf_cookie_name])
{
$this->csrf_show_error();
}
// We kill this since we're done and we don't want to
// polute the _POST array
unset($_POST[$this->_csrf_token_name]);
// Nothing should last forever
unset($_COOKIE[$this->_csrf_cookie_name]);
$this->_csrf_set_hash();
$this->csrf_set_cookie();
log_message('debug', "CSRF token verified ");
return $this;
}
Why not to store token in session? IMHO just checking is POST['token'] non-empty and is equal to COOKIE['token'] is not sufficient because both might be sent by an evil site.
There are a few reasons.
First being that storing the token in the cookie is not insecure. Anti-CSRF is not designed to prevent automated posting of content, it is to prevent forging a request as an authenticated user (via an iframe, or a simple link). So long as the token itself is not guessable, that is enough.
The second being if it's stored in the session, then you need sessions enabled, this also causes usability issues because if your session times out and you have a page open with a form, you can no longer submit that form (even if the form itself doesn't require a logged in state).
Because CSRF stands for "Cross-site Request Forgery." An example of this attack would be if you knew someone had a wordpress install at
http://somedomain.com/wordpress
. You could get them to click on a new link, which would really go do something bad over on their wordpress control panel. CSRF was designed to prevent this, verifying that the action taken was intended by the user taking the action.Even if someone knew how to forge both the cookie and the hidden form field to match, there's no way to do that cross-site, and there's no forgery to prevent anyway.
In CodeIgniter, they don't use native PHP sessions anywhere in the code.
The example you provided is shown using native PHP sessions.
While using CodeIgniter Session class, there's either: store data via cookies, or store them in database. [ reference: http://codeigniter.com/user_guide/libraries/sessions.html ]
While checking for csrf data, it wouldn't make sense to check the database every time, it would be plausible to store them in the cookies.
I think it's generally safe, but there are some windows of vulnerability with this method. Perhaps encrypting it with server side key might help increase the security...
EDIT:
https://code.djangoproject.com/wiki/CsrfProtection#Sessionindependentnonce
According to the article, it says that CSRF Protection with Session independent nonce (used by CodeIgniter) has a vulnerability to CSRF + MITM Attack (Man-in-the-Middle):
Pretty much, the function csrf_verify() only checks whether the cookie and input POST is equal, which both can be created through simple javascript. You should think twice about using this if you are serious about security.
Source: How does this Man-In-The-Middle attack work?