跨站请求伪造是一种恶意攻击,通过它,未经授权的命令以已认证用户的名义执行。值得庆幸的是,Laravel 让保护您的应用程序免受 跨站请求伪造 (CSRF) 攻击变得容易。
如果你不熟悉跨站请求伪造,我们来讨论一个这种漏洞如何被利用的例子。想象一下,你的应用程序有一个 /user/email 路由,它接受一个 POST 请求来更改已认证用户的电子邮件地址。很可能,这个路由期望一个 email 输入字段包含用户希望开始使用的电子邮件地址。
如果没有 CSRF 保护,恶意网站可以创建一个 HTML 表单,指向您应用程序的 /user/email 路由并提交恶意用户自己的电子邮件地址:
<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>如果恶意网站在页面加载时自动提交表单,恶意用户只需诱骗您的应用程序的毫无戒心的用户访问他们的网站,即可在您的应用程序中更改他们的电子邮件地址。
为了防止此漏洞,我们需要检查每一个传入的 POST、PUT、PATCH 或 DELETE 请求是否存在恶意应用程序无法访问的秘密会话值。
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 中间件组中,将自动验证请求输入中的令牌是否与会话中存储的令牌匹配。当这两个令牌匹配时,我们知道已认证用户是发起该请求的用户。
如果您正在构建一个使用 Laravel 作为 API 后端的 SPA,您应该查阅Laravel Sanctum 文档,以获取有关如何通过 API 进行身份验证以及如何防范 CSRF 漏洞的信息。
有时您可能希望将一组 URI 从 CSRF 保护中排除。例如,如果您正在使用 Stripe 处理支付并正在利用他们的 Webhook 系统,您将需要将您的 Stripe Webhook 处理程序路由从 CSRF 保护中排除,因为 Stripe 将不知道要向您的路由发送什么 CSRF 令牌。
通常,你应该将这些类型的路由放置在 Laravel 应用于 routes/web.php 文件中所有路由的 web 中间件组之外。然而,你也可以通过向你应用的 bootstrap/app.php 文件中的 validateCsrfTokens 方法提供它们的 URI 来排除特定路由:
->withMiddleware(function (Middleware $middleware): void {
$middleware->validateCsrfTokens(except: [
'stripe/*',
'http://example.com/foo/bar',
'http://example.com/foo/*',
]);
})[!注意]
为方便起见,当运行测试时,CSRF 中间件会在所有路由上自动禁用。
除了检查作为 POST 参数的 CSRF 令牌之外,Illuminate\Foundation\Http\Middleware\ValidateCsrfToken 中间件,默认情况下包含在 web 中间件组中,还会检查 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')
}
});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头部。