Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How specifically does Laravel build and check a CSRF token?

Tags:

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.

like image 496
prograhammer Avatar asked Mar 24 '14 13:03

prograhammer


Video Answer


2 Answers

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()
    {
       ...
    }
}
like image 187
Antonio Carlos Ribeiro Avatar answered Sep 16 '22 14:09

Antonio Carlos Ribeiro


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

});
like image 27
prograhammer Avatar answered Sep 16 '22 14:09

prograhammer