VerifyCsrfToken.php
TLDR
The file VerifyCsrfToken.php
contains the VerifyCsrfToken
class, which is a middleware used for CSRF token verification. It verifies if the CSRF token in the request matches the one stored in the session. If the tokens match, the middleware allows the request to proceed. If the tokens do not match, the middleware throws a TokenMismatchException
.
Methods
handle
This method handles an incoming request. It checks whether the request is a read request, running in unit tests, or excluded from CSRF verification. If none of these conditions are met and the CSRF tokens do not match, it throws a TokenMismatchException
.
isReading
This method determines if the HTTP request uses a 'read' verb (HEAD, GET, OPTIONS).
runningUnitTests
This method determines if the application is running unit tests.
inExceptArray
This method determines if the request URI is in the $except
array or the $neverVerify
array.
tokensMatch
This method determines if the session and input CSRF tokens match.
getTokenFromRequest
This method retrieves the CSRF token from the request.
shouldAddXsrfTokenCookie
This method determines if the XSRF-TOKEN cookie should be added to the response.
addCookieToResponse
This method adds the CSRF token to the response cookies.
newCookie
This method creates a new "XSRF-TOKEN" cookie that contains the CSRF token.
except
This static method allows specific URIs to be excluded from CSRF verification.
serialized
This static method determines if the cookie contents should be serialized.
<?php
namespace Illuminate\Foundation\Http\Middleware;
use Closure;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Contracts\Encryption\Encrypter;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Cookie\CookieValuePrefix;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Session\TokenMismatchException;
use Illuminate\Support\Arr;
use Illuminate\Support\InteractsWithTime;
use Symfony\Component\HttpFoundation\Cookie;
class VerifyCsrfToken
{
use InteractsWithTime;
/**
* The application instance.
*
* @var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
/**
* The encrypter implementation.
*
* @var \Illuminate\Contracts\Encryption\Encrypter
*/
protected $encrypter;
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array<int, string>
*/
protected $except = [];
/**
* The globally ignored URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected static $neverVerify = [];
/**
* Indicates whether the XSRF-TOKEN cookie should be set on the response.
*
* @var bool
*/
protected $addHttpCookie = true;
/**
* Create a new middleware instance.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
* @return void
*/
public function __construct(Application $app, Encrypter $encrypter)
{
$this->app = $app;
$this->encrypter = $encrypter;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*
* @throws \Illuminate\Session\TokenMismatchException
*/
public function handle($request, Closure $next)
{
if (
$this->isReading($request) ||
$this->runningUnitTests() ||
$this->inExceptArray($request) ||
$this->tokensMatch($request)
) {
return tap($next($request), function ($response) use ($request) {
if ($this->shouldAddXsrfTokenCookie()) {
$this->addCookieToResponse($request, $response);
}
});
}
throw new TokenMismatchException('CSRF token mismatch.');
}
/**
* Determine if the HTTP request uses a ‘read’ verb.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function isReading($request)
{
return in_array($request->method(), ['HEAD', 'GET', 'OPTIONS']);
}
/**
* Determine if the application is running unit tests.
*
* @return bool
*/
protected function runningUnitTests()
{
return $this->app->runningInConsole() && $this->app->runningUnitTests();
}
/**
* Determine if the request has a URI that should pass through CSRF verification.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function inExceptArray($request)
{
foreach (array_merge($this->except, static::$neverVerify) as $except) {
if ($except !== '/') {
$except = trim($except, '/');
}
if ($request->fullUrlIs($except) || $request->is($except)) {
return true;
}
}
return false;
}
/**
* Determine if the session and input CSRF tokens match.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function tokensMatch($request)
{
$token = $this->getTokenFromRequest($request);
return is_string($request->session()->token()) &&
is_string($token) &&
hash_equals($request->session()->token(), $token);
}
/**
* Get the CSRF token from the request.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
protected function getTokenFromRequest($request)
{
$token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
if (! $token && $header = $request->header('X-XSRF-TOKEN')) {
try {
$token = CookieValuePrefix::remove($this->encrypter->decrypt($header, static::serialized()));
} catch (DecryptException) {
$token = '';
}
}
return $token;
}
/**
* Determine if the cookie should be added to the response.
*
* @return bool
*/
public function shouldAddXsrfTokenCookie()
{
return $this->addHttpCookie;
}
/**
* Add the CSRF token to the response cookies.
*
* @param \Illuminate\Http\Request $request
* @param \Symfony\Component\HttpFoundation\Response $response
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function addCookieToResponse($request, $response)
{
$config = config('session');
if ($response instanceof Responsable) {
$response = $response->toResponse($request);
}
$response->headers->setCookie($this->newCookie($request, $config));
return $response;
}
/**
* Create a new "XSRF-TOKEN" cookie that contains the CSRF token.
*
* @param \Illuminate\Http\Request $request
* @param array $config
* @return \Symfony\Component\HttpFoundation\Cookie
*/
protected function newCookie($request, $config)
{
return new Cookie(
'XSRF-TOKEN',
$request->session()->token(),
$this->availableAt(60 * $config['lifetime']),
$config['path'],
$config['domain'],
$config['secure'],
false,
false,
$config['same_site'] ?? null,
$config['partitioned'] ?? false
);
}
/**
* Indicate that the given URIs should be excluded from CSRF verification.
*
* @param array|string $paths
* @return void
*/
public static function except($paths)
{
static::$neverVerify = array_values(array_unique(
array_merge(static::$neverVerify, Arr::wrap($paths))
));
}
/**
* Determine if the cookie contents should be serialized.
*
* @return bool
*/
public static function serialized()
{
return EncryptCookies::serialized('XSRF-TOKEN');
}
}