CSRF protection difference: Laravel 4.x, 5.0 and 5.1

For those who work with different Laravel versions on different projects, it's useful to know the difference of CSRF logic - it changed a little from 4.x to 5.x.

Laravel 4.2

In version 4.x we had filters. So we could add something into app/filters.php file, and one of pre-created filters was CSRF - right at the end of the file:

Route::filter('csrf', function()
{
    if (Session::token() !== Input::get('_token'))
    {
        throw new Illuminate\Session\TokenMismatchException;
    }
});

And then we can use that filter on whichever routes we prefer, usually on POST calls:

Route::post('register', array('before' => 'csrf', function()
{
    return 'You gave a valid CSRF token!';
}));

And, of course, we shouldn't forget to include the token itself in our forms:

<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">

So that was 4.x. Pretty straightforward, huh?

Laravel 5.0

Version 5.0 (which, actually, originally was planned as 4.3) introduced Middleware instead of filters. And there was a particular set of Middleware classes that were loaded by default. Including CSRF.

app/Http/Kernel.php - the last line of the array:

class Kernel extends HttpKernel {

    protected $middleware = [
        'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
        'Illuminate\Cookie\Middleware\EncryptCookies',
        'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
        'Illuminate\Session\Middleware\StartSession',
        'Illuminate\View\Middleware\ShareErrorsFromSession',
        'Illuminate\Foundation\Http\Middleware\VerifyCsrfToken',
    ];

This VerifyCsrfToken class was included in default $middleware which, in essence, meant that all POST calls are csrf-protected by default. And that became a problem.

See, not every POST request comes from a form. There are external calls for some API function, AJAX requests and various other cases. So people started to rant and search for the answer how to remove CSRF from default middleware. Of course, the obvious answer is to not include it in $middleware array, and add it whenever needed, like in good old Laravel 4.

Laravel 5.1 - problem solved

So, Taylor saw the problem and changed the logic in 5.1 version. Actually, there are several new things here:

1. You can use function csrf_field() to generate the whole input instead of just token.
So instead of:

<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">

You can just write:

{!! csrf_field() !!}

2. There is still VerifyCsrfToken present in $middleware array:

    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
    ];

3. BUT: you can exclude it from certain URLs you want. You can to it directly in app/Http/Middleware/VerifyCsrfToken.php class, by using property $except:

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;

class VerifyCsrfToken extends BaseVerifier
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        'ajax/*',
    ];
}

Notice that you can use a * (star) symbol to specify the rule, and this rule describes URLs, not routes.

So that's a brief history of CSRF in Laravel so far. Would you add more "tricks" that you use for CSRF protection?

No comments yet…

Like our articles?

Become a Premium Member for $129/year or $29/month

Written by