2

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!

orustammanapov
  • 1,792
  • 5
  • 25
  • 44
  • @ThomasMoors could you please be more precise about how does my server know if is my token? Does a private secret come into play? and could you suggest an option to tinker with in order to activate the plain token? Thanks – orustammanapov Jul 20 '17 at 19:05

3 Answers3

3

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);
    }
online Thomas
  • 8,864
  • 6
  • 44
  • 85
  • shouldn't the token be inside the cookie until the user session expires? I still don't get how to make my middleware pass the test? should I really be tinkering with it (middleware) and call `decrypt` method (doesn't sound like a right solution to me)? Isn't there some configuration option I've missed so far to set the plain session cookie or should I set it manually before I render my view? – orustammanapov Jul 20 '17 at 20:08
  • Cookies identify a session (unless you use the cookie driver) but it's values are stored somewhere depending on [the driver](https://laravel.com/docs/5.4/session#configuration) – online Thomas Jul 21 '17 at 11:29
  • Oh, I think I misunderstood the cookie, session and token part, I'am familiar with a session workflow (at least I think I'am :)). I've mixed up a session cookie with a token, you're probably meant that token doesn't have to be stored inside a cookie since it can be sent with a request header (like in AJAX reuest), am I right? In that case I want to clarify my question a bit, I was actually referring to its encryption, if I list the token name under `$excepted` it won't be encrypted and I don't get `TokenMismatchException` anymore, I was wondering if it was a proper way to get around my issue? – orustammanapov Jul 21 '17 at 11:52
  • I fail to understand why you can't do it like in the provided tutorial provided by Laravel and what you are trying to solve exactly. – online Thomas Jul 21 '17 at 11:54
  • I had my app since laravel version 5.2, and I've already migrated to version 5.3 and now to 5.4 since. I've read that in version 5.3 (if I'am not mistaken) one of the session tokens was taken out and brought back in version 5.4 again. I can't find which configuration option I should tinker with to solve my problem. My problem is, that I constantly get `TokenMismatchException`, when I call `csrf_token()` or `csrf_field()` the token that is generated is the plain one, but the session token stored in a cookie is encrypted, when I change the session token to the plain version, problem disappears. – orustammanapov Jul 21 '17 at 12:02
  • Hence the question, am I doing it right (converting session token to plain one) or does my way of doing it have some security concerns, that I'am not aware of? and is it a hack to list my token under `$excepted` inside `EcryptedCookies` or should it be avoided and other way used instead? – orustammanapov Jul 21 '17 at 12:05
  • 1
    You **don't** need to encrypt your csrf-token: it holds no significant data: it just tells "This request was made from your website, [not a fake / xhr / ajax call from another website.](https://www.incapsula.com/web-application-security/csrf-cross-site-request-forgery.html) – online Thomas Jul 21 '17 at 12:09
  • 1
    thanks, the part where it doesn't have to be encrypted is clear to me now, the thing is, that it wasn't me who started encrypting it. it was a default behavior after my update and I struggle since my application isn't some clean, fresh laravel install but a code base instead and now I have to find out a way to get rid of this exception without creating security holes. Now what is a proper way (best practice) in laravel to make out of encrypted cookie an unencrypted one (because it was the only way for me to get rid of the exception, for now at least)? – orustammanapov Jul 21 '17 at 12:32
1

Although I can't pinpoint the source of my problem (due to lack of experience with the framework) I could finally solve my issue. What I've missed so far is that some middleware classes were moved to a different middleware group in the app/Http/Kernel.php file (my problem probably appeared because order in which they're loaded have changed, or maybe because some of them weren't loaded at all?). I'm pretty sure, that it was the cause of my problem, because I've constantly reloaded my page along with clearing out the cache like suggested on github, it was suggested by jeremykenedy. The suggestion was to call the following commands:

sudo rm -rf storage/framework/sessions/*
sudo php artisan cache:clear
sudo php artisan clear-compiled
sudo composer dump-autoload

which all of you tackling a similar problem have probably encountered hundred of times already. Another suggestion I often found was to change permissions on the storage folder, it might help to some of you tackling a similar problem!

orustammanapov
  • 1,792
  • 5
  • 25
  • 44
0

I found a interesting things about token mismatch exception. I have faked my date time in AppServiceProvider as:

/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
    Carbon::setTestNow('2000-07-05 4:00 pm');
}

After that I coundn't logged in into my laravel application. Otherwise just one command enough to solve token mismatch exception.

php artisan config:clear
Bedram Tamang
  • 3,748
  • 31
  • 27