路由中间件之 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 网站上。

上一篇 下一篇
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 0
发起讨论 只看当前版本


暂无话题~