跨站请求伪造CSRF原理及防范
跨站请求伪造(Cross Site Request Forgery),缩写为CSRF或者XSRF,它是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。防范CSRF是保障系统稳定性的其中一环,接下来,我们来了解一下CSRF的原理和防御手段。
CSRF流程
整个攻击流程如下:
- 用户登录目标网站,即将要被攻击的系统。
- 用户访问危险网站。
- 危险网站违背用户意志,向目标网站发起请求,造成用户数据变更。
注:如果目标网站允许用户提交数据,并在不处理的情况下直接将这些数据暴露给目标网站下的其他用户,那么就有可能出现用户访问目标网站,执行了其他恶意用户上传脚本,导致用户登陆cookie被盗。这种情况被称为跨站脚本攻击(XSS Cross Site Scripting)。
CSRF根因
C/S(客户端/服务端)模式是计算机软件协同工作的模式。即用户操作客户端程序,由客户端程序将用户的意图转为特定格式的请求,发送到服务端,由服务端解析请求,更新相关数据,完成用户意图。
B/S(浏览器/服务端)模式可以认为是C/S模式的一种变种,浏览器充当了其中的客户端。浏览器充当客户端提高了业务开发效率,一方面由浏览器屏蔽了各平台差异,公司不需要再开发、维护多平台客户端;另一方面由于浏览器拥有渲染标准(HTML/CSS)、脚本执行标准(Javascript),降低了客户端开发门槛。
B/S模式虽然提高了业务开发效率,但由于浏览器并不是只为一家公司业务服务,用户可以同时使用浏览器处理多个公司的多个业务。由此而言,浏览器应当隔离各公司各业务,避免业务间相互影响,但由于浏览器上的业务构建采用的是同一套标准,且业务是运行在同一个程序中,浏览器即使做了一些隔离,但也不能完全避免业务间影响,换句话说,这些业务间可能还需要相互连通。
回到CSRF,假设浏览器彻底隔绝了业务间的连通,那就没CSRF什么事了,因为CSRF指的就是跨站,既然都不能跨站了,那请求再伪造都没用了。但实际上大家都知道,一个网站是可以跳转另一个网站的,这也是互联网的特征之一。
接下来我们就要讨论一下浏览器为我们做了哪些隔离。浏览器在创立之初,实现cookie功能时,HTTP标准保障了cookie只能被同一个网站的脚本获取,在请求同一个域名时才会被携带发送给对应服务器,因此大部分公司业务使用cookie来标记用户是否已经登录。这对应本文第一节中讲到的流程,用户浏览危险网站时,危险网站无法获取目标系统的cookie,只能背地里直接调用目标网站链接或诱惑用户点击目标网站的链接,浏览器认为这个操作是用户触发的,便携带目标网站的cookie,将异常请求发送到了目标网站,最终结果就是违背用户意愿,操作了用户目标网站中的内容。
CSRF防范
CSRF防范手段有很多,接下来我对这些手段做简单介绍。
跨域策略
原理
CORS Policy
跨域原理如下,危险网站在进行xhr时,浏览器会额外新增一个header,key为Origin,值为当前网站URL,并且JavaScript无法篡改。据此,服务端可根据此信息判断请求是否来自其他网站。
下面的curl是我们模拟csrf时,发出的请求。
curl 'http://127.0.0.1:8090/param' \
-H 'Connection: keep-alive' \
-H 'Pragma: no-cache' \
-H 'Cache-Control: no-cache' \
-H 'sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="96", "Microsoft Edge";v="96"' \
-H 'Accept: application/json, text/javascript, */*; q=0.01' \
-H 'DNT: 1' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.93 Safari/537.36 Edg/96.0.1054.53' \
-H 'sec-ch-ua-platform: "macOS"' \
-H 'Origin: http://localhost:8080' \
-H 'Sec-Fetch-Site: cross-site' \
-H 'Sec-Fetch-Mode: cors' \
-H 'Sec-Fetch-Dest: empty' \
-H 'Referer: http://localhost:8080/' \
-H 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6' \
--compressed
如果服务端对跨域策略不做任何处理,该请求就会发送到服务端并进行业务处理,但浏览器在解析response头时,会发现header中没有携带key为Access-Control-Allow-Origin的数据,因此会丢弃响应的body,由此,发起请求的xhr就不会拿到响应数据。
注意:在这种情况下,服务端还是对请求进行了处理,只不过客户端的请求源拿不到响应数据而已。此外,如果服务端设置了Access-Control-Allow-Origin响应头,但值不是当前域名,则也不会解析数据。
弊端
如果服务端对Origin进行判断处理,则还可能面临一种情况,即获取不到Orgin头。例如,302跳转后,浏览器不会携带Origin头,此外像IE11等一些浏览器不会在CORS时添加Origin头。
Referer
原理
referer也是http请求头中的一个header,记录了该HTTP请求的来源地址,对于ajax请求、图片和script请求,referer为发起请求的页面地址,对应页面跳转,referer为打开页面历史记录的前一个页面地址。因此我们可以使用referer知晓请求来源域名。
下面的curl是百度网页在获取图片时,发送的请求。
curl 'https://dss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/topnav/newxueshuicon-a5314d5c83.png' \
-H 'authority: dss0.bdstatic.com' \
-H 'pragma: no-cache' \
-H 'cache-control: no-cache' \
-H 'sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="99", "Google Chrome";v="99"' \
-H 'dnt: 1' \
-H 'sec-ch-ua-mobile: ?0' \
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.83 Safari/537.36' \
-H 'sec-ch-ua-platform: "macOS"' \
-H 'accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8' \
-H 'sec-fetch-site: cross-site' \
-H 'sec-fetch-mode: no-cors' \
-H 'sec-fetch-dest: image' \
-H 'referer: https://www.baidu.com/' \
-H 'accept-language: zh,en-US;q=0.9,en;q=0.8,zh-CN;q=0.7' \
--compressed
弊端
referer有一个策略标准:Referrer Policy,当前页面对应的服务端可以通过response的header设置策略为no-Referrer或origin,此后再有任何请求,就都不会携带referer了。此外IE6/7浏览器在使用window.location.href=url跳转时,会丢失referer;IE6/7浏览器在使用windown.open时会丢失referer;https跳转http时,会丢失referer;flash跳转referer也会有问题。
双重Cookie验证
原理
我们知道危险网站无法获取目标网站的cookie,因此可以要求目标网站的前端请求必须将cookie中的某一个key对应的值放置在请求头或parameter中,这样就可以通过验证cookie和请求头/parameter中的值是否一致来判断是否是伪造请求了。
弊端
假设使用的cookie种在www.baidu.com下,api.baidu.com就无法获取获取www.baidu.com的cookie,从而无法进行双重cookie验证,因此需要将cookie种在baidu.com下。但与此同时如果xxx.baidu.com域名被XSS攻击,攻击者得到baidu.com下的cookie,由此就能绕过所有子域名双重验证。
SameSite
原理
SameSite是Google提出的http新标准,即在cookie上增加属性Samesite。
当值为Strict时,任何从其他域名跳转的请求、跨域的请求,浏览器在判断来自其他域名时,就不会携带这个cookie。
当值为Lax时,稍微宽松一些,在跨域的异步请求和通过表单post提交进行页面跳转时,才不会发送此cookie。
弊端
子域也会受到限制,另外也是只有部分浏览器支持。
CSRF Token
原理
由于不同浏览器对http标准支持力度不一致,且在部分场景下,无法有效判断请求来源,因此利用http标准防范CSRF总会有考虑不到的地方,因此可以在业务上做一些安全校验。
例如,在用户打开页面时,服务器给这个用户生成一个token,这个token服务端存放在session中,客户端可以存放在localStorage或运行时,切记不能放到cookie中。在用户进行重要请求时,由客户端在发起请求前,将token放置在header或parameter中,服务端在拿到请求时,会先查看请求中携带的token是否与session中的一致。
由于CSRF只能携带目标网站的cookie,拿不到会话中的token,从而实现CSRF防范。token是否加密变形不影响安全性,只要能保证危险网站拿不到token即可,需注意XSS可以拿到token。
弊端
大型网站的服务器不止一台,session一般存放于独立的kv存储中,因此会带来额外的IO开销。但可使用Encrypted Token Pattern减少IO,具体原理是服务端根据用户id和随机数生成加密token,将token和随机数均发送给客户端。然后服务端在解析客户端请求时,拿着请求中的随机数和服务端的用户id,再次进行加密计算,然后校验算得的数据和请求中的token是否一致。
强制二次交互
可以使用附加验证码的方式强制和用户进行交互,缺点是步骤比较繁琐,适用于银行类高保系统。
结论
本文介绍了CSRF出现的原因,防范的策略。其中前四个策略依赖浏览器对HTTP协议的实现,因此存在不准确性,笔者推荐使用CSRF Token方式,它的实现方式比较简单。
参考
美团文章:https://tech.meituan.com/2018/10/11/fe-security-csrf.html
跨站攻击以前很流行,这个还是有必要学习下