想起来刚毕业时,腾讯面试官问我:“你了解 CSRF 吗?如何应对 CSRF 攻击?” 当时的我了解得实在不够深入,支支吾吾说不清楚。虽然事后专门去学习了,但发现这个知识点就像鱼一样,只有 7 秒钟的记忆,过段时间就忘了。
为了彻底跟它“分手”,我决定写下这篇超详细的总结,希望能帮到和我一样有过困惑的朋友。
🎭 到底什么是 CSRF?
CSRF,全称是 Cross-Site Request Forgery,中文译为 跨站请求伪造。
听起来很专业,但说白了就是:攻击者想办法诱导你(一个已经登录某网站的用户),去点击一个恶意链接或访问一个恶意网站。然后,这个恶意网站会利用你“已登录”的身份,偷偷地向你登录过的网站发送一个伪造的请求,替你执行一些你完全不知道的操作,比如改密码、发帖子、甚至转账。
一句话总结:攻击者盗用了你的身份,以你的名义发送恶意请求。
🔬 攻击是如何得逞的?深挖原理
CSRF 能得逞,核心是利用了浏览器的一个“小动作”:
当你的浏览器向某个域名(比如
bank.com)发送请求时,它会自动把你在这个域名下的所有 Cookie 都带上,不管这个请求最开始是哪个网站(比如evil.com)发起的。
攻击者正是利用这一点,构造了五花八门的攻击手段。
1. GET 型攻击:藏在各种标签里的“刺客”
任何能让浏览器自动发 GET 请求的标签,都可能成为帮凶:
<img>标签:一张看不见的图片就能删掉你的账号。html<img src="[https://bank.com/api/deleteUser?id=123](https://bank.com/api/deleteUser?id=123)" style="display:none;" /><link>和<script>标签:加载一个 CSS 或 JS 文件的同时,你的邮箱可能就被改了。html<link rel="stylesheet" href="[https://vulnerable-site.com/api/changeEmail?newEmail=attacker@evil.com](https://vulnerable-site.com/api/changeEmail?newEmail=attacker@evil.com)">- CSS
background-image:甚至一行 CSS 都能让你的购物车被塞满。cssbody { background-image: url('[https://e-commerce.com/api/addToCart?itemId=999&quantity=100](https://e-commerce.com/api/addToCart?itemId=999&quantity=100)'); }
⚠️ 重要原则:千万别用 GET 请求去执行任何涉及数据修改的操作!
2. POST 型攻击:精心伪装的“特洛伊木马”
如果接口只认 POST 请求怎么办?攻击者会造一个隐藏的、能自动提交的表单。
html
3. 针对 API 的高级攻击 (PUT/DELETE)
现代应用中,PUT / DELETE 请求同样面临风险。恶意网站可以通过 Fetch API 发送这些请求,只要你的认证方式还是依赖 Cookie,浏览器照样会“傻傻地”带上它,完成攻击。
🛡️ 如何彻底防范 CSRF 攻击?
聊完了原理,我们来看看有哪些终极解决方案。
✅ 方法一:王道正统 —— Anti-CSRF Token
这是最经典、最核心的防御方案,面试官最想听到的答案。
-
服务器发令牌:用户访问页面时,服务器为该用户的会话生成一个唯一的、不可预测的随机字符串(Token)。
-
前端藏令牌:服务器把这个 Token 藏在页面的表单里。
-
提交时带令牌:用户提交表单时,Token 会一起被发送给服务器。
-
服务器验令牌:服务器检查收到的 Token 和自己之前发的是不是同一个。
因为黑客的恶意网站上拿不到这个随机生成的 Token,所以他们伪造的请求注定是“裸奔”的,一验便知。
✅ 方法二:现代架构的“天然免疫” —— Token + LocalStorage
“为什么现在感觉 CSRF 攻击变少了?” 很大程度上要归功于现代前后端分离的架构。
现在主流的做法是:
-
登录后,后端返回一个 Token (比如 JWT)。
-
前端把它存在
localStorage里。 -
之后每次发请求,都手动从
localStorage取出 Token,放在Authorization请求头里。
这种方式天然免疫 CSRF,因为:
-
localStorage受同源策略严格限制,恶意网站的脚本绝对无法读取到存储在其他域名下的 Token。 -
Authorization请求头是需要 JS 代码手动添加的,浏览器不会像 Cookie 一样自动附加它。
黑客既偷不到 Token,也无法命令浏览器发送,攻击自然就失效了。
✅ 方法三:浏览器层面的防火墙 —— SameSite Cookie
如果你的项目依然使用 Cookie 来管理会话,那 SameSite 属性就是你的救星。它能告诉浏览器,啥时候该带 Cookie,啥时候不该带。
-
Lax(宽松):目前主流浏览器的默认选项。它能阻止第三方网站通过POST请求、<img>等方式带上你的 Cookie,但允许通过链接跳转等导航操作带上。这一个设置就能防住绝大多数 CSRF 攻击! -
Strict(严格):最严格,任何跨站请求都不带 Cookie。安全但可能影响体验。
只需在后端设置 Cookie 时加一个属性即可:
plan
Set-Cookie: session_id=...; SameSite=Lax; HttpOnly; Secure
✅ 方法四:锦上添花的辅助 —— 验证 Referer
这是一种辅助手段,通过检查请求头里的 Referer 或 Origin 字段来判断请求是从哪发起的。如果是来自我们自己网站的域名,就放行;如果是来自不明域名,就拦截。
但因为它可能被篡改或禁用,所以不能作为主力防御,只能是“锦上添花”。
📋 懒人包总结
| 防御方法 | 优点 | 缺点/注意点 |
|---|---|---|
| Anti-CSRF Token | 安全性极高,业界公认的最佳实践 | 实现相对复杂,需要前后端配合 |
| Token & LocalStorage | 天然免疫 CSRF,现代架构首选 | 需要重点防范 XSS 攻击,否则 Token 会被偷 |
| SameSite Cookie | 实现超简单,浏览器原生支持,效果好 | 需注意旧浏览器的兼容性 |
| 验证 Referer/Origin | 实现简单,可作为辅助防御 | 不完全可靠,容易被绕过 |
写在最后
从当初面试时的懵懂,到现在能把整个攻防原理想清楚,这个过程让我明白,理解一个技术的“Why”(为什么会发生)远比死记硬背“How”(如何解决)要重要得多。
希望这篇文章也能帮你彻底拿下 CSRF!
大家在面试或工作中还遇到过哪些有趣的攻防问题?欢迎在评论区一起交流!👇


