12

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

 3 years ago
source link: https://www.fly63.com/article/detial/11355
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

跨站请求伪造(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就不会拿到响应数据。

625cc2e128b95.jpg

注意:在这种情况下,服务端还是对请求进行了处理,只不过客户端的请求源拿不到响应数据而已。此外,如果服务端设置了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://www.fly63.com/article/detial/11355


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK