revolution/laravel-fetch-metadata 是一个用于校验浏览器发送的 Sec-Fetch-* HTTP header 的安全中间件包。你可以根据请求的发起来源、模式、目标以及是否由用户操作触发,来控制允许通过的请求。
借助浏览器内建的安全能力,可以在不影响正常用户体验的前提下,阻止来自恶意来源的非法请求。
在 Laravel 13 中,CSRF 保护的 Origin 校验开始使用 Sec-Fetch-Site header。详情请参考 CSRF 保护。
关于 Fetch Metadata 的详细规范,请参考 MDN 文档。
composer require revolution/laravel-fetch-metadata
注册中间件别名
在 bootstrap/app.php 中注册中间件别名。
use Illuminate\Foundation\Configuration\Middleware;
use Revolution\FetchMetadata\Middleware\SecFetchSite;
use Revolution\FetchMetadata\Middleware\SecFetchMode;
use Revolution\FetchMetadata\Middleware\SecFetchDest;
use Revolution\FetchMetadata\Middleware\SecFetchUser;
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'sec-fetch-site' => SecFetchSite::class,
'sec-fetch-mode' => SecFetchMode::class,
'sec-fetch-dest' => SecFetchDest::class,
'sec-fetch-user' => SecFetchUser::class,
]);
})
你也可以只使用其中的一部分中间件。别名可以任意修改。
中间件说明
SecFetchSite
Sec-Fetch-Site header 表示请求的发起来源与目标 origin 之间的关系。默认只允许 same-origin 与 none(直接访问)。
| 值 | 说明 |
|---|
same-origin | 来自同一 origin 的请求 |
same-site | 来自同一 site(如子域名)的请求 |
cross-site | 来自不同 site 的请求 |
none | 用户直接输入 URL 等由导航发起的请求 |
详情请参考 MDN 文档。
SecFetchMode
Sec-Fetch-Mode header 表示请求的模式。默认允许 navigate 与 cors。
| 值 | 说明 |
|---|
navigate | 由链接点击、表单提交等发起的导航请求 |
cors | CORS 请求 |
no-cors | no-cors 请求 |
same-origin | 同 origin 请求 |
websocket | WebSocket 连接请求 |
详情请参考 MDN 文档。
SecFetchDest
Sec-Fetch-Dest header 表示请求目标资源的类型。
详情请参考 MDN 文档。
SecFetchUser
Sec-Fetch-User header 表示请求是否由用户操作触发。值只有 ?1(有用户操作)。
使用 SecFetchUser 中间件会同时拦截搜索引擎爬虫和 AI agent。请不要用在需要被索引的公开页面上。
在路由中的使用示例
基本用法
use Illuminate\Support\Facades\Route;
use Illuminate\Http\Request;
Route::post('user/update-password', function (Request $request) {
//
})->middleware('sec-fetch-site');
通过参数指定允许值
Route::post('user/update-password', function (Request $request) {
//
})->middleware('sec-fetch-site:cross-site');
指定多个参数
Route::post('user/update-password', function (Request $request) {
//
})->middleware('sec-fetch-site:same-origin,cross-site');
不使用别名的情况
use Revolution\FetchMetadata\Middleware\SecFetchSite;
Route::post('user/update-password', function (Request $request) {
//
})->middleware(SecFetchSite::class.':same-origin,cross-site');
组合多个中间件
Route::post('user/update-profile', function (Request $request) {
//
})->middleware(['sec-fetch-site:same-origin', 'sec-fetch-mode:navigate']);
错误处理
当 Sec-Fetch-* header 的值不合法时,会抛出 Symfony\Component\HttpKernel\Exception\BadRequestHttpException。
可以在 bootstrap/app.php 中自定义响应。
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
->withExceptions(function (Exceptions $exceptions) {
$exceptions->render(function (BadRequestHttpException $e, Request $request) {
if ($request->expectsJson()) {
return response()->json([
'message' => $e->getMessage(),
], 400);
}
});
})
与 CSRF 保护的关系
在 Laravel 13 中,PreventRequestForgery 中间件作为 CSRF 保护的第一步,会通过 Sec-Fetch-Site header 进行 Origin 校验。本包在更细粒度上进一步利用 Fetch Metadata header,可以进一步增强应用的安全性。
详情请参考 CSRF 保护。