使用静态分析查找和修复基于DOM的XSS漏洞
尽管已经付出诸多努力来修复Web中的跨站脚本(XSS)漏洞,它仍然是软件中最危险的安全问题之一。特别是基于DOM的XSS正变得越来越重要:这种XSS漏洞完全存在于客户端代码(例如JavaScript)中。实际上,越来越多的Web应用完全使用前端技术实现其UI代码:单页应用(SPA)更容易出现此类漏洞,主要是因为它们比其他Web应用使用更多JavaScript。而Electron应用中的XSS可能造成更大危害,因为Electron框架提供了系统级API(例如读取本地文件和执行程序)。
以下文章将深入探讨Mozilla基于ESLint的工具如何检测和预防基于DOM的XSS,以及它如何对您现有的Web应用有所帮助。该ESLint插件是作为我们缓解Firefox浏览器中注入攻击的一部分而开发的,Firefox的用户界面同样使用HTML、JavaScript和CSS编写。
背景:基于DOM的XSS实际案例
首先让我们看看基于DOM的XSS的典型来源。假设有以下JavaScript代码:
|
|
您首先会注意到名为html的变量,它使用JavaScript模板字符串构造HTML片段。它还包含一个imageUrl变量,用于src属性。然后将完整的html字符串赋值给main.innerHTML。
如果我们假设imageUrl变量由攻击者控制,那么他们可以轻松突破src属性语法,插入任意选择的HTML来发起XSS攻击。
这个例子展示了意外实现DOM XSS漏洞是多么容易:应用期望一个图片URL,但也接受各种字符串,这些字符串随后被解析为HTML和JavaScript。这足以启用XSS攻击。
静态分析的工作原理
要避免这些错误,我们需要找到应用中所有将字符串解析为HTML的实例,然后确定是否可以从外部控制(例如表单输入、URL参数等)。为了高效完成此操作,我们需要检查源代码中的各种模式。
当我们首次尝试时,使用grep或ripgrep等工具进行文本搜索并不成功:即使使用复杂的搜索模式,我们也得到了数千个结果,其中包含许多误报(例如安全硬编码字符串的赋值)。我们知道需要更懂语法的工具。
静态分析的注意事项
由于我们扫描JavaScript源代码,有些事情不容易做到:
- 静态分析几乎无法了解变量内容(即有害、无害、攻击者控制、硬编码)
- JavaScript源代码不告诉我们变量类型
- 静态分析容易被压缩、打包或混淆所迷惑
在Mozilla,我们能够接受这些限制,因为我们可以基于现有的工程实践:
- 所有提议的补丁都要经过代码审查
- 代码库包含所有相关JavaScript源代码
实现技术细节
我们的ESLint插件实现基于抽象语法树(AST)分析。例如对于foo.innerHTML = evil的AST表示为:
|
|
而对于安全赋值foo.innerHTML = ""的AST为:
|
|
通过识别这种差异,我们可以区分安全和不安全的代码模式。
误报处理和消毒器集成
我们还需要考虑误报问题。对于已知通过其他方式安全的变量内容,我们建议开发人员使用受信任的消毒器库。我们允许右侧赋值只要是包装在已知消毒器函数调用中:
|
|
我们最终开发了名为eslint-plugin-no-unsanitized的ESLint插件,其中包含对其他典型XSS相关源代码片段的检查。
评估与集成效果
当我们首次尝试自动查找Firefox UI中的XSS时,使用grep发现了数千个潜在漏洞。使用ESLint插件后,我们将这个数字减少到34个发现!这使得我们能够开始有针对性的手动代码审计,最终只发现两个关键安全错误。
我们通过迭代方法将eslint-plugin-no-unsanitized完全集成到CI系统中:
- 按时间和目录逐步启用检查
- 跳过测试文件
- 对违反检查但实际不安全的代码允许例外
结论:您可以修复DOM-XSS
在整个代码库中修复基于DOM的XSS并不容易,但我们相信这个概述将作为有用的指南。作为第一步,我们高度推荐直接使用eslint-plugin-no-unsanitized插件并对源代码运行检查。干运行已经可以告诉您基于DOM的XSS是否是个问题。
但需要注意的是,源代码分析不是银弹:存在明显的注意事项,最好通过浏览器和运行时的强制执行来补充静态分析。最终,您将能够摆脱大量DOM-XSS问题。