LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

【安全防护】CSRF攻击到底有多狠?

admin
2025年12月17日 20:53 本文热度 1351

大家好,事情得从一个面试说起,那天面试小伙子简历写着精通 Laravel 安全防护,我就问他:“你做过电商项目没?遇到过 CSRF 攻击不?”他挠挠头说理论懂,实战没碰过。我心想正好拿我前阵子的教训给他上一课,其实这教训是我刚毕业那会在开发时,因为一个不起眼的功能差点捅的篓子。

事情是这样的,那时候还在实习运营刚把新开发的【用户地址批量导入】功能推给内测用户。这功能基于 Laravel 9 开发,前端用 Vue 写了个简单的导入页面,后端用 Laravel 的 Resource Controller 处理 CSV 文件解析。测试时一切顺利,CSV 里的地址能正常入库,响应速度也快。结果内测群里突然炸了:“我明明没点导入,怎么购物车被清空了?”“刚才收到短信提示地址被修改,但我没操作啊!”

我赶紧查日志,好家伙,日志里一堆 POST /user/address/batch-import的请求,IP 地址五花八门,User-Agent 看着像爬虫,但关键是这些请求的 Session ID 还都是内测用户的。再仔细一看,这些请求的 Referer 头全是陌生的外部域名(后来查是测试那边伪造的钓鱼页面)。最惨的是,有个用户的购物车数据被恶意请求清空了(因为批量导入接口顺手做了“清空旧地址”的逻辑)。我当时脸就红了,事后复盘,我们犯了两个致命错误:一是低估了伪造请求的风险,以为内测环境没人攻击;二是没搞清楚 Laravel 自带的 CSRF 保护在 AJAX 和跨域场景下会失灵。这时候大家自然会想到加验证码?限制 IP?但这些都治标不治本。CSRF 攻击的核心,是攻击者冒充用户身份发起恶意请求,而 Laravel 其实早就准备了应对的机制,只是咱没用好。

一、先说 CSRF 是啥?

在聊 Laravel 的防护之前,咱得先把 CSRF 这玩意弄清楚。用大白话讲,CSRF就是冒充熟人骗开门,假设你在银行网站登录后,浏览器存了你的登录态,这时候你点开一个恶意链接,这个链接会偷偷向银行的转账接口发请求(比如转钱给攻击者)。因为浏览器会自动带上你的 Cookie,银行服务器一看Cookie 是对的,就默认本人操作,但实际上这请求是你不知情的被伪造的。

Laravel 框架本身是自带 CSRF 防护的(从 Laravel 5 开始就内置了 VerifyCsrfToken中间件),因为 Laravel 的防护机制默认只对同源请求生效,一旦涉及 AJAX、跨域、SPA 单页应用,或者你手贱改了默认配置,这层防护就可能形同虚设。举个咱项目的例子:那个批量导入接口用的是 AJAX 请求,前端为了图省事,没在请求头里带 CSRF Token,Laravel 的VerifyCsrfToken中间件一看这请求没带 Token,直接放行了,这就等于给攻击者留了个后门。

二、Token 是怎么校验的?

先带大家捋捋 Laravel 的 CSRF 防护流程。

1. Token 生成

当用户登录 Laravel 应用时,框架会在 Session 里生成一个唯一的 CSRF Token(默认 40 位随机字符串),并存到 session()->token()里。这个 Token 相当于用户的身份证,每次请求都要出示。

2. Token 传递

Laravel 要求所有【非GET】请求必须携带这个 Token,传递方式有三种:表单隐藏域、Meta 标签、请求头。

3. Token 验证

Laravel 的 App\Http\Middleware\VerifyCsrfToken中间件是所有请求的必经之路。它干两件事:检查请求方法:如果是 GET/HEAD/OPTIONS,直接放行;非安全方法:从请求中提取 Token,和 Session 里的 Token 比对,一致才放行。

