直播网站建设重庆,如何创建一个自己的网页,网站添加标签云,清华大学学生工作做网站文档对象模型#xff08;DOM#xff09;充当着 HTML 和 JavaScript 之间的接口#xff0c;搭建起静态内容与动态交互之间的桥梁#xff0c;对现代 Web 开发而言#xff0c;DOM 的作用不可或缺。
然而#xff0c;DOM 也有一个致命的陷阱 —— DOM 劫持。DOM 劫持是指当 H…文档对象模型DOM充当着 HTML 和 JavaScript 之间的接口搭建起静态内容与动态交互之间的桥梁对现代 Web 开发而言DOM 的作用不可或缺。
然而DOM 也有一个致命的陷阱 —— DOM 劫持。DOM 劫持是指当 HTML 元素与全局 JavaScript 变量或函数产生冲突时可能会导致 Web 应用程序出现不可预期的行为甚至产生潜在的安全漏洞。
今天就可大家一起来聊聊 DOM 劫持的问题。
DOM 劫持是怎么发生的
每个 HTML 元素都可以有一个唯一的 id 或 name 属性方便在 JavaScript 中引用特定的元素。例如下面的 HTML 按钮具有一个值为 myButton 的 id 属性
button idmyButtonClick Me!/button我们可以在 JavaScript 代码中使用此 ID 来操作按钮例如当点击时改变其文本
document.getElementById(myButton).onclick function() {this.textContent Clicked!;
};如果 HTML 元素的 id 或 name 属性与全局 JavaScript 变量或函数冲突会发生什么呢
当浏览器加载 HTML 页面时它会自动为 HTML DOM 中的每个 id 和 name 属性创建全局 JavaScript 变量。如果我们有一个名为 “myButton” 的 HTML 元素浏览器会创建一个全局 JavaScript 变量 myButton引用该 HTML 元素。
现在让我们考虑一个场景其中我们声明了一个名为 myButton 的 JavaScript 函数
function myButton() {// some code
}但我们还有一个 id 或 name 为 “myButton” 的 HTML 元素。当页面加载时浏览器的自动进程会引用 HTML 元素并覆盖 JavaScript 函数 myButton。
button idmyButtonClick Me!/buttonconsole.log(myButton); // This will log the HTML button element, not the function这个过程叫做 DOM 劫持它可能会引发不可预测的行为和安全漏洞。如果攻击者能控制这些属性他们可能有能力向网页注入恶意代码从而引发包括跨站脚本XSS在内的安全问题。
为了说明这一点让我们考虑以下情景
Enter your name: input idusername typetext
button onclickgreet()Greet/buttonfunction greet() {var username document.getElementById(username).value;alert(Hello ${username});
}攻击者可以输入类似 img idalert srcx onerroralert(1) 的内容创建一个 id 为 alert 的新 HTML 组件。该组件会破坏 JavaScript 中的正常 alert 功能。下次网站尝试使用此功能时它将无法正常工作甚至可能运行恶意代码。
我们想象现在有一个带有用户反馈功能的基本 Web 应用程序。用户输入自己的姓名和反馈消息然后提交。页面显示反馈
html:
h2Feedback Form/h2
formlabel fornameName:/labelbrinput typetext idname namenamebrlabel forfeedbackFeedback:/labelbrtextarea idfeedback namefeedback/textareabrinput typesubmit valueSubmit
/formdiv idfeedbackDisplay/divJavaScript:
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 pb${name}/b: ${feedback}/p;
};这段代码会获取用户的姓名和反馈并将其显示在 FeedbackDisplay div 内的段落元素中。
攻击者可以通过在反馈表单中提交一段 HTML 来利用此代码。例如如果他们在名称字段中输入以下代码并提交表单则反馈显示区域就会被 Script 替换
script idfeedbackDisplaywindow.location.hrefhttp://conardli.top;/script当表单尝试显示下一条反馈时就会执行脚本将用户重定向到恶意网站。这是 DOM 劫持造成严重后果的一个例子 —— 攻击者可以控制用户的浏览器从而窃取敏感数据或安装恶意软件。
缓解 DOM 劫持的安全编码实践
通过更深入地了解这些漏洞我们可以继续采取一些最佳实践来减轻 DOM 劫持的风险。
正确定义变量和函数的作用域
DOM 劫持的最常见原因之一是滥用 JavaScript 中的全局作用域。
通过在特定的作用域范围内定义变量和函数我们可以限制对该范围或任何嵌套范围的覆盖并最大限度地减少潜在的冲突。
我们来用 JavaScript 的作用域规则并重构前面的示例来展示如何做到这一点
(function() {// All variables and functions are now in this functions scopeconst 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 inputname DOMPurify.sanitize(name);feedback DOMPurify.sanitize(feedback);const newFeedback document.createElement(p);newFeedback.textContent ${name}: ${feedback};feedbackElement.appendChild(newFeedback);};
})();首先我们使用了 DOMPurify 来对上述代码块中的用户输入进行清理。
在此版本的代码中我们将所有内容都包含在立即调用函数表达式 (IIFE) 中这会创建一个新作用域。form 和 FeedbackElement 变量以及分配给 onsubmit 事件处理程序的函数不在全局作用域内因此它们不能被劫持。
使用唯一标识符
确保网页上的每个元素都有唯一的 id 可以降低无意中覆盖重要函数或变量的风险。另外避免使用通用名称或可能与全局 JavaScript 对象或函数冲突的名称。
避免全局命名空间污染
保持全局命名空间干净是编写安全 JavaScript 的一个重要方面。全局作用域中的变量和函数越多DOM劫持的风险就越大。使用 JavaScript 的函数作用域或 ES6 的块作用域来保留变量和函数。这是使用后者的示例 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 inputname DOMPurify.sanitize(name);feedback DOMPurify.sanitize(feedback);let newFeedback document.createElement(p);newFeedback.textContent ${name}: ${feedback};feedbackElement.appendChild(newFeedback);};在这段代码中我们使用块由 {} 定义来创建新作用域。所有变量和函数现在都限制在该块中并且不在全局作用域内。
正确使用 JavaScript 特性
现代 JavaScript 提供了一些有助于最大限度地缓解 DOM 劫持的风险。特别是 ES6 中引入的 let 和 const 关键字提供了对声明变量的更多控制。
在以前我们使用 var 关键字声明 JavaScript 变量。var 有一些怪癖其中之一是就它没有块作用域只有函数作用域和全局作用域。这意味着用 var 声明的变量可以在声明它的块之外访问和覆盖。
另一方面let 和 const 都具有块作用域这意味着它们只能在声明它们的块内访问。这一特性通常使它们成为变量声明的更好选择因为它限制了覆盖变量的可能性。
我们还可以使用 const 来声明常量 — 分配它们后我们无法更改的值。它们可以防止重要的变量被意外覆盖。 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 inputname DOMPurify.sanitize(name);feedback DOMPurify.sanitize(feedback);const newFeedback document.createElement(p);newFeedback.textContent ${name}: ${feedback};feedbackElement.appendChild(newFeedback);};在此代码中我们将所有 var 的使用替换为 const。我们将所有变量限制在声明它们的块中并且常量不能被覆盖。
但是 使用 let 和 const 并不能完全消除 DOM 劫持的风险但这种做法仍然是安全编码的一个关键方面。
使用 Devtools 发现潜在的 DOM 劫持风险
例如 Chrome 或 Firefox 中的浏览器开发者工具也是探测 DOM 劫持漏洞的强大助手。
最简单的方法我们直接打开 Devtools。
然后在控制台输入 window 这里面包含了网站全局作用域下所有的全局变量和函数。
然后我们检查下是否有任何看起来不合适的变量尤其是那些与 HTML 元素 id 或 name 同名的变量。
通过 Elements 选项卡编辑页面的 HTML 来操控 DOM 并测试潜在的漏洞。例如添加一个 id 与全局变量或函数相匹配的元素看看是否会被覆写。