基于DOM的XSS漏洞与防护
字数 1306 2025-11-22 16:37:31
基于DOM的XSS漏洞与防护
1. 漏洞描述
基于DOM的XSS(跨站脚本)是一种客户端漏洞,攻击者通过操纵页面的DOM(文档对象模型)环境来注入恶意脚本。与反射型或存储型XSS不同,DOM型XSS的恶意代码无需经过服务器响应,而是直接在浏览器中通过修改DOM结构(如document.location、document.write等)触发。
关键特征:
- 攻击载荷在URL参数或客户端存储(如
localStorage)中传递,但不会被发送到服务器。 - 漏洞触发依赖于前端JavaScript对用户输入的不安全处理。
2. 漏洞原理与触发场景
2.1 典型触发流程
- 用户访问恶意构造的URL(例如:
http://example.com/page#<script>alert(1)</script>)。 - 页面JavaScript从URL片段(
location.hash)或参数(location.search)中读取数据。 - 数据未经净化直接被插入DOM(如通过
innerHTML或document.write)。 - 恶意脚本在受害者浏览器中执行。
2.2 常见危险代码模式
// 示例1:直接使用location.hash
const userInput = location.hash.substring(1);
document.getElementById("content").innerHTML = userInput; // 危险!
// 示例2:通过URL参数动态生成内容
const urlParams = new URLSearchParams(location.search);
document.write(urlParams.get("keyword")); // 危险!
3. 漏洞挖掘与验证
3.1 识别数据源
- URL参数:
location.search、location.hash、document.referrer。 - 客户端存储:
localStorage、sessionStorage、Cookie。 - 全局变量:通过
window.name或postMessage传递的数据。
3.2 测试方法
- 在参数中插入测试载荷(如
<img src=x onerror=alert(1)>)。 - 观察页面是否弹窗或DOM是否被修改(使用开发者工具检查元素)。
- 确认输入是否被编码或过滤:
- 若
<被转义为<,则漏洞不存在; - 若原始载荷被直接插入DOM,则漏洞存在。
- 若
4. 漏洞防护方案
4.1 输入验证与编码
- 避免直接操作HTML:使用
textContent代替innerHTML,避免将用户输入作为HTML解析。 - 上下文感知编码:
- 插入到HTML属性时,使用
setAttribute或编码特殊字符(如"→")。 - 插入到URL时,使用
encodeURIComponent。
- 插入到HTML属性时,使用
- 推荐使用安全库(如DOMPurify)对HTML进行净化:
const cleanInput = DOMPurify.sanitize(userInput); document.getElementById("content").innerHTML = cleanInput;
4.2 安全API与策略
- 禁用危险函数:避免使用
eval()、document.write()、innerHTML等。 - 实施内容安全策略(CSP):通过HTTP头禁止内联脚本执行:
Content-Security-Policy: script-src 'self'; object-src 'none';
4.3 设计层面防护
- 最小化DOM操作:将动态数据与代码逻辑分离(如使用前端框架的模板引擎)。
- 严格定义数据源:仅允许可信来源(如服务器渲染的数据)修改DOM。
5. 实战案例:修复漏洞代码
漏洞代码:
// 从URL片段中获取数据并直接插入页面
const data = decodeURIComponent(location.hash.slice(1));
document.querySelector("#status").innerHTML = data;
修复后代码:
// 使用textContent避免HTML解析
const data = decodeURIComponent(location.hash.slice(1));
document.querySelector("#status").textContent = data;
// 或使用DOMPurify净化
const cleanData = DOMPurify.sanitize(data);
document.querySelector("#status").innerHTML = cleanData;
6. 总结
DOM型XSS的防护核心是严格区分数据与代码。通过前端安全编码规范、自动化工具(如CSP、静态检查)以及框架的内置防护机制,可有效降低风险。