Laravel默认通过VerifyCsrfToken中间件和@csrf指令自动防护CSRF,要求正确使用@csrf、不绕过中间件且session正常;非安全请求触发校验,API应改用X-CSRF-TOKEN头,AJAX需配置withCredentials并读取XSRF-TOKEN cookie。

Laravel 默认通过中间件和表单隐藏字段自动防护 CSRF 攻击,不需要手动写校验逻辑 —— 但前提是正确使用 @csrf 指令、不绕过中间件、且 session 正常工作。
CSRF 中间件如何触发校验
Laravel 的 VerifyCsrfToken 中间件在每次非安全方法(POST、PUT、PATCH、DELETE)请求时自动运行。它只对以下情况生效:
- 请求携带了有效的 session(即
session_start()已执行,且 session ID 可被识别) - 请求未被显式排除(如配置了
$except数组) - 请求不是来自白名单域名(若启用了
SameSite或 CORS 策略)
注意:GET、HEAD、OPTIONS 请求默认跳过校验 —— 这是设计使然,不是漏洞。
@csrf 指令生成的 token 怎么来的
@csrf 实际等价于输出一个隐藏字段:,其中 value 来自当前 session 中存储的 _token 键。这个值在用户首次访问带 session 的页面时生成,并在整个 session 生命周期内复用(除非调用 csrf_token() 强制刷新)。
常见误区:
- 在 API 路由中错误使用
@csrf:API 通常无 session,@csrf会报错或输出空值,应改用X-CSRF-TOKEN请求头 +csrf_token() - 前端用 JS 动态提交表单却忘了传
_token:必须显式读取并附上,例如formData.append('_token', document.querySelector('input[name=_token]').value) - 多标签页共用同一 session 时,token 不会因页面刷新而更新,但多次提交同一 token 是允许的(Laravel 不一次性作废)
为什么 AJAX 请求容易 419 错误
419(Page Expired)本质是 CSRF 校验失败,最常见于 AJAX 场景,原因包括:
- 没在请求头中带上
X-XSRF-TOKEN(Laravel 会从该 header 读取 token,且要求该值是经过加密签名的) - 前端未从
XSRF-TOKENcookie 中提取值(Laravel 自动把签名后的 token 写入该 cookie,JS 可读) - 使用了
axios但没启用withCredentials: true,导致 cookie 不发送 - 前后端域名不一致(如前端
localhost:3000,后端api.test),浏览器拒绝发送 cookie
简单修复示例(axios):
axios.defaults.withCredentials = true; axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; // X-XSRF-TOKEN 会由 axios 自动从 XSRF-TOKEN cookie 读取并设置(仅限同域)
哪些地方必须关掉 CSRF 校验,怎么安全地关
只有明确无用户会话上下文、且不操作敏感状态的接口才可关闭,比如公开 webhook 接收端、静态资源回调。禁用方式只能是路由级排除:
- 在
app/Http/Middleware/VerifyCsrfToken.php的$except数组中添加路径,如'webhook/*' - 不要用
Route::withoutMiddleware()在某条路由上临时移除 —— 它绕过整个中间件栈,风险不可控 - 绝不应在控制器方法里手动跳过校验(比如重写
shouldPassThrough())
真正难处理的是 SPA 前后端分离场景:此时 session 和 token 管理边界模糊,容易误判“要不要关”。核心原则是——只要前端能维持有效 cookie,就别关,而是配好 SameSite=None; Secure 和 CORS。










