Forum

Disabling the CSRF Middleware in Laravel 5

I noticed CSRF Middleware is applied to all POST and DELETE requests by default. Is there anyway to disable it and only apply it on selected routes?

Thanks

opilo
opilo
  • 2 years ago

I removed 'Illuminate\Foundation\Http\Middleware\VerifyCsrfToken' in app/Http/Kernel.php.

    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',
    ];

does anyone know if there's another way?

Sorry to inject here, but you have marked as solution but there is still a question. I'd like to know the correct way also to turn it off.

jimgwhit said:

Sorry to inject here, but you have marked as solution but there is still a question. I'd like to know the correct way also to turn it off.

You are right,I reopened the topic.

I would edit your app/Http/Middleware/VerifyCsrfToken.php and change the following:

public function handle($request, Closure $next)
{
        // Add this:
        if($request->method() == 'POST')
        {
        return $next($request);
        }

    if ($request->method() == 'GET' || $this->tokensMatch($request))
    {
        return $next($request);
    }
    throw new TokenMismatchException;
}

Obviously you can add other conditions, like checking the origin, etc.

I have no app/Http/Middleware/VerifyCsrfToken.php file, nor folder.

The VerifyCsrfToken Middleware was absorbed into the actual framework a little while ago, so you won't find that file in any up-to-date install of Laravel 5.

CSRF is now a "middleware" registered globally in App\Http\Kernel.php. Removing it will default to no CSRF protection (Laravel4 behavior).

To enable it in a route:

Create a short-hand key in your app/Providers/RouteServiceProvider.php :

protected $middleware = [
  // ....
  'csrf'  => 'Illuminate\Foundation\Http\Middleware\VerifyCsrfToken',
];

You can now enable it to any Route:

$router->post('url', ['middleware' => 'csrf', function() {
 ... 
}]);

Not the most elegant solution IMO...

Offtopic:

Form POST should be CSRF checked. But what about AJAX calls, RESTful API requests etc??? I think Laravel should not be so opinionated...

igaster said: Form POST should be CSRF checked. But what about AJAX calls, RESTful API requests etc??? I think Laravel should not be so opinionated...

Exactly

In my humble opinion, framework's opinions are not that bad if they can be changed easily.

Using Laravel + some frontend framework and JWT for authentication, there is no need of CSRF in any way but if you can disable it removing it from the middleware list, that is not bad at all.

I guess Laravel has some goal in mind, to provide some standards for the most common applications. If apart from that, it allows you to override those standards, fine for me.

Why do you think that you don't need to check AJAX/API calls for CSRF? In case of API, you might have a different authentication method (e.g. via headers), but AJAX calls are just as susceptible to CSRF as non-AJAX ones.

What we do in our apps is add a <meta name="csrf-token" content="{{{ Session::token() }}}"> in our <head> in all pages, and we've abstracted AJAX calls with a custom function that always appends the token to all calls automatically. It's just a little bit of upfront work, with no extra work later on, but all of our AJAX endpoints are also protected against CSRF.

(We actually use RESTful API endpoints for AJAX, with authentication allowed either through the session, or via headers for non-browser-based API clients, and skip the CSRF check for non-AJAX calls, but this is beyond the scope of this answer.)

This thread is a little old but ranked high on Google; so apologies for the late response - it's for posterity :)

The proper way to neuter the default behavior of using the App\Http\Middleware\VerifyCsrfToken middleware from your Laravel 5 application would be to nix it from App/Http/Kernel.php. Before:

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

After:

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

Or, assuming you're doing things only by REST, you can get rid of all of the involved middleware overhead, which you'd probably end up substituting a custom authentication/authorization middleware for later:

    protected $middleware = [
    ];

In any kind of hybrid API/end-user application any of this would be bad form, yada, yada, yada. You can enable/disable on a per-route basis similar to @igaster's comment above.

Another simple option is what I did when I needed to allow post requests to an RESTful API was create my own middleware that extends VerifyCsrfToken, then add in your own exclusion function. After just replace the one in App/Http/Kernal.php with your own. I like this as I wanted to keep CSRF ON as I think it's just a must for security.

In this example I called mine VerifyCsrf

File: App\Http\Middleware\VerifyCsrf.php

<?php namespace App\Http\Middleware;

