IE 8 XSS 过滤器架构与实现
最近我们宣布了Internet Explorer 8 XSS过滤器,并简要讨论了其设计理念。本文将更详细地描述该过滤器的架构和实现。
设计目标
Internet Explorer 8 XSS过滤器旨在缓解反射型/“Type-1”XSS漏洞,同时不“破坏网络”。我们的基线方法需要满足以下三个条件:
-
XSS过滤器必须兼容:对良性内容/数据的干扰应最小,理想情况下为零。虽然通过从输入中删除所有非字母数字字符可能实现有效过滤,但这将是不切实际和过于苛刻的解决方案。任何涉及直接修改请求URL的解决方案都可能在服务器端持久化损坏的数据。同样,询问用户无法回答的问题或阻止整个页面的方法也是不可接受的。
-
XSS过滤器必须安全:通常,不能通过修改攻击来颠覆过滤器,否则这些攻击会被有意阻止。尽管XSS过滤器不能缓解所有可能的XSS攻击,但它可以在一些关键战役中取得决定性胜利。我们可以尽可能推动以最大化XSS过滤器的有效性,同时也要注意不损害兼容性或性能。
-
XSS过滤器必须高性能:用户更喜欢快速的浏览器,即使较慢的浏览器“更安全”。因此,出于性能原因,某些方法根本不可接受。例如,为每次导航创建浏览器渲染引擎的额外实例将影响太大而无法考虑。
在实现过滤器时,我们做出了最佳满足上述目标的决策。
实际考虑
XSS过滤器必须能够观察和拦截从浏览器到Web服务器的请求和响应。在Internet Explorer中,这可以通过MIME过滤器实现。XSS过滤器的原型实现实际上就是作为MIME过滤器实现的,但出于性能考虑,在构建Internet Explorer 8时,它被移入了MSHTML(浏览器渲染引擎)。
架构/实现
图A:XSS过滤器托管在Internet Explorer 8中
图B:XSS过滤器逻辑流
图A和图B描绘了XSS过滤器的高层视图。让我们深入细节。
出于性能考虑,XSS过滤器仅对浏览器内可能导致脚本执行的导航生效。XSS过滤器无需对资源(如图像,只要它们在Internet Explorer中真正呈现为图像)进行操作。
过滤器还检查浏览器内导航的源和目标URL。如果导航是跨站点的,或者无法确定源(例如:用户点击了收藏夹),则导航将被过滤。
XSS过滤器可以按区域启用/禁用。对于Beta 2版本,XSS过滤器将在Internet和受限站点区域启用,但不在本地Intranet区域启用。管理员可以通过组策略选择为任何区域启用或禁用XSS过滤器。
核心XSS过滤器引擎分两个阶段操作:
- 扫描HTTP GET/POST数据以匹配一组识别XSS攻击向量的启发式规则。匹配用于构建签名,以识别在HTTP响应中重放的标记/脚本。
- 生成的签名用于扫描HTTP响应。由签名识别的标记/脚本被中性化以阻止执行。
验证XSS攻击是否实际重放到响应中,可以最大化XSS检测的可靠性——“反射型XSS”需要反射。能够识别和中性化重放的标记/脚本,使过滤器避免过于苛刻的缓解措施,如询问用户、修改传出请求或阻止整个页面。
我们的方法在性能上是高效的,因为唯一显著的“繁重工作”是扫描HTTP响应体,这仅在生成签名的情况下发生。签名生成高度指示实际的XSS攻击,在日常浏览中很少见。
正则表达式乐趣 – 第1部分:启发式规则
如果导航满足过滤条件,过滤器将获取URL以及与该请求关联的任何POST数据,根据需要解码,并使用正则表达式识别XSS攻击向量。这些不区分大小写的模式是过滤启发式规则。以下是一个示例:
{<sc{r}ipt.*src*=}
此启发式规则将识别带有SRC属性的SCRIPT标签。虽然SCRIPT标签在HTML中可能常见,但它们在URL或POST数据中的存在是XSS攻击的一个迹象。
在上面的示例启发式规则中,请注意内部大括号内的字符是我们在此称为中性化字符的字符。每个启发式规则可以有一个或多个中性化字符。每个中性化字符,在本例中为‘r’,表示最终将在HTTP响应体中被过滤器修改以阻止XSS攻击的字符。‘#’字符用作中性化替换字符——它在破坏HTML元素以及注入的脚本块方面是有效的。
在任何启发式规则中选择中性化字符都非常重要。为启发式规则选择错误的中性化字符可能会颠覆过滤器。例如,选择引号符号作为中性化字符将导致过滤器中性化引号。聪明的攻击者可以利用此行为强制匹配并中性化页面上的引号,意图启用否则不可能的XSS攻击。
对启发式规则的匹配本身并不触发过滤器检测XSS。相反,它指示过滤器必须检查HTTP响应体以验证输入URL或POST数据中的脚本是否实际重放到输出页面。
上面简要提到的解码过程是灵活的,还可以考虑各种Web平台的产物。根据需要,过滤器基于对相同输入数据的替代解释生成额外的签名(见下文)。例如,因为畸形的URLEncoded字符可能在不同Web平台上有不同的处理方式,过滤器必须能够构建适当的签名。
正则表达式乐趣 – 第2部分:签名
当启发式规则匹配时,过滤器为每个匹配生成一个签名。签名是一个新的正则表达式,将用于扫描HTTP响应体以查找重放的可疑输入。中性化替换字符在匹配签名后暂时放入输入中。然后继续匹配启发式规则,直到在输入中找不到更多匹配。签名是为不包含中性化替换字符的URL生成的。否则,签名本身将包含中性化替换字符,并且它们不会正确匹配HTTP响应中存在的攻击。
对于每个启发式规则,提供安全字符列表。对于检测脚本标签的启发式规则,安全字符是大于和小于字符以及字母数字。安全字符有效地形成了过滤器试图识别的XSS攻击的本质。
为什么使用签名?
如果过滤器简单地逐字搜索匹配,它不一定能找到匹配。Web服务器可能在重放时偶然从请求中删除或转换特定字符。这实际上很常见,攻击者可以利用此行为为自己谋利。
安全字符限制在“低ASCII”范围(0x00 – 0x7F),以便我们基本上保持字符集无关。能够替代“低ASCII”编码的字符集(例如:UTF-7)目前没有特殊处理,但将来会对这些字符集的一般使用施加一些新的限制。(这些更改超出了本文的范围,但请关注我的博客以获取更多细节)。
以下是检测脚本标签的启发式规则的匹配示例:
<SCRIPT src=”http://www.fabrikam.com/evil.js”
为此匹配生成的签名将是:
<SC{R}IPT¤src¤¤http¤¤¤www¤fabrikam¤com¤evil¤js¤
签名中的每个¤表示原始匹配中的非安全字符。零到N个未指定字符的序列将匹配任何¤。(目前N是10)
如果没有为特定页面生成签名,则过滤器允许页面加载而不修改——未检测到XSS。
但是,如果签名存在,过滤器将扫描HTTP响应体的每个签名。一旦识别,过滤器准确记录必须中性化的字符,如签名中大括号内的字符所示。一旦签名列表完全处理,中性化替换字符就位,HTTP响应体传递到浏览器进行渲染。
页面将正常渲染,但信息栏将通知用户页面已修改,XSS攻击将被禁用。
XSS过滤器限制
与所有安全缓解和保护技术一样,XSS过滤器的方法确实有局限性,因为它是应用程序兼容性、安全性和性能之间的务实平衡。一些示例:
- 注入某些上下文不被阻止。例如:内容可以直接注入javascript而不跳出字符串的场景。
- 目前不阻止由某些HTTP头促进的注入。例如:基于“Referer”的注入。
- 如果页面包含多个附近的注入点,可以构建挫败XSS过滤器的攻击。
这些都是无疑在真实网站上发生的问题。XSS过滤器设计理念要求我们区分通常使XSS过滤器被绕过的问题与仅在某些情况下适用的问题。上述问题,虽然非常显著,显然属于后一类。随着时间的推移,我们将继续增强XSS过滤器以最大化其有效性,但在此过程中我们不会损害网站兼容性。
结论
以平衡兼容性、安全性和性能需求的方式缓解XSS是具有挑战性的。XSS过滤器的两阶段方法通过非常具体地针对反射型(“Type-1”)XSS攻击帮助我们实现这些目标。这种架构使我们能够默认缓解当今网络上最常见的XSS,为Internet Explorer 8的用户提供保护。
- David Ross, SVRD Blogger 发布内容“按原样”提供,不提供任何保证,也不授予任何权利。 2008年8月19日下午4:15:更新正确日期