I'm using Laravel's CSRF protection on my public site. However since Laravel uses a session to maintain this, I'm worried that a user might walk away from their computer and return to a page they have previously left open, only to find ajax requests don't work. The ajax requests don't work because the session has timed out (and the token no longer validates?). If these users were "logged in" users, then I could simply redirect them back to the login page. Since they are public users, then the user is forced to refresh the page to get it back working (awkward).
Or am I wrong about this? Would the CSRF token still get validated by Laravel (even after the session has timed out, the page will still send over the token...but what will Laravel do with it?). An optimal solution would be to have the tokens partially based on a timestamp so that we could give the tokens expiration limits apart from session time limits. I could make my CSRF tokens last for 2 days (so only those users that walk away for 2 days will return to a dead page).
Ultimately this brings me to my question: Where is the specific code in the Laravel framework that handles this? I'm currently trying to locate it. Also, is there an easy drop in replacement I can make, or am I left to create my own version of csrf_token();
to output to my pages and then I would need to create my own route filter to go with it.
Laravel just facilitates that for you by keeping the token stored in session, but the code is actually yours (to change as you wish). Take a look at filters.php
you should see:
Route::filter('csrf', function()
{
if (Session::token() != Input::get('_token'))
{
throw new Illuminate\Session\TokenMismatchException;
}
});
It tells us that if you have a route:
Route::post('myform', ['before' => 'csrf', 'uses' => 'MyController@update']);
And the user session expires, it will raise an exception, but you can do the work yourself, keep your own token stored wherever you think is better, and instead of throwing that exception, redirect your user to the login page:
Route::filter('csrf', function()
{
if (MySession::token() != MyCSRFToken::get())
{
return Redirect::to('login');
}
});
And, yes, you can create your own csrf_token()
, you just have to load it before Laravel does. If you look at the helpers.php file in Laravel source code, you`ll see that it only creates that function if it doesn't already exists:
if ( ! function_exists('csrf_token'))
{
function csrf_token()
{
...
}
}
Since this has become a popular question, I decided to post my specific solution that has been working quite nicely...
Most likely you will have a header.php or some partial view that you use at the top of all your pages, make sure this is in it in the <head>
section:
<meta name="_token" content="<?=csrf_token(); ?>" />
In your filters.php:
Route::filter('csrf', function()
{
if (Request::ajax()) {
if(Session::token() != Request::header('X-CSRF-Token')){
throw new Illuminate\Session\TokenMismatchException;
}
}
});
And in your routes.php
Route::group(array('before' => 'csrf'), function(){
// All routes go in here, public and private
});
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