class VerifyCsrf extends \Illuminate\Foundation\Http\Middleware\VerifyCsrfToken
{
    /**
     * Routes we want to exclude.
     *
     * @var array
     */
    protected $routes = [
            'api/some/route',
            'another/route/here',
            'yup/more/routes',
    ];

     /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     *
     * @throws \Illuminate\Session\TokenMismatchException
     */
    public function handle($request, \Closure $next)
    {
        if ($this->isReading($request) 
            || $this->excludedRoutes($request) 
            || $this->tokensMatch($request))
        {
            return $this->addCookieToResponse($request, $next($request));
        }

        throw new \TokenMismatchException;
    }

    /**
     * This will return a bool value based on route checking.

     * @param  Request $request
     * @return boolean
     */
    protected function excludedRoutes($request)
    {
        foreach($this->routes as $route)
            if ($request->is($route))
                return true;

            return false;
    }

}

Then I swapped it out in App\Http\Kernel.php

<?php namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{

    /**
     * The application's global HTTP middleware stack.
     *
     * @var array
     */
    protected $middleware = [
        'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
        'Illuminate\Cookie\Middleware\EncryptCookies',
        'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
        'Illuminate\Session\Middleware\StartSession',
        'Illuminate\View\Middleware\ShareErrorsFromSession',
        // 'App\Http\Middleware\VerifyCsrfToken',
                'App\Http\Middleware\VerifyCsrf',
    ];

    /**
     * The application's route middleware.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'auth' => 'App\Http\Middleware\Authenticate',
        'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
        'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
    ];

}

jnewing said:

Another simple option is what I did when I needed to allow post requests to an RESTful API was create my own middleware that extends VerifyCsrfToken, then add in your own exclusion function. After just replace the one in App/Http/Kernal.php with your own. I like this as I wanted to keep CSRF ON as I think it's just a must for security.

In this example I called mine VerifyCsrf

File: App\Http\Middleware\VerifyCsrf.php

<?php namespace App\Http\Middleware;

class VerifyCsrf extends \Illuminate\Foundation\Http\Middleware\VerifyCsrfToken
{
   /**
    * Routes we want to exclude.
    *
    * @var array
    */
   protected $routes = [
           'api/some/route',
           'another/route/here',
           'yup/more/routes',
   ];

    /**
    * Handle an incoming request.
    *
    * @param  \Illuminate\Http\Request  $request
    * @param  \Closure  $next
    * @return mixed
    *
    * @throws \Illuminate\Session\TokenMismatchException
    */
   public function handle($request, \Closure $next)
   {
       if ($this->isReading($request) 
           || $this->excludedRoutes($request) 
           || $this->tokensMatch($request))
       {
           return $this->addCookieToResponse($request, $next($request));
       }

       throw new \TokenMismatchException;
   }

   /**
    * This will return a bool value based on route checking.

    * @param  Request $request
    * @return boolean
    */
   protected function excludedRoutes($request)
   {
       foreach($this->routes as $route)
           if ($request->is($route))
               return true;

           return false;
   }

}

Then I swapped it out in App\Http\Kernel.php

<?php namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{

  /**
   * The application's global HTTP middleware stack.
   *
   * @var array
   */
  protected $middleware = [
      'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
      'Illuminate\Cookie\Middleware\EncryptCookies',
      'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
      'Illuminate\Session\Middleware\StartSession',
      'Illuminate\View\Middleware\ShareErrorsFromSession',
      // 'App\Http\Middleware\VerifyCsrfToken',
               'App\Http\Middleware\VerifyCsrf',
  ];

  /**
   * The application's route middleware.
   *
   * @var array
   */
  protected $routeMiddleware = [
      'auth' => 'App\Http\Middleware\Authenticate',
      'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
      'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
  ];

}

Great solution. Thanks!

@levacic

I'm also developing a RESTfull API using Laravel and disabling CSRF protection just seemed a really bad idea to me, but whenever a client tried to reach an endpoint (route) that expects POST, PUT or DELETE a TokenMismatchException was thrown by the application.

What I ended up doing, was to override the tokensMatch function of Illuminate\Foundation\Http\Middleware\VerifyCsrfToken To achieve this I added the following on App\Http\Middleware\VerifyCsrfToken (yes, our infamous middleware!), since it's already extending the class we want to override:

/**
 * Determine if the session and input CSRF tokens match.
 *
 * @param  \Illuminate\Http\Request  $request
 * @return bool
 */
