> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# CSRF 保护

> 介绍 CSRF 攻击的原理，以及 Laravel 13 通过 Origin 校验和令牌校验进行防御的方法。

## 简介

CSRF（Cross-Site Request Forgery）是一种冒充已登录用户，让其发送非本人意图请求的攻击。

例如，你的应用有一个 `POST /user/email` 接口，用于修改邮箱地址。如果攻击者在另一个网站上放置了自动向该 URL 提交的表单，那么用户可能在毫无察觉的情况下被修改了邮箱。

在 Laravel 13 中，由于 `web` 中间件组已内置相关机制，CSRF 保护默认处于启用状态。

## 防御 CSRF 攻击

Laravel 的 `PreventRequestForgery` 中间件通过以下两个层面防御 CSRF：

1. **Origin 校验**（`Sec-Fetch-Site` 头）
2. **令牌校验**（会话级 CSRF 令牌）

它首先检查 Origin，若无法判定或校验失败，则回退到令牌校验。

## Origin 校验

Laravel 首先检查 `Sec-Fetch-Site`，判断请求是否来自同一 Origin。这在 HTTPS 环境下尤其有效。

若 Origin 校验通过，则该请求会被直接放行。若未通过，则会像以往那样执行 CSRF 令牌校验。

<Warning>
  `Sec-Fetch-Site` 的前提是通过 HTTPS 连接使用。在 HTTP 环境下 Origin 校验不会生效，令牌校验将成为主要的防御手段。
</Warning>

### Origin-only 模式

你可以禁用令牌校验的回退，仅通过 Origin 校验来判断。

```php theme={null}
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware): void {
        $middleware->preventRequestForgery(originOnly: true);
    });
```

在 Origin-only 模式下，Origin 校验失败的请求将返回 `403` 而不是 `419`。

如果你希望允许同站请求（例如跨子域请求），可以设置 `allowSameSite`。

```php theme={null}
$middleware->preventRequestForgery(allowSameSite: true);
```

## 令牌校验

Laravel 会为每个会话生成 CSRF 令牌。可以通过 `csrf_token()` 或会话获取。

```php theme={null}
use Illuminate\Http\Request;

Route::get('/token', function (Request $request) {
    $tokenFromSession = $request->session()->token();
    $tokenFromHelper = csrf_token();
});
```

在 `web` 路由中创建 `POST`、`PUT`、`PATCH`、`DELETE` 表单时，务必包含 `@csrf`。

```blade theme={null}
<form method="POST" action="/profile">
    @csrf

    <!-- 等效的 hidden input -->
    <input type="hidden" name="_token" value="{{ csrf_token() }}" />
</form>
```

## 排除 URI

对于像 Stripe Webhook 这样由外部服务发来的请求，有时需要将特定 URI 从 CSRF 保护中排除。

```php theme={null}
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware): void {
        $middleware->preventRequestForgery(except: [
            'stripe/*',
            'http://example.com/foo/bar',
            'http://example.com/foo/*',
        ]);
    });
```

<Info>
  如果可能，请将 Webhook 路由放在 `web` 中间件组之外，并将排除设置控制在最小范围。
</Info>

## X-CSRF-TOKEN 头

Laravel 不仅会校验表单中的 `_token`，也会校验 `X-CSRF-TOKEN` 头。

首先，将令牌输出到 meta 标签。

```blade theme={null}
<meta name="csrf-token" content="{{ csrf_token() }}">
```

然后将该值添加到 AJAX 请求头中。

```js theme={null}
$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': document
            .querySelector('meta[name="csrf-token"]')
            .getAttribute('content'),
    },
});
```

## X-XSRF-TOKEN 头

Laravel 还会发送加密过的 `XSRF-TOKEN` Cookie。Axios 或 Angular 在发送同源请求时，会自动将该值设置到 `X-XSRF-TOKEN` 头中。

因此，在 SPA 或 AJAX 实现中，某些情况下即使不手动编写请求头设置，CSRF 保护也能生效。

## SPA 的注意事项

当在 SPA 中将 Laravel 作为 API 后端使用时，需要先获取 CSRF Cookie，再发送登录请求。

```js theme={null}
await axios.get('/sanctum/csrf-cookie');
await axios.post('/login', {
    email: 'user@example.com',
    password: 'password',
});
```

详细内容请参阅 [Sanctum](/zh/sanctum)。


## Related topics

- [Blade 模板](/zh/blade.md)
- [Laravel Fetch Metadata](/zh/packages/laravel-fetch-metadata.md)
- [2026 年 3 月 Laravel 更新](/zh/blog/changelog/202603.md)
- [中间件](/zh/middleware.md)
- [HTTP 客户端](/zh/http-client.md)
