什么是 CSRF
CSRF(“跨站请求伪造”):攻击者通过邮件、社区发帖等方式诱导受害者进入第三方网站,并且在第三方网站中向被攻击网站发送跨站请求;这主要是利用了受害者在被攻击网站已经获取的登录凭证(cookie),且 cookie 会自动附在对特定域名的请求里的浏览器特性,达到冒充用户对被攻击的网站执行某项操作(如指定受害者的邮箱账户自动将所有收到的邮件都转发到攻击者的邮箱里)的目的。
几种常见的攻击类型
GET 类型的 CSRF 攻击
GET 类型的 CSRF 攻击非常容易实现,在<img>
的 url 里构造恶意 URL 即可,用户在任何页面上只要加载了这个<img>
,都相当于向该恶意 URL 发出了一次 GET 请求。
POST 类型的 CSRF 攻击
这种类型的CSRF利用起来通常使用的是一个自动提交的表单:
<form action="http://bank.example/withdraw" method=POST>
<input type="hidden" name="account" value="xiaoming" />
<input type="hidden" name="amount" value="10000" />
<input type="hidden" name="for" value="hacker" />
</form>
<script> document.forms[0].submit(); </script>
超链接类型的 CSRF 攻击
跟上述两种 CSRF 攻击,超链接类型的 CSRF 攻击需要诱导用户主动点击,所以需要一定的欺骗手段,用户点击超链接后会跳转到攻击者构建的一个恶意 URL 里,也相当于是执行了一次 GET 请求。
CSRF的特点
- CSRF(通常)发生在第三方域名,但如果被攻击网站管控用户内容不严格的话,也有可能被发布恶意超链接等,这样防护起来就更困难了,因为 referer 是来自于本身。
- 攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;而不是直接窃取或修改受害者的隐私数据。
防护策略
针对这两点,我们可以专门制定防护策略,如下:
-
阻止不明外域的访问
- 同源检测
- Samesite Cookie
-
提交时要求附加本域才能获取的信息
- CSRF Token / 双重 cookie 验证
- 在 LocalStorage 里存放登录凭证
同源检测
浏览器在发送请求的时候,会带上origin
和referer
这两个 HTTP 头部字段,一般情况下,我们可以在收到请求的时候校验这两个字段是否来自可信的域名。
但是,虽然浏览器不允许修改origin
和referer
,但是有可能由于浏览器本身的机制,或者是攻击者刻意隐藏,导致请求里并没有这两个字段的信息,对于这种情况,可以考虑拦截掉。
Samesite Cookie
cookie 有个SameSite
属性可以防 CSRF 攻击,使用SameSite=Lax
后,从别的网站提交 POST 表单或发送 ajax 请求时都不会附上 cookie ,这样就从根本上解决 CSRF 攻击。但这个SameSite
属性只在现代浏览器上生效, IE 上是不支持的。
CSRF Token
除了传统的 cookie 以外,我们可以添加一个额外的 token 作为用户凭证。
token 的生成
- 全局共享:每次登录的时候生成一个随机数写到 cookie 里;以后每次请求接口的时候,前端就从 cookie 中取出这个随机数作为请求参数;后端校验的时候只需要比对接口参数和 cookie 里的值就可以了,也不需要存 session 了(分布式 session 成本较高)。这种方法被称为“双重 cookie”。
- 每个接口/表单独立生成:针对每个接口/表单都生成一个独立的 token ,“验证码”就属于这种方案。在这种方案下,生成和储存 token 的成本都比较高,建议只用于敏感接口。
双重 cookie 验证
所谓的双重 cookie ,指的是在请求接口时,除了常规带上 cookie 中的用户凭证信息,如 session_id 外,还把 cookie 中的用户凭证信息读出来附在接口请求参数里;这种方案对比起 CSRF Token 方案来说,好在不需要生成额外的 token ,也同样能够起到防御 CSRF 攻击的效果。
在 LocalStorage 里存放登录凭证
CSRF 的关键是:cookie 是自动附在请求里的,那如果登录凭证不是放在 cookie 而是 LocalStorage 里的话,比如使用 jwt 方案,那就从根本上破解了 CSRF 攻击了,不过这样的话,就需要防止 XSS 攻击了。
避免自己的网站被利用
除了需要避免自己的网站被攻击外,我们还需要避免自己的网站被利用来攻击其它网站:
- 严格限制用户上传的内容,如禁止上传 HTML 文件
- 不允许用户直接引用外链的图片
- 对于用户发布的超链接,需要对浏览者提醒这是属于外链的超链接
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。