php - api 端点未在 Sanctum 上进行 CSRF 令牌验证 - CSRF 令牌不匹配
问题描述
我目前正在学习 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,
],
当我尝试提交登录请求时,我现在得到一个带有错误 CSRF 令牌不匹配的 HTTP 419。
所以我已经取得了进展,它现在似乎正在尝试 CSRF 验证,但现在它总是说存在不匹配,即使它在请求中发送相同的 XSRF-TOKEN cookie。
解决方案
我相信我已经弄清楚了,这部分与我的HTTP
Rest 客户端(Insomnia.Rest
)有关,但我使用 axios 设置了一个测试项目,ReactJS
并且遇到了同样的问题,但后来解决了。
部分原因是Sanctums
默认配置有点乱,部分是加密session/cookies
,另一部分不是。
所以下config/session.php
集encrypt => true
在 api middlewareGroups下App\Http\Kernel.php
添加以下内容
\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
请求中添加一个新标头,该标头称为来自GET 请求X-XSRF-TOKEN
的 cookie 的值。sanctum
如果您正在为您的前端项目使用HTTP
客户端,例如 AXIOS,这将自动完成,因此您无需担心。
下一个问题是Insomnia.Rest
我使用的 HTTP 客户端。
然而,当我收到XSRF-TOKEN
Insomnia 将 cookie 存储在它的 cookie 存储中时,它们似乎对其进行了错误编码,因此您获得了一个存储如下的 cookie 字符串:
eyJpdiI6Iml5YWEreGVaYUw0WGc2QmxlVEhQOGc9PSIsInZhbHVlIjoieVU2bmdyTjMyNFM0d0dnb3RsM24rMDFhRnJNWHVLcGg2SU9YMHh5dW8yaTZSTWcxbGxtSFdaK0I5MzB4Ymc4QWZWSzhjN2R6Y1RUTTc0d1VIY2FUaVhGMVE4bzQvWVBmL1YvajAwY3ZUNlZ4VEZIRk12cloyV0owVmNYOUxEZTIiLCJtYWMiOiI4OTUyN2U1MGI3NmUyMjEzZjgyNDcxMjAwYmViYjRkNzAwYmQ1YWUxOGY5NTYyNTVhZDczMmQ0ZjdlNjQwMGFhIn0%3D
请注意,最后它有 %3D,这是 = 符号的 URL 编码,因此 Laravel 得到它并且无法与它所期望的匹配。
因此,您需要编辑 cookie 以将 %3D 替换为 =,然后发送请求,它应该可以工作。
我还有另一件奇怪的事情是,如果我发送带有 Referrer 和 Origin 标头的请求,则 CSRF 验证有效,但是,如果我不这样做,那么请求被接受,这对我来说似乎不正确,因为它有点违背了 CSRF 保护的目的。
推荐阅读
- sql - sql like operator %string% 在很少的记录上花费太多时间
- apache-spark - Spark批量从kafka读取avro rdd
- python - 仅基于条件 python 加入
- c# - FluentAssertions:如何突破Which/And cascades的向下钻取?
- r - 高于第 99 个百分位的值的百分比
- excel - 来自日期范围内多个可能性的单元格数据
- 2sxc - 804 的最高版本
- r - 按日期和名称获取最新行
- vue.js - 如何用 v-pagination 替换 v-data-table 分页
- sql - 如何将字符串添加到 SQL 中的正则表达式,然后将其转换为日期?