api 端点未在 Sanctum 上进行 CSRF 令牌验证 - CSRF 令牌不匹配

时间:2021-06-14 13:01:45

标签: php laravel csrf laravel-sanctum

我目前正在学习 Laravel(进行得不是特别顺利)并且我已经配置了几条路由来使用 sanctum 测试身份验证。

我正在构建一个只有 API 的 laravel 服务,并计划 ReactJS 项目将使用 API。

我目前虽然没有使用 ReactJS 并使用 Insomnia REST 客户端来测试 API。

我有一条用于注册新用户、登录的路由,然后是另一条仅返回经过身份验证的用户以证明身份验证机制正常工作的路由。

我对 CSRF 不太了解,但我的理解是我请求一个新的 CSRF 令牌,然后对于 API 的每个请求都使用此 CSRF 令牌,例如,当我登录然后从相应的路由,CSRF令牌cookie也被发送,因此如果发送不同的CSRF令牌,我应该得到令牌不匹配错误。

我正在使用 Insomnia 进行测试,方法是向 /sanctum/csrf-cookie 发送请求,该请求会返回 204 并且 Insomnia 设置了 3 个 cookie,其中一个是 XSRF-TOKEN,我理解它是 CSRF 的加密形式令牌。

然后我成功登录,然后当我调用我的路由以获取经过身份验证的用户时,我修改或删除 XSRF-TOKEN cookie 并发送请求,然后我希望收到有关令牌不匹配的错误,但是这似乎并非如此,我得到了有效回复。

下面是我的 api.php(我将各种路由分组到单独的 PHP 文件中,以便在我实际构建 API 时保持井井有条)

Route::prefix('/auth')->group(__DIR__ . '/endpoints/auth.php');

Route::middleware('auth:sanctum')->get('/me', function(){
    //return response(null, 200);
    return auth()->user();
});

在我的 /endpoints/auth.php 中,我有以下内容:

Route::post('/register', [UserController::class, "register"]);

Route::post('/login', [UserController::class, "login"]);

Route::middleware('auth:sanctum')->post('/logout', [UserController::class, 'logout']);

因此,在上面的代码中,当我在更改或删除我的 XSRF-TOKEN 后向 /api/me 发送请求时,我预计令牌不匹配,但实际上我通过经过身份验证的用户详细信息得到了 200 OK。

更新

我已经取得了一些进展。

我在 api 数组下的 App/Http/Kernel.php 中添加了如下项目:

'api' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

当我尝试提交登录请求时,我现在收到一个 HTTP 419,错误是 CSRF 令牌不匹配。

所以我已经取得了进展,现在它似乎正在尝试 CSRF 验证,但现在它总是说不匹配,即使它在请求中发送相同的 XSRF-TOKEN cookie。

2 个答案:

答案 0 :(得分:1)

我相信我已经弄清楚了,这部分与我的 HTTP Rest 客户端 (Insomnia.Rest) 有关,但我在 ReactJS 上使用 axios 设置了一个测试项目,并且同样的问题,但后来解决了。

部分原因是 Sanctums 默认配置有点混乱,部分是加密 session/cookies 而另一部分不是。

所以在config/session.php下设置encrypt => true

App\Http\Kernel.php 下将以下内容添加到 api middlewareGroups

\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,

confif/cors.php下设置support_credentials => true

config/session.php 下确保 SESSION_DRIVER 是 cookie

另一个问题是对请求中发送的内容的误解。

当您在调用 XSRF-TOKEN 时获得 /sanctum/csrf-cookie 时,我认为它只是作为 Insomnia 自动执行的 cookie 在请求中发回。

事实并非如此。相反,您应该从 Insomnia 请求中提取 cookie,然后在每个 POST/PUT/DELETE 请求中向 X-XSRF-TOKEN GET 请求中的 cookie 的值添加一个名为 sanctum 的新标头。

如果您为前端项目(例如 AXIOS)使用 HTTP 客户端,这会自动完成,因此您无需担心。

下一个问题是我使用的 Insomnia.Rest HTTP 客户端。

当我收到 XSRF-TOKEN 时,Insomnia 将 cookie 存储在它的 cookie 存储中,但是,他们似乎对它进行了错误的编码,因此您会得到如下存储的 cookie 字符串:

<块引用>

eyJpdiI6Iml5YWEreGVaYUw0WGc2QmxlVEhQOGc9PSIsInZhbHVlIjoieVU2bmdyTjMyNFM0d0dnb3RsM24rMDFhRnJNWHVLcGg2SU9YMHh5dW8yaTZSTWcxbGxtSFdaK0I5MzB4Ymc4QWZWSzhjN2R6Y1RUTTc0d1VIY2FUaVhGMVE4bzQvWVBmL1YvajAwY3ZUNlZ4VEZIRk12cloyV0owVmNYOUxEZTIiLCJtYWMiOiI4OTUyN2U1MGI3NmUyMjEzZjgyNDcxMjAwYmViYjRkNzAwYmQ1YWUxOGY5NTYyNTVhZDczMmQ0ZjdlNjQwMGFhIn0%3D

请注意,最后它有 %3D,这是 = 符号的 URL 编码,因此 Laravel 得到了它并且无法与预期的匹配。

因此您需要编辑 cookie 以将 %3D 替换为 =,然后发送请求,它应该可以工作。

不过,我还有一件奇怪的事情是,如果我发送带有 Referrer 和 Origin 标头的请求,CSRF 验证会起作用,但是,如果我不这样做,则该请求将被接受,这对我来说似乎不正确这有点违背了 CSRF 保护的目的。

答案 1 :(得分:0)

您不会遇到与 /api/me 不匹配的令牌,因为这是一个 GET 请求,而 CSRF 保护针对的是可能执行未经授权命令的端点。

我认为您可能(还)不了解 CSRF 是什么以及 CRSF 保护应该做什么。

这里,仅仅列出用户并不是未经授权的命令;您可以一遍又一遍地请求它,而不会发生任何其他事情。它不会更改数据库中的任何内容、进行汇款、重置密码或其他任何事情。

这是否有助于思考为什么请求任何 GET 或 HEAD 不涉及 CSRF 检查?

只是请求 URI 本身不应该为授权用户执行不需要的命令;传统上只有 POST 和其他方式可以用于在 Web 应用程序、API 上运行某些命令;在 GET 请求中包含所有参数是不可接受的原因有很多,其中最主要的原因是潜在的敏感信息泄露。

如果你查看文档,它明确提到只检查 POST、PUT、PATCH 和 DELETE 的秘密会话值。可能需要进一步阅读的原因。

[1] https://laravel.com/docs/8.x/csrf