CSRF 保护
简介
跨站请求伪造 (CSRF) 是一种恶意攻击,攻击者通过这种攻击以已认证用户的身份执行未经授权的命令。幸运的是,Laravel 使保护你的应用免受 跨站请求伪造 (CSRF) 攻击变得非常容易。
漏洞说明
如果你不熟悉跨站请求伪造,让我们讨论一个如何利用此漏洞的示例。假设你的应用有一个 /user/email
路由,它接受 POST
请求以更改已认证用户的电子邮件地址。很可能,此路由期望 email
输入字段包含用户想要开始使用的电子邮件地址。
在没有 CSRF 保护的情况下,恶意网站可以创建一个指向你的应用 /user/email
路由的 HTML 表单,并提交恶意用户的电子邮件地址。
<form action="https://your-application.com/user/email" method="POST"></form> <script> document.forms[0].submit();</script>
如果恶意网站在页面加载时自动提交表单,则恶意用户只需诱骗你应用的不知情用户访问他们的网站,他们的电子邮件地址就会在你应用中被更改。
为了防止此漏洞,我们需要检查每个传入的 POST
、PUT
、PATCH
或 DELETE
请求,以查看是否存在恶意应用无法访问的秘密会话值。
防止 CSRF 请求
Laravel 会自动为应用管理的每个活动 用户会话 生成一个 CSRF “令牌”。此令牌用于验证已认证用户是否确实是向应用发出请求的人。由于此令牌存储在用户的会话中,并且每次会话重新生成时都会更改,因此恶意应用无法访问它。
当前会话的 CSRF 令牌可以通过请求的会话或 csrf_token
助手函数访问。
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>
Illuminate\Foundation\Http\Middleware\ValidateCsrfToken
中间件(默认情况下包含在 web
中间件组中)将自动验证请求输入中的令牌是否与存储在会话中的令牌匹配。当这两个令牌匹配时,我们就知道已认证用户是发起请求的人。
CSRF 令牌和 SPA
如果你正在构建一个使用 Laravel 作为 API 后端的 SPA,则应查阅 Laravel Sanctum 文档,了解有关使用 API 进行身份验证和防止 CSRF 漏洞的信息。
从 CSRF 保护中排除 URI
有时你可能希望从 CSRF 保护中排除一组 URI。例如,如果你使用 Stripe 处理支付并使用其 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/*', ]);})
为了方便起见,当 运行测试时,CSRF 中间件会自动对所有路由禁用。
X-CSRF-TOKEN
除了将 CSRF 令牌作为 POST 参数进行检查之外,默认情况下包含在 web
中间件组中的 Illuminate\Foundation\Http\Middleware\ValidateCsrfToken
中间件还会检查 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
标头中。
默认情况下,resources/js/bootstrap.js
文件包含 Axios HTTP 库,该库会自动为你发送 X-XSRF-TOKEN
标头。