Excluding URI from CSRF Protection not working

Posted 3 years ago by adamtomat

According to the docs (5.1) it's possible to exclude URI's easily with the protected $except property, like so:

    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        'api/*'
    ];

Here I am trying to exclude the Set-Cookie :XSRF-TOKEN from API requests. However, this isn't working.

So I dug into Illuminate\Foundation\Http\Middleware\VerifyCsrfToken and became really confused. By the looks of it, the exclude will be ignored on any GET request, and therefore you get a cookie set. Here's the handle method:

    public function handle($request, Closure $next)
    {
        if ($this->isReading($request) || $this->shouldPassThrough($request) || $this->tokensMatch($request)) {
            return $this->addCookieToResponse($request, $next($request));
        }

        throw new TokenMismatchException;
    }

isReading() will return true if the request is GET, rendering the results of shouldPassThrough() pointless. There is only 2 outcomes to this method too: 1) you get a cookie set, 2) you get an exception. I'd expect a third outcome of no cookie if the request matches an except item, right?

Also, looking at shouldPassThrough() seems backwards too. It returns true if the request matches an except item:

    /**
     * Determine if the request has a URI that should pass through CSRF verification.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return bool
     */
    protected function shouldPassThrough($request)
    {
        foreach ($this->except as $except) {
            if ($request->is($except)) {
                return true;
            }
        }

        return false;
    }

The method docs suggest that if it returns true it should pass through CSRF verification...i.e. get a cookie. But it only returns true if it matches an except item i.e. we don't want a cookie.

To get the behaviour I expect, I had to create my own addCookieToResponse method which only adds a cookie if the request doesn't match any except items;

    protected function addCookieToResponse($request, $response)
    {
        if (!$this->shouldPassThrough($request)) {
            $response = parent::addCookieToResponse($request, $response);
        }

        return $response;
    }

Can somebody either validate this (as a bug?), or explain to me how it is intended to work please? Because what the docs (and code) imply don't make sense to me.

Please sign in or create an account to participate in this conversation.

Laracasts Mascot

Hi, Have We Met Yet?

Did you know that, in addition to the forum, Laracasts includes well over 1000 lessons on modern web development? All for the price of one lunch out per month.

Sign Me Up

Channels

Reply to

Use Markdown with GitHub-flavored code blocks.