文档对象模型(DOM)充当HTML和JavaScript之间的接口,弥合了静态内容和动态交互性之间的差距。此功能使现代网络开发人员不可或缺。
但是,DOM具有陷阱。当HTML元素与全局JavaScript变量或功能冲突时,会发生DOM CLOBBER,这可能导致意外行为和Web应用程序中的潜在安全漏洞。
解决该问题需要勤奋的编程实践,对JavaScript的实质性知识以及对Web应用程序安全性的坚定掌握。本文探讨了DOM Clobbering的概念,并提供了构建更安全和强大的Web应用程序的策略。
如何发生dom clobbering
每个HTML元素都可以具有唯一的ID或名称属性,以便于引用JavaScript中的特定元素。例如,以下HTML按钮具有带有值"myButton"
的ID属性:
Click Me!
我们可以在我们的JavaScript代码中使用此ID来操纵按钮,例如,单击时更改其文本:
document.getElementById('myButton').onclick = function() {
this.textContent = 'Clicked!';
};
DOM促进了HTML和JavaScript之间的这种相互作用。但是,如果HTML元素的ID或名称属性与全局JavaScript变量或函数发生冲突,会发生什么?
当浏览器加载HTML页面时,它会自动为HTML DOM中的每个ID和名称属性创建全局JavaScript变量。如果我们有一个名为"myButton"
的HTML元素,则浏览器将制作一个全局JavaScript变量,myButton
,引用HTML元素。
现在,让我们考虑一个场景,在该场景中,我们声明了一个名为myButton
:
的JavaScript函数
function myButton() {
// some code
}
,但我们也有一个带有ID或名称"myButton"
的HTML元素。当页面加载时,浏览器的自动进程覆盖JavaScript函数myButton
参考HTML元素:
Click Me!
console.log(myButton); // This will log the HTML button element, not the function
此过程是dom clobbering,这可能会导致不可预测的行为和安全漏洞。如果攻击者可以控制属性,则他们可能能够将恶意代码注入网页,从而导致cross-site scripting(XSS)等安全性问题。
要说明,请考虑以下情况:
Enter your name:
Greet
function greet() {
var username = document.getElementById('username').value;
alert(`Hello ${username}`);
}
攻击者可以键入类似![](x)
的内容,并使用'alert'
的'id'
创建新的HTML组件。该组件替代JavaScript中的正常警报功能或clobbers。下次网站尝试使用此功能时,它无法正常工作,甚至可能运行恶意代码。
识别dom clobbering漏洞
考虑带有用户反馈表格的基本Web应用程序。用户输入其名称和反馈消息,然后提交。该页面显示反馈:
html:
Feedback Form
-------------
Name:
Feedback:
JavaScript:
document.querySelector('form').onsubmit = function(event) {
event.preventDefault();
let name = document.getElementById('name').value;
let feedback = document.getElementById('feedback').value;
let feedbackElement = document.getElementById('feedbackDisplay');
feedbackElement.innerHTML = `**${name}**: ${feedback}
`;
};
此代码获取用户的名称和反馈,并将其显示在feedbackDisplay
div。
攻击者可以通过以反馈表格提交HTML来利用此代码。例如,如果他们在 name 字段中输入以下代码并提交表单,则反馈显示区域将被脚本标签替换:
window.location.href='http://maliciouswebsite.com';
当表单试图显示下一个反馈时,它将执行脚本,将用户重定向到恶意网站。
此方案创建漏洞,因为该应用程序需要用户提供的输入来创建HTML,从而生成具有与现有元素相同ID的脚本标签。这是Dom Clobbers and Carty Rackain的一个示例 - 攻击者可以控制用户的浏览器,这使他们能够窃取敏感数据或安装恶意软件。
安全的编码惯例,以防止DOM CLOBBERING
有了对这些漏洞的更深入的了解,我们可以继续进行一些最佳实践,以减轻DOM CLOBBER的风险。
正确范围变量和功能
DOM Clobbering最常见的原因之一是滥用JavaScript中的全局范围。通过定义特定范围内的变量和功能,我们可以将覆盖范围限制为该范围或任何嵌套范围,并最大程度地减少潜在的冲突。
让我们应用JavaScript的scoping rules并重构一个示例,以显示如何完成:
(function() {
// All variables and functions are now in this function's scope
const form = document.querySelector('form');
const feedbackElement = document.getElementById('feedbackDisplay');
form.onsubmit = function(event) {
event.preventDefault();
const name = document.getElementById('name').value;
const feedback = document.getElementById('feedback').value;
// Sanitize user input
name = DOMPurify.sanitize(name);
feedback = DOMPurify.sanitize(feedback);
const newFeedback = document.createElement('p');
newFeedback.textContent = `${name}: ${feedback}`;
feedbackElement.appendChild(newFeedback);
};
})();
注意:我们使用DOMPurify在上述代码块中对HTML进行消毒。您可以使用npm install dompurify
将其安装在node.js中。将其包含在您的html中。
在此版本的代码中,我们将所有内容都包含在Immediately Invoked Function Expression(Iife)中,该代码创建了一个新的范围。 form
和feedbackElement
变量以及分配给onsubmit
事件处理程序的功能不在全局范围中,因此它们可以被关闭。
使用唯一的标识符
确保网页上的每个元素具有唯一的ID,降低了无意中覆盖重要功能或变量的风险。另外,避免使用可能与全局JavaScript对象或函数冲突的通用名称或名称。
避免全球名称空间污染
保持全局名称空间是编写安全JavaScript的重要方面。全球范围中的变量和功能越多,DOM障碍的风险就越大。使用JavaScript的功能范围或ES6的块范围来保持您的变量和功能。这里是使用后者的一个示例:
// All variables and functions are now in this block's scope
let form = document.querySelector('form');
let feedbackElement = document.getElementById('feedbackDisplay');
form.onsubmit = function(event) {
event.preventDefault();
let name = document.getElementById('name').value;
let feedback = document.getElementById('feedback').value;
// Sanitize user input
name = DOMPurify.sanitize(name);
feedback = DOMPurify.sanitize(feedback);
let newFeedback = document.createElement('p');
newFeedback.textContent = `${name}: ${feedback}`;
feedbackElement.appendChild(newFeedback);
};
在此代码中,我们使用了一个块(由{}
定义)来创建一个新的范围。现在,所有变量和功能都局限于此块,不在全局范围中。
注意:本指南中的代码示例是为了演示目的而不是用于制作网站或应用程序的。
使用JavaScript功能来减轻DOM CLOBBERING
Modern JavaScript提供了几种功能,可帮助最大程度地减少DOM Clobbering的风险。特别是,ES6中引入的let
和const
关键字提供了对声明变量的更多控制。
传统上,我们使用var
关键字声明了JavaScript变量。但是,var
有一些怪癖,其中之一是它没有块范围唯一的功能范围和全局范围。这意味着可以在我们声明的街区外访问并覆盖使用var
声明的变量。
另一方面,let
和const
都具有块范围,这意味着它们只能在声明的块中访问。这种特征通常使它们成为变量声明的更好选择,因为它限制了覆盖变量的可能性。
我们还可以使用const
来声明常数,分配后可以更改。它们代表了防止对重要变量的意外更改的另一种有用的工具。
让我们看如何在我们的反馈表格代码中实现这些功能以防止dom clobbering:
const form = document.querySelector('form');
const feedbackElement = document.getElementById('feedbackDisplay');
form.onsubmit = function(event) {
event.preventDefault();
const name = document.getElementById('name').value;
const feedback = document.getElementById('feedback').value;
// Sanitize user input
name = DOMPurify.sanitize(name);
feedback = DOMPurify.sanitize(feedback);
const newFeedback = document.createElement('p');
newFeedback.textContent = `${name}: ${feedback}`;
feedbackElement.appendChild(newFeedback);
};
在此代码中,我们用const
替换了var
的所有用途。我们将所有变量限制在我们声明它们的块中,而常数不能被覆盖。
请记住,使用let
和const
并不能消除DOM Clobbering的风险,但是这种做法仍然是安全编码的关键方面。了解和实施这些JavaScript功能可以使您的Web应用程序更加安全。
监视和检测DOM CLOBBERING漏洞
安全的编码实践可以帮助防止DOM障碍,但是在出现时检测漏洞同样至关重要。让我们看一些可以帮助的工具和技术。
静态代码分析
Static application security testing (SAST) Snyk代码(例如Snyk代码)的工具可以有效地检测潜在的安全漏洞,包括DOM CLOBBERING。这些工具可以分析您的源代码而无需执行它,从而确定可能导致安全问题的模式。 Snyk的全面JavaScript覆盖范围使其成为网络开发人员的绝佳选择。
浏览器开发人员工具
浏览器开发人员工具,例如Chrome或Firefox中可用的工具,可以是检测DOM-CLOBBERING漏洞的强大盟友。
让我们详细介绍如何使用这些工具来识别潜在问题的简短示例:
- 在您的网页上,右键单击任何地方,然后选择 Inspect 打开开发人员工具。您也可以在Windows上使用 ctrl +
+ i cmd + option + i Macos。 - 导航到控制台选项卡,其中显示JavaScript错误或日志。
- 将窗口键入控制台,然后按输入以使用所有全局变量和函数检查全局范围。检查看起来不合适的任何变量,尤其是那些与您的HTML元素ID或名称相同的变量。
- 使用元素选项卡,编辑页面的HTML来操纵DOM并测试潜在的漏洞。例如,添加一个带有与全局变量或函数匹配的ID的元素以查看其是否被覆盖。
结论
本文解释了dom clobbering是如何发生的,其相关的危险以及如何防止它的。使用唯一标识符实施适当的可变范围,并正确选择JavaScript的let
和const
功能对于缓解此漏洞至关重要。
维护代码安全需要平衡预防和检测工作。使用推荐的编码实践,浏览器开发人员工具以及SNYK(例如SNYK)的SAST解决方案可以帮助查明现有和潜在的问题。
通过将这些技术纳入您的JavaScript应用程序并保持在Web应用程序安全趋势上的最新信息,您可以保持主动保护您的编码实践。