首页 > 解决方案 > 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。

标签: phplaravelcsrflaravel-sanctum

解决方案


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

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

所以下config/session.phpencrypt => true

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

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

confif/cors.phpsupport_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-TOKENInsomnia 将 cookie 存储在它的 cookie 存储中时,它们似乎对其进行了错误编码,因此您获得了一个存储如下的 cookie 字符串:

eyJpdiI6Iml5YWEreGVaYUw0WGc2QmxlVEhQOGc9PSIsInZhbHVlIjoieVU2bmdyTjMyNFM0d0dnb3RsM24rMDFhRnJNWHVLcGg2SU9YMHh5dW8yaTZSTWcxbGxtSFdaK0I5MzB4Ymc4QWZWSzhjN2R6Y1RUTTc0d1VIY2FUaVhGMVE4bzQvWVBmL1YvajAwY3ZUNlZ4VEZIRk12cloyV0owVmNYOUxEZTIiLCJtYWMiOiI4OTUyN2U1MGI3NmUyMjEzZjgyNDcxMjAwYmViYjRkNzAwYmQ1YWUxOGY5NTYyNTVhZDczMmQ0ZjdlNjQwMGFhIn0%3D

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

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

我还有另一件奇怪的事情是,如果我发送带有 Referrer 和 Origin 标头的请求,则 CSRF 验证有效,但是,如果我不这样做,那么请求被接受,这对我来说似乎不正确,因为它有点违背了 CSRF 保护的目的。


推荐阅读