使用脚本隔离技术对抗XSS攻击,在圣诞节之后,我与@mikewest和@fgrx讨论了关于使用“隔离”的概念(将会在下面解释)作为对抗XSS漏洞攻击的安全缓解措施。这看起来似乎是一个很有趣的问题,因此,我花了一些时间研究了一下。
下面描述的设计方案将允许你(web开发人员)通过两个简单的步骤保护一些客户端免受XSS攻击:
设置新的cookie标志(isolatedScript = true)设置一个HTTP标头(Isolated-Script:true)
就这样。有了这两个东西,你就可以保护你的应用程序防御XSS漏洞攻击。它有点类似于Suborigins,但是使用的是“隔离脚本”的思路防御XSS攻击,即使js文件是在同一个页面内!
除此之外,它还有另外一个优点,它也能够缓解第三方javascript代码(如广告,分析,etcetera)。
设计
隔离脚本包括以下三个要素,它们一起工作以提供隔离脚本的防御能力。
(译者注:为了让读者更易理解本文所提供的思路且保证原文纯正的feel,所以译者对这三个要素名词不作翻译)
Isolated WorldsIsolated cookiesSecret HTML
我会在下面详细描述每一个要素,并给你一些演示。
Isolated Worlds是当前在Chrome扩展的用户脚本和Firefox中的Greasemonkey脚本中最突出使用的一个概念 —— 本质上,它允许JavaScript代码通过文档的DOM获得“view(视图)”,但是是在一个被隔离的JavaScript运行时环境中。 让我详细的来解释一下:
假设在一个网站上有如下代码:
Isolated World将有权访问document.getElementById(” text”),但它不能够访问window.variable这个变量。 也就是说,两个脚本共享的唯一的东西就是HTML的DOM的独立视图。 这种隔离非常重要,因为用户脚本可能会提升权限,例如,他们可以向任何网站发起XMLHttpRequests请求并读取其响应内容。
如果不使用Isolated World进行隔离,那么页面就可以在用户的脚本中执行代码,并最终攻击用户:
document.getElementById = function() { arguments.callee.caller.constructor("attack()")(); };
通过这样的方式,Isolated World就允许我们从攻击者执行的上下文中保护我们的特权执行上下文。那么,问题来了:我们能否可以使用相同的设计来保护受信任的脚本防止XSS漏洞吗?
也就是说,我们与其专注于阻止脚本执行作为一种缓解措施(我们发现这种措施容易出问题)还不如将关注点放在将可信脚本与不可信脚本隔离开来。
在同一个DOM中运行具有特权和非特权脚本的想法并不新鲜,事实上,已经有一些这方面的具体实现(如Mario Heiderich的Iceshield和Antoine Delignat-Lavaud的Defensive JS),但是它们的实现都需要将代码重写。使用Isolated World,就没有那么麻烦。
因此,隔离脚本的思路就是—— 为Web开发人员提供一种机制,将特定脚本标记为受信任的,然后由浏览器将它们加载后在Isolated World中运行。
要在demo中实现这一点的一个简单方法就是通过在Chrome扩展中重用Isolated Worlds实现,通过在页面中插入一个用户脚本,为每个脚本设置正确的HTTP响应头。
(译者注:被隔离的脚本就是可信任的脚本,而不是恶意脚本文件。)
Isolated Cookies
现在我们有一个脚本运行在受信任的执行上下文中,现在我们需要一种方法能够让Web服务器识别来自这个脚本的请求。这个做法很有必要,因为服务器可能只想将一些敏感数据暴露给被隔离的脚本。
最简单的方法是添加一个类似于Origin头的新的HTTP请求头(我们可以使用Isolated-Script:foo.js)。另一种方法是创建一种新的cookie类型,它只在请求来自于一个被隔离的脚本时才会发送给服务器。此替代方法优于HTTP请求头有两个原因,如下:
向后兼容,没有实现这种方法的浏览器将只是像往常一样发送cookie。它可以与同一网站的Cookie(同时也能防御CSRF)结合使用。
Web服务器需要设置Cookie信息:
Set-Cookie: SID=XXXX; httpOnly; secure; SameSite=Strict; isolatedScript
然后,浏览器会像往常一样处理cookie,只有当请求是由被隔离的脚本发出的才会在请求信息中包含该Cookie,另外,如果浏览器不能识别这个新的标志,将会在所有的请求信息中包含该Cookie。
要在demo中实现这一点的一个简单方法是,在cookie中使用特殊的名称,并且除非请求来自于被隔离的脚本否则拒绝发送cookie。
Devdatta提出了一个想法是使用cookie前缀,这同样也可以保护cookie免受窜改攻击。
Secret HTML
我们现在的这种针对脚本隔离的机制依旧让脚本有一些额外的特权,并让这些脚本在隔离的执行上下文中运行来阻止恶意代码执行,然而,该脚本如果将想要显示给用户的数据写入到 DOM后,那么恶意的脚本也能够立即读取到这些内容。所以,为此,我们需要为将被隔离的脚本读写的HTML 内容进行保护从而让恶意的脚本不能够读取到。
虽然此原语你可能从未听过,但它实际上在今天已经存在了!JavaScript代码本身已经可以编写用户可见的HTML内容,但如何让这些HTML内容对DOM不可用?有至少两种方法可以做到这一点