DOM Clobbering 是什么?前端如何避免 DOM 属性覆盖带来的安全问题?
简化版
DOM Clobbering 是攻击者通过 HTML 中的 id、name 等属性,把页面上的全局变量或对象属性“覆盖”成 DOM 节点,导致代码逻辑被劫持。它常发生在把不可信 HTML 插入页面、同时代码又依赖 window.foo、document.forms.name 这类隐式 DOM 全局映射时。防范方式是净化 HTML、不要依赖 DOM clobbering 全局变量、访问对象属性要做类型校验,并使用安全的 DOM API。
详细版
浏览器为了兼容历史行为,会把部分带 id/name 的元素暴露到全局对象上。
<form id="config"></form>
某些场景下代码访问 window.config,拿到的可能是这个 form,而不是你以为的配置对象。
| 风险点 | 说明 |
|---|---|
| 不可信 HTML | 攻击者可插入 id/name |
| 隐式全局访问 | window.xxx 被 DOM 节点污染 |
| 类型不校验 | 把节点当配置对象使用 |
| 老代码 | 表单、锚点名称映射更多 |
DOM Clobbering 很隐蔽,因为攻击代码看起来可能只是“普通 HTML”。
完整版教学
一、DOM Clobbering 的核心
Clobbering 有“覆盖、破坏”的意思。DOM Clobbering 指的是 DOM 元素通过名字映射影响 JavaScript 变量或属性访问。
如果应用把用户可控 HTML 插进页面,攻击者就可能构造特殊 id/name 来影响代码逻辑。
二、为什么浏览器会这样
早期浏览器为了方便开发,允许通过 window.elementId 访问页面元素。虽然现代开发不推荐,但兼容行为仍然存在。
这就让 HTML 不只是展示内容,也可能影响 JS 变量解析。
三、一个简化例子
假设代码期望全局配置:
const url = window.config.apiUrl
攻击者插入:
<form id="config"><input name="apiUrl" value="https://evil.com"></form>
在某些访问模式下,window.config 可能变成 DOM 元素,后续逻辑就偏离预期。
四、和 XSS 的关系
DOM Clobbering 不一定直接执行脚本,但它可能成为 XSS 或逻辑绕过的前置条件。
例如它可以影响脚本 URL、配置对象、跳转地址、表单引用和安全判断。
五、防范策略
核心策略是减少不可信 HTML 和隐式全局访问。
| 策略 | 说明 |
|---|---|
| HTML Sanitizer | 过滤危险标签和属性 |
| 不用 window 隐式变量 | 配置走模块作用域或显式对象 |
| 类型校验 | 确认拿到的是期望类型 |
| CSP | 降低脚本执行风险 |
六、代码习惯
不要写依赖全局名称映射的代码:
// 不推荐
const el = window.submitButton
// 推荐
const el = document.getElementById('submitButton')
配置对象应从模块导入或闭包传入,不要挂在容易被污染的全局名字上。
七、常见误区与追问
- 误区:只有 script 标签才会造成安全问题。 普通 HTML 的 id/name 也可能影响 JS 属性访问。
- 误区:现代浏览器没有 DOM 全局映射。 为了兼容,相关行为仍存在,只是细节因浏览器而异。
- 误区:使用 innerHTML 只要过滤 script 就够。 还要过滤事件属性、危险 URL、id/name 污染等风险。
- 追问:它和原型污染有什么区别? 原型污染改对象原型链,DOM Clobbering 利用 DOM 名称映射覆盖属性访问。
- 追问:如何检测? 安全测试中构造特殊 id/name,观察全局对象和关键配置是否被覆盖。
- 追问:CSP 能完全防住吗? 不能完全防逻辑覆盖,但能降低进一步执行脚本的风险。
八、加强记忆
DOM Clobbering 记成“HTML 抢了 JS 名字”。不可信 HTML 要净化,代码不要依赖 window.xxx 隐式元素映射,关键对象访问要校验类型。