I'm using Codeigniter and I want to prevent CSRF attacks attempts that may happen. And to achieve this I add a hidden input tag with a random token to each form I want to protect, and in the same time I keep this token in a session to compare against when begin handling this form data.
// set a token to prevent CSRF attacks
$csrf_token = md5(uniqid(rand(), true));
$this->session->set_userdata("csrf_token", $csrf_token);
And the form will look like this:
<form action="path/to/handler/page" method="post">
<input type="text" name="title">
<input type="text" name="date">
<textarea name="content"></textarea>
<input type="hidden" name="csrf_token" value="<?php echo $this->session->userdata("csrf_token") ?>">
<input type="submit" name="submit" value="Save">
</form>
And in the page where I handle submitted data I check CSRF attacks something like this:
// make sure there is no CSRF attack attempt
$csrf_token = $this->session->userdata("csrf_token");
if (empty($csrf_token) || $csrf_token !== $this->input->post("csrf_token")) {
die("Some message here!!");
}
And that works pretty good. But as you see I generate a random token for each page containing a form and in some cases this causes a problem if for example I opened another tab in the browser to perform some another action. Consider this scenario:
add.php
page to add a new item. edit.php
page in another tab in the browser to edit an existing item.add.php
page that was filled in and tried to submit the data.At this point I will get an error because value of the token that has been stored in the session when I opened add.php
page has been changed and replaced with another token when I open the edit.php
page. So how can I fix this problem? Should I generate just one token for each user when he successfully login and then use this token in all pages that he may deal with? Does this approach has any risks or any cons?
I scanned your post and don't see a reason why not to use the base codeigniter CSRF protection? It seems you are reinventing the wheel and creating problems that don't exist within its standard implementation.
Not to mention you are violating DRY principles by trying to print your tokens to every form. Any reason why not to keep it simple?
Codeigniter has build in CSRF protection that can be enabled in /application/config/config.php
$config['csrf_protection'] = TRUE;
$config['csrf_token_name'] = 'csrf_token_name';
$config['csrf_cookie_name'] = 'csrf_cookie_name';
$config['csrf_expire'] = 7200;
To solve this problem, you could create a token string with a unique key and store the pairs of keys/tokens in the session (as userdata in CodeIgniter).
Considering this scenario, You'll need these steps:
<input>
elements for CSRF key and token.$csrf_key = "TOKEN_" . mt_rand(0, mt_getrandmax());
$csrf_token = hash("sha512", mt_rand(0, mt_getrandmax()));
// Store the key/token pair in session
$this->session->set_userdata($csrf_key, $csrf_token);
Adding hidden input
s:
<form action="path/to/handler/page" method="post">
<!-- form input elements -->
<input type="hidden" name="csrf_key" value="<?php echo $csrf_key; ?>">
<input type="hidden" name="csrf_token" value="<?php echo $this->session->userdata($csrf_key); ?>">
</form>
Validating the posted key/token:
if (count($_POST)) {
if (! isset($_POST['csrf_key']) or ! isset($_POST['csrf_token'])) {
die('No CSRF token found, invalid request.');
}
$key = $this->input->post('csrf_key');
$token = $this->input->post('csrf_token');
if ($token !== $this->session->userdata($key)) {
die('Invalid CSRF token, access denied.');
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With