一、CSRF原理
用户在A网站登陆后,浏览器保存其cookie。网站B使用某种方式请求A网站的接口时,带上了A网站的cookie,成功完成请求。
二、一个疑问
同源策略不能阻止CSRF的发生吗?
看同源策略的一些约束。(来自MDN:developer.mozilla.org/zh-CN/docs/…):
- 通常允许跨域写操作(Cross-origin writes)
- 链接
- 重定向
- 表单提交。特定少数的HTTP请求需要添加 preflight。
- 通常允许跨域资源嵌入(Cross-origin embedding)。
- script
- link
- img
- iframe
<video>、<object>、<embed>、<applet>
- 通常不允许跨域读操作(Cross-origin reads)。但常可以通过内嵌资源来巧妙的进行读取访问
- ajax跨域请求得到的结果将被拦截,不允许读操作
- canvas 操作图片(e.g. 读取嵌入图片的高度和宽度)的时候会有跨域问题
同源策略通常不阻止请求发送,只拦截请求结果;并且,同源策略允许跨域资源的写操作和嵌入,举几个例子:
1、img标签发起
<img src="http://bank.example/withdraw?amount=10000&for=hacker" />
2、form提交
<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>
3、link
<a href="http://test.com/csrf/withdraw.php?amount=1000&for=hacker" taget="_blank">
重磅消息!!
<a/>
上面这几个例子不报跨域是因为请求由 浏览器 控制,无法用 js 直接操作获得的结果,因此浏览器认为是安全的,不会报跨域的错。
来自知乎的总结(www.zhihu.com/question/31…
四、防护策略
1、浏览器
-
Samesite cookie
-
用法:
-
strict值
在任何跨域请求情况下,Cookie都不会被携带
-
lax值
从外部引用的链接跳转到本站,可以保持用户的登录状态;但是对于有CSRF倾向的post请求,将不会发送cookie
导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求,GET 表单。详见下表(表自 www.ruanyifeng.com/blog/2019/0…)。
Set-Cookie: xxx=1; Samesite=Strict
-
-
劣势:
兼容性问题。
-
-
同源判断
可靠性:这些头部不能被程序代码修改(比如使用XSS中的javascript),只有浏览器有权设置他们。
-
Origin
从HTTPS来的HTTP请求(即:https的网站协议降级访问http的链接),将会保留Origin头部。
- 缺点:
- 少部分情况下,Origin头或Referrer头可能不存在(由于某些合法的原因,如保护用户隐私,或者浏览器的问题)
- 向可信域发送CORS跨域请求,IE11不会添加Origin头部。
- 重定向302跨域,Origin将不会被包含在重定向的请求头部中,因为它们被认为是敏感信息不能被发送到其他域。
- 某些涉及隐私的场景下,Origin被设置成“null”。
- 对于同源请求,Origin头部一般都会带上。但是在大多数情况下,只会被POST/DELETE/PUT这些请求方法带上。在GET请求中,做了一些修改状态的操作,那么这个方法将起不到原作用。 (详见我翻译的https://juejin.cn/post/6844904053994979335)
- 缺点:
-
Referer
Origin头部不存在的时候,考虑使用Referer 在以下两种情况下,Referer 不会被发送:
来源页面采用的协议为表示本地文件的 "file" 或者 "data" URI
当前请求页面采用的是非安全协议,而来源页面采用的是安全协议(HTTPS)。参考以下表格控制Referer头部的发送策略
-
2、token防御:提交请求时,要求带上本域才能获取的信息
-
CSRF token
-
用法:
STEP1:
服务端生成token,给到客户端(同步token需要存放于Session之中,然后在每次请求时把token从Session中拿出,下文详解)STEP2:
提交请求的时候,客户端将token放在隐藏的form input中,或者放在请求头、请求parameter中。STEP3:
服务端负责校验token。从session中取出token,与请求中的Token进行比对;或者使用加解密算法的原理作比较 -
同步token
- token的构造: 生成超大随机数,并存在session里
- token的校验: 服务器比对token中的随机数
- 缺点: 增加了服务端的存储压力;分布式场景下需将token存在公共数据库redis之类的
-
基于加密算法的
- token构造: 使用密钥key,将sessionId和时间戳通过加密算法加密,得到csrf token
- token的校验: 将请求提交上来的token,使用密钥key解密,得到sessionId和时间戳。判断sessionId是否和当前请求用户的sessionId一致;比较时间戳和当前时间,用于判断token是否过期。
- 优点: 不使用随机数,则不需要存储到数据库,服务器只需要使用密钥,解密token,进行sessionId的比对即可。
- 缺点: 增加了服务器的计算量
-
-
双重提交cookie (double submit cookie)
cookie里的某些信息作为token
- 原理:CSRF无法读取cookie的信息,只能冒用
- 步骤:
STEP1:把一个token写入cookie
STEP2:发起请求的时候,从cookie种获取该token,在query、body或者header里带上这个token
STEP3:服务器验证Cookie中的字段与URL参数中的字段是否一致,不一致则拒绝。能获取到cookie中的token,并且token值正确的请求,则可以判断是正常的本域请求。CSRF的请求无法做到这一点。
- 优点:
1)不使用session
2)token存在客户端,减小服务器端的存储压力
3)不需要逐个接口和页面加校验逻辑,统一拦截 - 缺点:
1)难以做到子域名隔离如果在子域名种下cookie,则在主域名中无法读取子域名的cookie;2)为了确保Cookie传输安全,需要确保整站使用HTTPS
如果在主域名种下cookie,在子域名中可以读取,但是又可以修改主域名的cookie;
子域名发生XSS攻击,攻击者修改主域的cookie之后,可在主域发起CSRF攻击;
【参考文章】