浏览器 Web 安全基础系列

  1. 浏览器怎么判断是不是同一个网站

  2. Cookie 为什么会自动携带

  3. Session 登录状态为什么可能被劫持

  4. HTTPS 为什么仍然不够

  5. CORS 到底在限制什么

  6. CSRF 是怎么冒用登录状态的(当前篇)

  7. CSRF 如何防御

  8. XSS 为什么能在你的网站执行

  9. CSP 与浏览器安全响应头

  10. Clickjacking 点击劫持

  11. 第三方资源为什么危险

  12. 防盗链到底防什么

CSRF 的关键不是“攻击者偷到了 Cookie”,而是:攻击者让受害者的浏览器替他发送请求,浏览器又自动附带了目标网站的登录 Cookie。

CSRF 攻击流程

攻击前提

假设用户已经登录银行:

https://bank.example

浏览器保存:

Cookie: sid=abc123

银行有一个只验证登录状态的接口:

app.post('/transfer', requireLogin, (req, res) => {
  transfer(req.session.userId, req.body.to, req.body.amount);
  res.send('ok');
});

攻击者知道接口地址和参数格式,在自己的网站放置:

<form action="https://bank.example/transfer" method="post">
  <input type="hidden" name="to" value="attacker">
  <input type="hidden" name="amount" value="1000">
</form>
<script>document.forms[0].submit()</script>

用户打开恶意页面后,浏览器向银行提交表单。只要 Cookie 的 SameSite 等规则允许,浏览器就会自动带上银行 Cookie。

银行看到有效 Session,只知道“这个浏览器登录过”,却不知道请求是由恶意页面触发的,于是执行转账。

攻击者通常看不到响应

同源策略和 CORS 通常阻止恶意页面 JavaScript 读取银行响应。但状态修改攻击并不需要读取结果:

删除成功即可
转账成功即可
修改邮箱成功即可

这也是“CORS 报错为什么仍可能造成副作用”的典型场景。

CORS 与 CSRF 的边界

CSRF 常攻击哪些接口

  • 修改邮箱或手机号;

  • 绑定第三方账号;

  • 修改收货地址;

  • 发布或删除内容;

  • 给用户授权;

  • 发起转账或下单;

  • 修改安全设置。

攻击者需要知道接口结构,但不需要知道用户 Cookie 的值。

为什么 GET 不能修改数据

如果删除接口设计成:

GET /delete-account

攻击者只需一张图片:

<img src="https://target.example/delete-account">

浏览器加载图片时就可能触发删除。GET 应当保持安全、幂等语义,不用于修改服务器状态。

但把 GET 改成 POST 仍不等于防住 CSRF,因为 HTML 表单可以提交 POST。

为什么 HttpOnly 不防 CSRF

HttpOnly 只阻止 JavaScript 读取 Cookie:

document.cookie // 看不到 sid

CSRF 根本不需要读取 sid;浏览器会在请求时自动携带。因此 HttpOnly 很重要,但它解决的是 Cookie 被脚本直接窃取的问题。

为什么 Authorization 请求头风险不同

如果 Token 保存在内存中,并由前端主动写入:

fetch('/api/transfer', {
  method: 'POST',
  headers: { Authorization: `Bearer ${token}` }
});

攻击者的普通表单无法自动添加这个请求头,传统 CSRF 风险明显下降。但 XSS 一旦能读取或使用 Token,仍然危险;Token 存储、刷新和撤销也有其他权衡。

本地实验观察点

完整实验位于:

examples/06-csrf-attack/

它启动:

  • 受害网站 http://localhost:3060

  • 恶意网站 http://localhost:4060

实验步骤:

  1. 在受害网站登录,余额为 10000。

  2. 打开恶意网站并点击“领取奖励”。

  3. 恶意页面自动向受害网站提交转账表单。

  4. 回到受害网站,余额减少。

  5. 在开发者工具 Network 中查看请求的 OriginCookie 和 Form Data。

同一主机不同端口是跨源但同站,因此实验能帮助观察:CORS 和 SameSite 判断的边界并不相同。

CSRF 成立条件

可以用四个问题快速判断:

  1. 用户是否已经在目标网站建立登录状态?

  2. 身份凭证是否会被浏览器自动附带?

  3. 攻击者能否构造目标接口接受的请求格式?

  4. 服务端是否只验证登录,而没有验证请求来源或随机 Token?

条件越齐全,风险越高。

常见误区

不需要。攻击者依赖浏览器自动携带。

POST 天生安全

不对。普通 HTML 表单可以跨源 POST。

JSON 接口一定没有 CSRF

如果接口只接受带自定义头的 JSON,普通表单难以构造,但仍应检查 CORS、内容类型处理、同站攻击和认证方式,不能只靠“目前表单发不了 JSON”。

小结

CSRF 是“借身份执行”,不是“偷身份凭证”。浏览器自动携带认证 Cookie,服务端又无法仅凭 Cookie 判断操作意图,因此需要 SameSite、CSRF Token、Origin/Referer 校验等多层防护。

参考资料