protected function tokensMatch($request)
{
    $token = $request->input('_token') ?: $request->session()->getToken();

    if ( ! $token && $header = $request->header('X-XSRF-TOKEN'))
    {
        $token = $this->encrypter->decrypt($header);
    }

    return StringUtils::equals($request->session()->token(), $token);
}

Don't forget to also add:

use Symfony\Component\Security\Core\Util\StringUtils;

Summing up, what I did was fetch the _token on the session instead of the default Laravel's behaviour of fetching it on the input.

Just my 2 cents, hope it helps!

Checkout this tutorial on how to disable CSRF on specific routes in Laravel 5

http://www.techigniter.in/tutorials/disable-csrf-check-on-specific-routes-in-laravel-5/

That seems like an elegant solution vineetgarg90, thanks for sharing.

The way I did this was to alter the \App\Http\Middleware\VerifyCsrfToken class to check the Auth config file for paths where I do not want to have the csrf validation.

public function handle($request, Closure $next)
{
        if(in_array($request->path(), Config::get('auth.no_csrf'))) {
            return parent::addCookieToResponse($request, $next($request));
        }
        return parent::handle($request, $next);
}

and in \Config\Auth.php I added

'no_csrf' => array('nocsrf/path/1', 'no/csrf/path/2'),

I hope this helps someone.

Very nice @jasonhoule really tidy way of doing things in L5, thanks!

jasonhoule said:

The way I did this was to alter the \App\Http\Middleware\VerifyCsrfToken class to check the Auth config file for paths where I do not want to have the csrf validation.

public function handle($request, Closure $next)
{
       if(in_array($request->path(), Config::get('auth.no_csrf'))) {
           return parent::addCookieToResponse($request, $next($request));
       }
       return parent::handle($request, $next);
}

and in \Config\Auth.php I added

   'no_csrf' => array('nocsrf/path/1', 'no/csrf/path/2'),

I hope this helps someone.

Wow thanks a lot! I was stuck for so long and realized I had to disable CSRF on some routes and your solution was the most straight forward.

By the way I had to add:

use Config;

At the top of VerifyCsrfToken.php or it gave me an error of it not found.

pretty clever

jnewing said:

Another simple option is what I did when I needed to allow post requests to an RESTful API was create my own middleware that extends VerifyCsrfToken, then add in your own exclusion function. After just replace the one in App/Http/Kernal.php with your own. I like this as I wanted to keep CSRF ON as I think it's just a must for security.

In this example I called mine VerifyCsrf

File: App\Http\Middleware\VerifyCsrf.php

<?php namespace App\Http\Middleware;

class VerifyCsrf extends \Illuminate\Foundation\Http\Middleware\VerifyCsrfToken
{
   /**
    * Routes we want to exclude.
    *
    * @var array
    */
   protected $routes = [
           'api/some/route',
           'another/route/here',
           'yup/more/routes',
   ];

    /**
    * Handle an incoming request.
    *
    * @param  \Illuminate\Http\Request  $request
    * @param  \Closure  $next
    * @return mixed
    *
    * @throws \Illuminate\Session\TokenMismatchException
    */
   public function handle($request, \Closure $next)
   {
       if ($this->isReading($request) 
           || $this->excludedRoutes($request) 
           || $this->tokensMatch($request))
       {
           return $this->addCookieToResponse($request, $next($request));
       }

       throw new \TokenMismatchException;
   }

   /**
    * This will return a bool value based on route checking.

    * @param  Request $request
    * @return boolean
    */
   protected function excludedRoutes($request)
   {
       foreach($this->routes as $route)
           if ($request->is($route))
               return true;

           return false;
   }

}

Then I swapped it out in App\Http\Kernel.php

<?php namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{

  /**
   * The application's global HTTP middleware stack.
   *
   * @var array
   */
  protected $middleware = [
      'Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode',
      'Illuminate\Cookie\Middleware\EncryptCookies',
      'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
      'Illuminate\Session\Middleware\StartSession',
      'Illuminate\View\Middleware\ShareErrorsFromSession',
      // 'App\Http\Middleware\VerifyCsrfToken',
               'App\Http\Middleware\VerifyCsrf',
  ];

  /**
   * The application's route middleware.
   *
   * @var array
   */
  protected $routeMiddleware = [
      'auth' => 'App\Http\Middleware\Authenticate',
      'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
      'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
  ];

}