CSRF 防护


介绍

跨站请求伪造(CSRF)是一种恶意攻击,可以在经过认证的用户的名义下执行未授权的命令。幸运的是,Laravel 可以轻松保护您的应用免受跨站请求伪造攻击

漏洞的解释

如果您不熟悉跨站请求伪造,请让我们来讨论一下这个漏洞的示例。假设您的应用有一个 /user/email 路由,接受一个 POST 请求来更改已认证用户的电子邮件地址。最有可能,该路由会期望一个电子邮件输入字段 email 包含用户想要开始使用的电子邮件地址。

如果没有 CSRF 保护,恶意网站可以创建一个指向您的应用程序 /user/email 路由的 HTML 表单,并提交恶意用户自己的电子邮件地址:

<form action="https://your-application.com/user/email" method="POST">
    <input type="email" value="malicious-email@example.com">
</form>
 
<script>
    document.forms[0].submit();
</script>

如果恶意网站在页面加载时自动提交表单,恶意用户只需要引诱您的应用程序的无意识用户访问他们的网站,他们的电子邮件地址就会在您的应用程序中被更改。

为了防止此漏洞,我们需要检查每个传入的 POSTPUTPATCHDELETE 请求是否具有恶意应用程序无法访问的秘密会话值。

阻止 CSRF 请求

Laravel 会自动为每个应用程序所管理的活动用户会话生成 CSRF “令牌”。此令牌用于验证认证用户实际上是在向应用程序发出请求的人。由于此令牌存储在用户会话中并且每次会话重新生成时都会更改,因此恶意应用程序无法访问它。

可以通过请求的会话或 csrf_token 帮助函数访问当前会话的 CSRF 令牌:

use Illuminate\Http\Request;
 
Route::get('/token', function (Request $request) {
    $token = $request->session()->token();
 
    $token = csrf_token();
 
    // ...
});

在应用程序中定义任何“POST”、“PUT”、“PATCH”或“DELETE” HTML表单时,都应在表单中包含一个隐藏的 CSRF _token 字段,以便 CSRF 保护中间件可以验证请求。为方便起见,可以使用 @csrf Blade 指令生成隐藏的令牌输入字段:

<form method="POST" action="/profile">
    @csrf
 
    <!-- Equivalent to... -->
    <input type="hidden" name="_token" value="{{ csrf_token() }}" />
</form>

默认情况下包含在 web 中间件组中的 App\Http\Middleware\VerifyCsrfToken 中间件将自动验证请求输入中的令牌与存储在会话中的令牌是否匹配。当这两个令牌匹配时,我们知道认证用户正在发起请求。

CSRF 令牌和 SPA

如果您正在构建一个将 Laravel 用作 API 后端的SPA,则应查阅 Laravel Sanctum 文档,了解有关如何验证 API 和防止 CSRF 漏洞的信息。

从 CSRF 保护中排除 URI

有时,您可能希望从 CSRF 保护中排除一组 URI。例如,如果您正在使用 Stripe 处理付款并使用其 Webhook 系统,则需要从 CSRF 保护中排除 Stripe Webhook 处理程序路由,因为 Stripe 不知道要发送到您的路由的 CSRF 令牌。

通常,您应该将这些路由放在 routes/web.php 文件中 App\Providers\RouteServiceProvider 适用于所有路由的 web 中间件组之外。但是,您也可以通过将它们的 URI 添加到 VerifyCsrfToken 中间件的 $except 属性中来排除这些路由:

<?php
 
namespace App\Http\Middleware;
 
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
 
class VerifyCsrfToken extends Middleware
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        'stripe/*',
        'http://example.com/foo/bar',
        'http://example.com/foo/*',
    ];
}

为方便起见,在运行测试时,所有路由都会自动禁用 CSRF 中间件。

X-CSRF-TOKEN

除了检查 CSRF token 是否作为 POST 参数提交,App\Http\Middleware\VerifyCsrfToken 中间件还会检查 X-CSRF-TOKEN 请求头。例如,您可以将 token 存储在 HTML meta 标记中:

<meta name="csrf-token" content="{{ csrf_token() }}">

然后,您可以指示像 jQuery 这样的库自动将令牌添加到所有请求头中。使用传统的 JavaScript 技术,这为基于 AJAX 的应用程序提供了简单、方便的 CSRF 保护:

$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});

X-XSRF-TOKEN

Laravel 将当前的 CSRF token 存储在一个加密的 XSRF-TOKEN cookie 中,该 cookie 包含了框架生成的每个响应。您可以使用 cookie 值来设置 X-XSRF-TOKEN 请求头。

这个 cookie 主要作为开发人员的便利,因为一些 JavaScript 框架和库,如 Angular 和 Axios,在同源请求上自动将其值放在 X-XSRF-TOKEN 头中。

默认情况下,resources/js/bootstrap.js 文件包括 Axios HTTP 库,它将自动为您发送 X-XSRF-TOKEN 头。


点赞 取消点赞 收藏 取消收藏

<< 上一篇: 中间件

>> 下一篇: 控制器