跨站请求伪造CSRF原理及防范

更新日期: 2022-04-16阅读: 1.3k标签: csrf

跨站请求伪造(Cross Site Request Forgery),缩写为CSRF或者XSRF,它是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。防范CSRF是保障系统稳定性的其中一环,接下来,我们来了解一下CSRF的原理和防御手段。

CSRF流程

整个攻击流程如下:

  1. 用户登录目标网站,即将要被攻击的系统。
  2. 用户访问危险网站。
  3. 危险网站违背用户意志,向目标网站发起请求,造成用户数据变更。

注:如果目标网站允许用户提交数据,并在不处理的情况下直接将这些数据暴露给目标网站下的其他用户,那么就有可能出现用户访问目标网站,执行了其他恶意用户上传脚本,导致用户登陆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

原文 :  https://blog.gavinzh.com/2022/04/16/CSRF-Principle-And-Defense/


链接: https://fly63.com/article/detial/11355

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!