CSRF for Web Developer

写给网页开发者的 CSRF 理解与防范

什么是 CSRF

CSRF(Cross-Site Request Forgery)跨站请求伪造,意味着攻击者通过用户身份发送恶意请求。 具体来说该攻击利用浏览器请求默认会带上 Cookie 的特性,通过各种手段让浏览器发送用户不知情的请求来达成目的

举例来说用户登录过 A 网站并在浏览器 Cookie 留下了敏感信息,并且又浏览了某恶意网站,其中包含了恶意请求代码,当触发时就会让用户在设计好的恶意页面 B 对 A 页面发送恶意捏造的请求。

  • 可以是点击链接
<a href="http://bank.com/transfer.do?acct=MARIA&amount=100000">View my Pictures!</a>
  • 可以是加载隐藏的图片
<img src="http://bank.com/transfer.do?acct=MARIA&amount=100000" width="0" height="0" border="0" />
  • 可以是加载隐藏的自动提交表单
<body onload="document.forms[0].submit()">
<form action="http://bank.com/transfer.do" method="POST">
<input type="hidden" name="acct" value="MARIA" />
<input type="hidden" name="amount" value="100000" />
<input type="submit" value="View my pictures" />
</form>
</body>

大概念即是通过不同方式想办法伪造用户在其他网站的请求,关键字:「跨站」、「请求」、「伪造」。

防范 CSRF

要防范 CSRF 在用户的角度其实是比较难做到的,多数的防范措施都需要在服务器端进行,在客户端最多只能如下防范:

  1. 不让任何敏感信息储存于浏览器,每次使用完网站就登出。
  2. 避免进入来路不明的可疑网站。

防范 CSRF 最根本的方式就是「不要相信请求来源自本人」,势必要添加一些需要用户互动的验证机制或是只有用户本人才能知道的信息来确认请求的合法性。

CSRF Token

服务器可以针对不同用户生成随机的 CSRF Token 并且在接收请求时比对验收,因为 CSRF Token 并不存储于 Cookie,跨站的网页并没有办法获取因而能够确认用户的请求是否为本人。

举例表单提交前自动夹带服务器生成的 CSRF Token,提交时自动送出,只有当 CSRF Token 吻合才是合法的请求:

<form action="http://bank.com/transfer.do" method="POST">
<input type="hidden" name="csrf-token" value="Generate CSRF Token Here..." />
<button type="submit">Submit</button>
</form>

设置 Cookie 的 SameSite 属性,限制第三方网站对 Cookie 的访问,这样一来就无法通过第三方网站发送恶意伪造的请求。不过基于这种做法依赖浏览器支持,具体可以参考 samesite - caniuse🔗

SameSite Cookie 属性有三种模式,可以根据方便性与安全性进行取舍:

  • Strict :只有当浏览者是从同一个网站直接互动(例如点击链接、输入网址)时,才会送出 Cookie。
  • Lax(大多数浏览器默认值):允许「安全的跨站请求」(像是 GET 链接、表单 submit),但不允许非安全请求(如 POST、PUT、DELETE)携带 Cookie。
  • None: 允许任何跨站请求携带 Cookie。
Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Secure; SameSite=Lax

总结

无论如何都不轻易相信用户请求即能最大程度防范 CSRF。随着用户逐渐使用重视 Cookie 存取安全性的浏览器,可以期待 CSRF 可以被更好更简单的防范。

延伸阅读