How specifically does Laravel build and check a CSRF token?

14,604

Solution 1

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()
    {
       ...
    }
}

Solution 2

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

});
Share:
14,604

Related videos on Youtube

prograhammer
Author by

prograhammer

David Graham Developing web applications for The Home Depot.

Updated on June 15, 2022

Comments

  • prograhammer
    prograhammer about 2 years

    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.

  • prograhammer
    prograhammer over 10 years
    Ah...I was aware of this code, but I was looking deeper into the framework trying to see what computation is actually being done. But you're right in that we can obviously see that sessions are being used here for CSRF. This makes me think that the typical usage for CSRF protection is for logged in users. However I'm sending out JSON in different public areas of my site that I don't want easily grabbed by others who could try and use the site as if it were some public api.
  • prograhammer
    prograhammer over 10 years
    And...You really helped me out by reminding me that we can override those helpers. So the safe bet is for me to rewrite that and get these tokens out of the session. Thanks again Antonio!
  • Michel Ayres
    Michel Ayres almost 9 years
    Just adding a little info, if you are using ajax calls, you can use the following jQuery.ajaxSetup({ headers: { 'X-CSRF-Token' : $('meta[name=_token]').attr('content') } }); source
  • prograhammer
    prograhammer almost 9 years
    Yup. This is what I do in Laravel 5. It's actually mentioned in the Laravel docs now. :-)