这套机制听起来挺完美,但总会翻车,因为咱的 AJAX 请求既没带表单隐藏域,也没在 Header 里传 Token,中间件直接睁一只眼闭一只眼放了行,这就是典型的没按规矩出牌。

三、实战踩坑:Laravel CSRF 防护的 4 个大坑(附老王事故复盘)

接下来结合自己的踩坑经历,给大家盘点 Laravel 项目中最容易翻车的 CSRF 场景。

坑 1:AJAX 请求忘了带 Token

就是开头的地址导入接口。前端用 Vue 的 axios 发 POST 请求,后端 VerifyCsrfToken没拦住,导致攻击者伪造请求清空用户数据。Laravel 不会“自动”给 AJAX 请求加 Token。除非你手动配置。后端虽然启用了 VerifyCsrfToken中间件,但 axios 没传 Token,中间件比对时发现请求里没 Token,按理说应该拒绝,但咱当时为了调试方便,手贱改了中间件的 $except 数组,把这个接口排除了……

解决方案:给 AJAX 请求加上 Token。分两种情况:

#传统 AJAX$.ajaxSetup({    headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') }}); # Vue/React(axios)import axios from 'axios';axios.defaults.headers.common['X-CSRF-TOKEN'] = document.querySelector('meta[name="csrf-token"]').getAttribute('content');

坑 2:跨域请求(CORS)导致 Token 验证失败

后来咱做了个 H5 活动页,前端部署在 CDN 域名(m.xxx.com),后端 API 在 api.xxx.com。用户点击活动页的领取优惠券按钮,前端用 axios 发 POST 请求到 api.xxx.com/coupon/receive,结果返回 419 。跨域请求时,浏览器会先发一个 OPTIONS 预检请求,检查服务器是否允许跨域。而 Laravel 的 VerifyCsrfToken中间件默认会对 OPTIONS 请求也做 Token 验证,但 OPTIONS 请求本身不会带 Token,自然就被拦了。

解决方案:配置 CORS 中间件,让 OPTIONS 请求跳过 CSRF 验证。Laravel 可以用 fruitcake/laravel-cors包,安装后修改 config/cors.php:

// config/cors.phpreturn [    'paths' => ['api/*''sanctum/csrf-cookie'], // 允许跨域的路径    'allowed_methods' => ['*'],    'allowed_origins' => ['https://m.xxx.com'], // 允许的域名    'allowed_headers' => ['*'],    'exposed_headers' => [],    'max_age' => 0,    'supports_credentials' => true// 允许带 Cookie];

时在 VerifyCsrfToken中间件的 $except数组里加上 OPTIONS 请求(或直接排除跨域接口):

// app/Http/Middleware/VerifyCsrfToken.phpprotected $except = [    'api/coupon/receive'// 排除跨域 API 接口    'sanctum/csrf-cookie'// Laravel Sanctum 的 CSRF Cookie 接口];

坑 3:Token 过期或 Session 失效

有用户反馈,登录后点击“提交订单”按钮,偶尔会返回 419 错误,刷新页面后又好了。查日志发现,这些请求的 Session ID 是新的(说明 Session 过期了),但前端还在用旧的 CSRF Token。Laravel 的 CSRF Token 默认和 Session 绑定,Session 过期后,Token 也会失效。如果用户长时间停留在页面(比如填表单填了半小时),这时候提交,Token 已经失效,自然验证失败。

解决方案:两种思路,一是延长 Session 有效期,修改 config/session.php的 'lifetime'值,比如设为 1440,但会增加安全风险;二是动态刷新 Token,前端在每次请求成功后,主动从响应头或 Meta 标签获取新 Token(Laravel 会在 Session 续期时自动更新 Token,可通过 csrf_token()函数获取)。

推荐第二种,用 JS 监听 Token 过期,自动刷新:

// 前端:每次请求后检查响应头是否有新 Token(Laravel 会在 Session 续期时返回新 Token)axios.interceptors.response.use(response => {    const newToken = response.headers['x-csrf-token'];    if (newToken) {        document.querySelector('meta[name="csrf-token"]').setAttribute('content', newToken);        axios.defaults.headers.common['X-CSRF-TOKEN'] = newToken;    }    return response;});

