简介

跨站点请求伪造是一种恶意攻击类型,利用这种手段,代表经过身份验证的用户执行未经授权的命令。值得庆幸的是,Laravel 可以轻松保护你的应用程序免受 跨站点请求伪造(CSRF)攻击。

漏洞的解释
如果你不熟悉跨站点请求伪造,我们讨论一个利用此漏洞的示例。假设你的应用程序有一个 php /user/email 路由,它接受 php POST 请求来更改经过身份验证用户的电子邮件地址。最有可能的情况是,此路由希望 php email 输入字段包含用户希望开始使用的电子邮件地址。

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

  1. <form action="https://your-application.com/user/email" method="POST">
  2. <input type="email" value="[email protected]">
  3. </form>
  4. <script>
  5. document.forms[0].submit();
  6. </script>

如果恶意网站在页面加载时自动提交了表单,则恶意用户只需要诱使你的应用程序的一个毫无戒心的用户访问他们的网站,他们的电子邮件地址就会在你的应用程序中更改。

为了防止这种漏洞,我们需要检查每一个传入的 php POSTphp PUTphp PATCHphp DELETE 请求以获取恶意应用程序无法访问的秘密会话值。

阻止 CSRF 请求

Laravel 会自动为应用程序管理的每个活动 用户会话 生成一个 CSRF 「令牌」。此令牌用于验证经过身份验证的用户是否是实际向应用程序发出请求的人。由于此令牌存储在用户的会话中,并且每次重新生成会话时都会发生变化,因此恶意应用程序无法访问它。

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

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

每当你在应用程序中定义 php POSTphp PUTphp PATCHphp DELETE HTML 表单时,都应该在表单中包含一个隐藏的 CSRF php _token 字段,以便 CSRF 保护中间件可以验证请求。为了方便起见,你可以使用 php @csrf Blade 指令来生成隐藏令牌输入字段:

  1. <form method="POST" action="/profile">
  2. @csrf
  3. <!-- 相当于... -->
  4. <input type="hidden" name="_token" value="{{ csrf_token() }}" />
  5. </form>

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

CSRF Tokens & SPAs

如果你正在构建使用 Laravel 作为 API 后端的 SPA,则应查阅 Laravel Sanctum 文档,了解有关使用 API 进行身份验证和防范 CSRF 漏洞的信息。

从 CSRF 保护中排除 URI

有时你可能希望从 CSRF 保护中排除一组 URIs。例如,如果你使用 Stripe 处理付款并使用他们的 webhook 系统,则需要将你的 Stripe webhook 处理程序路由从 CSRF 保护中排除,因为 Stripe 不会知道要向你的路由发送什么 CSRF 令牌。

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

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


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

X-CSRF-TOKEN

除了检查POST参数中的CSRF令牌外,php Illuminate\Foundation\Http\Middleware\ValidateCsrfToken 中间件默认包含在 php web 中间件组中,它还会检查 php X-CSRF-TOKEN 请求头。例如,你可以将令牌存储在 HTML 的 php meta 标签中:

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

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

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

X-XSRF-TOKEN

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

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


技巧
默认情况下,php resources/js/bootstrap.js 文件包含 Axios HTTP 库,它会自动为你发送 php X-XSRF-TOKEN 标头。