路由中间件之 VerifyCsrfToken

简介

[
    'App\Http\Middleware\EncryptCookies',
    'Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse',
    'Illuminate\Session\Middleware\StartSession',
    'Illuminate\View\Middleware\ShareErrorsFromSession',
    'App\Http\Middleware\VerifyCsrfToken',  // 本章内容
    'Illuminate\Routing\Middleware\SubstituteBindings',
    'App\Http\Middleware\RedirectIfAuthenticated',
]

本章,我们围绕 VerifyCsrfToken 中间件进行源码分析。从字面意思我们知道,这个中间件就是实现 CSRF 令牌验证的中间件。如果我们在调试 web 路由时,被 CSRF 影响,可以注释掉此中间件,即可消除 CSRF 令牌的影响。如下:

Laravel

四种情况下,将不会抛出 CSRF 异常

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

    throw new TokenMismatchException;
}

大家看上面的源码,内容与逻辑一目了然。当 isReadingrunningUnitTestsinExceptArraytokensMatch 有一个为真,那么就通过 CSRF 令牌验证,否则抛出异常。

isReading

protected function isReading($request)
{
    return in_array($request->method(), ['HEAD', 'GET', 'OPTIONS']);
}

从源码我们知道:isReading 意思是当请求方法是 HEAD、GET、OPTIONS 中的一个时,直接通过 CSRF 验证。

runningUnitTests

protected function runningUnitTests()
{
    return $this->app->runningInConsole() && $this->app->runningUnitTests();
}

runningUnitTests 是指当 php 在控制台运行且是运行环境为测试环境时,通过 CSRF 请求

inExceptArray

protected function inExceptArray($request)
{
    foreach ($this->except as $except) {
        if ($except !== '/') {
            $except = trim($except, '/');
        }

        if ($request->fullUrlIs($except) || $request->is($except)) {
            return true;
        }
    }

    return false;
}

inExceptArray 指排除掉在 except 属性中指定的路由,不对其进行 CSRF 验证。

except 属性指定位置如下:

Laravel

tokensMatch

protected function tokensMatch($request)
{
    $token = $this->getTokenFromRequest($request);

    return is_string($request->session()->token()) &&
           is_string($token) &&
           hash_equals($request->session()->token(), $token);
}

tokensMatch 即请求过来的 token 与服务端存储的 token 进行防时序 hash 验证,看看是否相等,如果相等通过 CSRF 验证,如果不相等则抛出异常。

那么如何获取请求过来的 token 呢

protected function getTokenFromRequest($request)
{
    // 重点在这里,获取 token 的两种方式:在请求体中加入 _token 字段,在请求头中加入 X-CSRF-TOKEN 字段
    $token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');

    if (! $token && $header = $request->header('X-XSRF-TOKEN')) {
        // 第三种 token 获取方式,从请求头 X-XSRF-TOKEN 字段解密获取请求过来 token,这种更加麻烦与安全
        $token = $this->encrypter->decrypt($header, static::serialized());
    }

    return $token;
}

关于官方 CSRF 的内容 --> 传送门

本篇如有错误、不当或者需补充的内容,请各位同僚多提宝贵意见。

本文章首发在 LearnKu.com 网站上。
上一篇 下一篇
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
讨论数量: 0
发起讨论 只看当前版本


暂无话题~