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

Boa*_*rdy 7 php csrf laravel laravel-sanctum

我目前正在学习 Laravel(进展不是特别顺利),并且我配置了一些路由来使用 sainttum 测试身份验证。

我正在构建一个仅 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();
});
Run Code Online (Sandbox Code Playgroud)

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

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

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

Route::middleware('auth:sanctum')->post('/logout', [UserController::class, 'logout']);
Run Code Online (Sandbox Code Playgroud)

因此,在上面的代码中,当我在更改或删除 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,
        ],
Run Code Online (Sandbox Code Playgroud)

当我尝试提交登录请求时,我现在收到 HTTP 419,其中包含错误 CSRF 令牌不匹配。

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

Boa*_*rdy 14

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

部分原因是因为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,
Run Code Online (Sandbox Code Playgroud)

confif/cors.phpsupport_credentials => true

config/session.php确保下SESSION_DRIVER是cookie

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

当你接到XSRF-TOKEN电话时/sanctum/csrf-cookie,我假设它只是作为 cookie 在请求中发回,Insomnia 会自动执行此操作。

不是这种情况。相反,您应该从 Insomnia 请求中提取 cookie,然后在每个POST/PUT/DELETE请求中添加一个新标头,X-XSRF-TOKEN该标头调用 GET 请求中 cookie 的值sanctum

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

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

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

eyJpdiI6Iml5YWEreGVaYUw0WGc2QmxlVEhQOGc9PSIsInZhbHVlIjoieVU2bmdyTjMyNFM0d0dnb3RsM24rMDFhRnJNWHVLcGg2SU9YMHh5dW8yaTZSTWcxbGxtSFdaK0I5MzB4Ymc4QWZWSzhjN2R 6Y1RUTTc0d1VIY2FUaVhGMVE4bzQvWVBmL1YvajAwY3ZUNlZ4VEZIRk12cloyV0owVmNYOUxEZTIiLCJtYWMiOiI4OTUyN2U1MGI3NmUyMjEzZjgyNDcxMjAwYmViYjRkNzAwYmQ1YWUxOGY5NTYyNTVh ZDczMmQ0ZjdlNjQwMGFhIn0%3D

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

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

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

  • “这有点违背了 CSRF 保护的目的”,因为 CSRF 令牌并不是用来保护 API 调用的,它的目的是防止表单伪造/跨站点攻击。您可以使用 Origin 标头、JWT (JsonWebToken) 进行额外保护,许多东西都是无状态的。coockies 和 CSRF 用于有状态调用。 (2认同)

小智 6

我也遇到了这个错误。我将此代码添加到.env文件中,错误消失了。

SESSION_DOMAIN=localhost
SANCTUM_STATEFUL_DOMAINS=localhost
Run Code Online (Sandbox Code Playgroud)