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:
- Has the missing "X-" some meaning?
- How do I change the encrypted version of the token to the plain one? (regardless of the question #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?)
- 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!
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);
}
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!
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