laraveltokencsrfcsrf-protection

What is the right way to resolve token mismatch error in laravel?


since I've updated laravel to 5.4 I constantly get:

TokenMismatchException in VerifyCsrfToken.php line 68

exception thrown. After some digging and reading through a whole lot of posts and github issues I've figured that my tokens aren't matching :). The point is that my laravel app sets the encrypted version of the token "XSRF-TOKEN" instead of its plain (X-CSRF-TOKEN) counterpart and the helper csrf_token() spits out the plain token hence mismatching tokens. Confusing though why documentation mentions X-XSRF-TOKEN when I get XSRF-TOKEN (missing X-) instead? So the questions are:

  1. Has the missing "X-" some meaning?
  2. How do I change the encrypted version of the token to the plain one? (regardless of the question #3)
  3. Should I even try to make a plain token out of the encrypted one or is it better to encrypt csrf_token() instead? (Does it even matter, since the connection is encrypted?)
  4. Is listing "XSRF-TOKEN" under $excepted in the \MiddleWare\EncryptCookies.php a viable option or could you suggest a better solution instead? (which sort of brings us back to the question #3)

I'm sorry for so many questions and thanks in advance!

Edit

After rereading my questions several times I came to conclusion that they weren't clear enough and didn't complied with the title. My problem were the mismatching tokens and I thought having my doubts cleared would lead me to a solution, I thank @ThomasMoors for his patience and help. I will accept his answer, and although I've solved my problem a different way, it was his help that led me to the solution! I've additionally posted my own answer which describes how I've solved my issue to help others having similar issues!


Solution

  • The function that checks the token tries to find it (1) inside a request plain with the key _token, if it does not find it, it will try to look inside (2) the request headers with the key X-CSRF-TOKEN. The token to match is stored inside the session, where the session lives depends on your config.

        /**
         * 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->header('X-CSRF-TOKEN');
            if ( ! $token && $header = $request->header('X-XSRF-TOKEN'))
            {
                $token = $this->encrypter->decrypt($header);
            }
            return StringUtils::equals($request->session()->token(), $token);
        }
    

    Q: Has the missing "X-" some meaning?

    A: Laravel stores the current CSRF token in a XSRF-TOKEN cookie that is included with each response generated by the framework. You can use the cookie value to set the X-XSRF-TOKEN request header.

    This cookie is primarily sent as a convenience since some JavaScript frameworks and libraries, like Angular and Axios, automatically place its value in the X-XSRF-TOKEN header.

    Q: How do I change the encrypted version of the token to the plain one? (regardless of the question #3)

    A: As in the code above (from github) \Illuminate\Contracts\Encryption\Encrypter has a function called decrypt() that does this

    Q: Should I even try to make a plain token out of the encrypted one or is it better to encrypt csrf_token() instead? (Does it even matter, since the connection is encrypted?)

    A: It does not matter, because the token has no data inside it, it is just an identifier of a legit request originating from your site and not some ajax/xhr call from another domain. read more on https://en.wikipedia.org/wiki/Cross-site_request_forgery

    Q: Is listing "XSRF-TOKEN" under $excepted in the \MiddleWare\EncryptCookies.php a viable option or could you suggest a better solution instead? (which sort of brings us back to the question #3)

    A: The token has no reason to be inside a cookie at any time, does it?

    UPDATE

    It seems that from 5.0 to 5.4 indeed something has changed in the function above. It looks like this now:

     protected function tokensMatch($request)
        {
            $token = $this->getTokenFromRequest($request);
            return is_string($request->session()->token()) &&
                   is_string($token) &&
                   hash_equals($request->session()->token(), $token);
        }