JasonGenX
JasonGenX

Reputation: 5444

Laravel 5 null cookie crash

Scenario:

Same browser.

  1. Tab 1: logging into my laravel application.
  2. Tab 2: logging into my laravel application.
  3. Tab 2: log off
  4. Tab 1: Click a button that causes a redirect to a route that's protected by: Route::group(['middleware' => 'auth'], function () { ...

Result: Laravel 5 crashes before it gets to my code on:

enter image description here

Stack:

Symfony\Component\Debug\Exception\FatalErrorException Call to a member function setCookie() on null
    vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php:184 __construct
    vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php:131 fatalExceptionFromError
    vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php:116 handleShutdown
    vendor/laravel/framework/src/Illuminate/Foundation/Bootstrap/HandleExceptions.php:0 addCookieToResponse
    vendor/laravel/framework/src/Illuminate/Session/Middleware/StartSession.php:72 handle
    vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:125 Illuminate\Pipeline\{closure}
    vendor/laravel/framework/src/Illuminate/Cookie/Middleware/AddQueuedCookiesToResponse.php:36 handle
    vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:125 Illuminate\Pipeline\{closure}
    vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php:40 handle
    vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:125 Illuminate\Pipeline\{closure}
    vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/CheckForMaintenanceMode.php:42 handle
    vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:125 Illuminate\Pipeline\{closure}
    vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:101 call_user_func:{/home/vagrant/dev/opus-web-app/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:101}
    vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:101 then
    vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:115 sendRequestThroughRouter
    vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:84 handle
    public/index.php:53 {main}
    public/index.php:0 [main]

Even when I remove the Route::group(['middleware' => 'auth'] group from my routes... going in tab 1 to the now open URLs will produce this error. I just don't get it.

How do I get rid of this?

Upvotes: 11

Views: 3835

Answers (4)

gthuo
gthuo

Reputation: 2566

Experienced a similar issue specifically when returning a response from a middleware, and I realised it was caused by returning return view('my.view.blade') rather than return response()->view('my.view.blade').

So in short, don't return the view directly. Chain the view() helper to the response() helper.

return view('myview.blade'); //wrong
return response()->view('myview.blade'); //works

Upvotes: 0

Nestor Mata Cuthbert
Nestor Mata Cuthbert

Reputation: 1359

Seems to me that the problem is your logic in the check of the CSRF.

Here is the problem: You are checking if the tokens match, and that function is as follow:

protected function tokensMatch($request)
{
    $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');

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

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

But if you look closely you will see that in the return it checks for:

$request->session()->token()

But, the session is null so you will get an exception for trying to request a method from a null.

So, if I'm not mistaken, all you need to do is just to add an additional check to the if in the handle method. Instead of this:

if ($request && ((
      $this->isReading($request) 
      || $this->excludedRoutes($request) 
      || ($this->tokensMatch($request) && Auth::check())
    )))

You should have this:

if ($request && ((
      $this->isReading($request) 
      || $this->excludedRoutes($request) 
      || ($request->hasSession() && $this->tokensMatch($request) && Auth::check())
   )))

This way, if there is no session it will not even check if tokens match and it will not crash there.

You may also consider refactor that part of the if statement to a function that by name it describes what you are checking for, and even the complete if statement to a function that says it all. Just for code clarity.


Is either that, or you may consider checking your other Middlewares, anyone that be touching the headers of the request and potentially be unsetting the headers.

If you take at the code of the handle function of the StartSession Middleware:

public function handle($request, Closure $next)
{
    $this->sessionHandled = true;
    if ($this->sessionConfigured())
    {
        $session = $this->startSession($request);
        $request->setSession($session);
    }

    // ** HERE ALL OTHER MIDDLEWARES ARE BEING CALL
    $response = $next($request);

    if ($this->sessionConfigured())
    {
        $this->storeCurrentUrl($request, $session);
        $this->collectGarbage($session);
        // ** AFTER ALL OTHER MIDDLEWARES ARE CALLED THE FOLLOWING FUNCTION 
        // TOUCHES THE HEADER AND IS NULL
        $this->addCookieToResponse($response, $session);
    }
    return $response;
}

So, it is possible that somewhere in any of any other middleware you are touching the header and leaving it null, as per your error says.

I hope that helps.

Upvotes: 0

Vision Coderz
Vision Coderz

Reputation: 8078

May be this will usefull for you .check in middleware some think similar like this

public function handle($request, Closure $next)
{
    $response = $next($request);

    if ( ! Auth::user()) // Your logic here...
        abort(403, 'Unauthorized action.');

    return $response;
}

Ref::https://laracasts.com/discuss/channels/general-discussion/disable-global-verifycsrftoken-and-use-as-middleware-doesnt-work-setcookie-on-null

in that discussion some cookie error discussed.that might help you

Upvotes: 0

JasonGenX
JasonGenX

Reputation: 5444

I figured out the cause, but I'm just not sure about it. Hoping one of you will know.

In kernel.php:

I had the 'App\Http\Middleware\VerifyCsrfToken' defined in the $middleware black and not the $routeMiddleware. when I moved it to the $routeMiddleware I stopped getting that error.

Content of VerifyCsrfToken:

class VerifyCsrfToken extends BaseVerifier {

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

        return view('sessionOver');
    }

    protected function excludedRoutes($request)
    {
        $routes = [
            'deployPush'  // webhook push for bitBucket.
        ];

        foreach($routes as $route)
            if ($request->is($route))
                return true;

        return false;
    }
}

Upvotes: 3

Related Questions