坑 4:SPA 单页应用首次 Token 获取

后来咱用 React 重构了后台管理系统,登录后进入首页,点击菜单加载数据时,频繁出现 419 错误。排查发现,React 组件在挂载时就发起了请求,但此时 Blade 模板里的 Meta 标签还没渲染(因为是异步加载),导致前端拿不到 Token。SPA 应用通常是先加载一个空白 HTML,再通过 JS 动态渲染内容,如果 CSRF Token 是写在 Blade 模板里的 Meta 标签,可能在 JS 执行时还没生成,前端自然拿不到 Token。

解决方案:用 Laravel Sanctum 的CSRF Cookie接口主动获取 Token。Sanctum 是 Laravel 的轻量级 API 认证包,它提供了一个 /sanctum/csrf-cookie接口,访问后会返回一个包含 CSRF Token 的 Cookie(名为 XSRF-TOKEN),前端可以从 Cookie 里读取 Token 并设置到 Header。

四、进阶防护:除了 Token,还能怎么加固 CSRF 防护?

CSRF Token 是 Laravel 防护的核心,但不是全部。再分享几个进阶技巧。

1. SameSite Cookie 属性:从源头减少“被伪造”的可能。

现代浏览器支持 Cookie 的 SameSite属性,它可以限制 Cookie 在跨站请求中的发送:

  • SameSite=Strict:完全禁止跨站发送 Cookie(最安全,但可能影响正常跨站功能);

  • SameSite=Lax:允许部分跨站请求(如 GET 导航)携带 Cookie,POST 等非安全方法禁止(推荐);

  • SameSite=None:允许跨站发送,但必须配合 Secure(HTTPS)(用于 SPA 跨域场景)。

Laravel 可以在 .env文件中设置:

SESSION_SECURE_COOKIE=true # 仅 HTTPS 传输 CookieSESSION_SAME_SITE=lax # 推荐设为 Lax

2. 二次验证:敏感操作再加一道锁

对于“转账”“修改密码”“批量删除”等高危操作,即使有 CSRF Token,也可以再加一层验证(比如短信验证码)。Laravel 可以用 laravel/fortify包集成双因素认证,或者在业务逻辑里手动校验:

// 敏感操作:先验证二次验证码public function batchImport(Request $request){    $user auth()->user();    if (!$user->verifyTwoFactorCode($request->input('2fa_code'))) {        return response()->json(['message' => '验证码错误'], 403);    }    // ... 后续逻辑}

3. 监控异常请求:用日志揪出攻击者

在项目里加了 CSRF 验证失败的日志记录,方便追踪攻击:

// 在 VerifyCsrfToken 中间件的 handle 方法中扩展public function handle($requestClosure $next){    if ($this->isReading($request) || $this->runningUnitTests() || $this->inExceptArray($request) || $this->tokensMatch($request)) {        return tap($next($request), function ($responseuse ($request{            // 记录成功的 CSRF 验证(可选)        });    }
    // 记录失败的 CSRF 验证(重点!)    Log::warning('CSRF 验证失败', [        'ip' => $request->ip(),        'url' => $request->fullUrl(),        'method' => $request->method(),        'user_agent' => $request->userAgent(),        'session_id' => $request->session()->getId(),    ]);
    throw new TokenMismatchException('CSRF Token 不匹配');}

五、CSRF 防护的核心就一句话 => [别让攻击者冒充用户]

折腾了这一大圈,从数据被改到压测翻车,再到一步步填坑,最大的感悟是:CSRF 防护的本质,是验证“请求确实来自用户的主动操作”。Laravel 已经给了我们一把好锁,但能不能防住贼,还得看咱会不会用。安全从来不是有了就行,细节到位才是王道。


阅读原文:原文链接


该文章在 2025/12/18 9:35:18 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2026 ClickSun All Rights Reserved