laravel
4. 基础知识
CSRF 保护

介绍

跨站请求伪造是一种恶意利用类型,通过这种方式,未经授权的命令会以经过身份验证的用户的名义执行。值得庆幸的是,Laravel 可以轻松保护您的应用程序免受跨站请求伪造 (opens in a new tab)(CSRF)攻击。

漏洞解释

如果您不熟悉跨站请求伪造,让我们讨论一个如何利用此漏洞的示例。假设您的应用程序有一个/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
 
    <!-- 等同于... -->
    <input type="hidden" name="_token" value="{{ csrf_token() }}" />
</form>

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

CSRF 令牌与单页应用程序(SPAs)

如果您正在构建一个将 Laravel 用作 API 后端的 SPA,则应查阅Laravel Sanctum 文档,以获取有关使用您的 API 进行身份验证以及防止 CSRF 漏洞的信息。

从 CSRF 保护中排除 URI

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

通常,您应该将这类路由放置在 Laravel 在 routes/web.php 文件中应用于所有路由的 web 中间件组之外。但是,您也可以通过在应用程序的 bootstrap/app.php 文件中向 validateCsrfTokens 方法提供其 URI 来排除特定路由:

->withMiddleware(function (Middleware $middleware) {
    $middleware->validateCsrfTokens(except: [
        'stripe/*',
        'http://example.com/foo/bar',
        'http://example.com/foo/*',
    ]);
})

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

X-CSRF-TOKEN

默认包含在 web 中间件组中的 Illuminate\Foundation\Http\Middleware\ValidateCsrfToken 中间件,除了检查作为 POST 参数的 CSRF 令牌外,还会检查 X-CSRF-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 令牌存储在一个加密的 XSRF-TOKEN Cookie 中,该 Cookie 包含在框架生成的每个响应中。您可以使用 Cookie 值来设置 X-XSRF-TOKEN 请求头。

此 Cookie 主要是为了开发人员的方便而发送的,因为一些 JavaScript 框架和库,如 Angular 和 Axios,会在同源请求中自动将其值放置在 X-XSRF-TOKEN 头中。

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