DOM型XSS如何规避

wen 开源项目 27

本文目录导读:

DOM型XSS如何规避

  1. 核心防御:输出转义与编码(最关键)
  2. 输入源控制:数据获取时的严格校验
  3. 高级防御:内容安全策略(CSP)与沙箱
  4. 特定场景规避:框架与API安全使用
  5. 工具与自动化检测
  6. 避坑清单

DOM型XSS(基于DOM的跨站脚本攻击)的规避需要从输入源头、数据流转、输出编码三个层面进行防御,核心原则是永远不要信任用户输入或任何不可控的数据源,以下是具体的规避策略和代码实践:


核心防御:输出转义与编码(最关键)

DOM型XSS的本质是不可信数据被当作HTML/JavaScript代码执行,在将数据插入到HTML、属性、JavaScript代码或URL前,必须进行上下文感知的编码。

1 避免直接操作危险API

🚫 高危操作(应完全避免):

// 1. innerHTML - 直接解析HTML
element.innerHTML = userData;
// 2. document.write / writeln
document.write('<script>alert(1)</script>');
// 3. eval() / setTimeout(string) / setInterval(string)
eval(userData);
// 4. location / href - 直接设置
window.location = userInput;

✅ 安全替代方案(优先使用):

// 使用textContent(自动转义HTML标签)
element.textContent = userData;
// 使用innerText(会考虑CSS样式,性能稍差)
element.innerText = userData;
// 创建DOM节点(最安全)
const p = document.createElement('p');
p.textContent = userData;
document.body.appendChild(p);

2 正确使用编码函数

当必须动态生成HTML时,使用encodeURI或自定义转义函数:

// HTML实体编码(防HTML注入)
function escapeHtml(str) {
    const div = document.createElement('div');
    div.appendChild(document.createTextNode(str));
    return div.innerHTML;
}
// JavaScript字符串编码(防eval注入)
function escapeJs(str) {
    return str.replace(/[\\"']/g, function(match) {
        if (match === '\\') return '\\\\';
        if (match === '"') return '\\"';
        if (match === "'") return "\\'";
    });
}
// URL编码(防location注入)
function escapeUrl(str) {
    return encodeURIComponent(str);
}

输入源控制:数据获取时的严格校验

DOM型XSS的输入源通常来自URL参数HashlocalStoragepostMessageReflect等,必须对来源数据进行格式净化

1 URL参数净化(location.search)

// 从URL获取参数
function getParam(name) {
    const urlParams = new URLSearchParams(window.location.search);
    const value = urlParams.get(name);
    // 校验:只允许字母数字
    if (!/^[\w-]+$/.test(value)) {
        return ''; // 或抛异常
    }
    return value;
}

2 Hash净化(location.hash)

// 只允许纯文本(如锚点导航)
function safeHash() {
    const hash = decodeURIComponent(window.location.hash.slice(1));
    // 校验:只允许允许的字符
    if (!/^[a-zA-Z0-9_-]+$/.test(hash)) {
        return '';
    }
    return hash;
}

3 postMessage数据校验

window.addEventListener('message', (event) => {
    // 1. 验证来源(origin)
    if (event.origin !== 'https://trusted-site.com') {
        return;
    }
    // 2. 验证数据格式
    if (typeof event.data !== 'string') {
        return;
    }
    // 3. 安全处理数据
    document.getElementById('output').textContent = event.data;
});

高级防御:内容安全策略(CSP)与沙箱

CSP是浏览器层面的终极防线,即使开发有漏洞,CSP也能阻止XSS执行。

1 设置严格的CSP头部

服务器HTTP响应头:

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; object-src 'none'; base-uri 'self'
  • 禁止内联脚本:移除'unsafe-inline',强制所有JS通过外部文件加载。
  • 禁止eval:不添加'unsafe-eval'
  • 限制来源:仅允许加载同域资源。

2 使用nonce或hash(当必须内联时)

若必须使用内联脚本(如某些框架),用nonce值限制:

<!-- 服务端生成随机nonce -->
<script nonce="abc123">
    // 安全的内联代码
</script>

CSP头部:

Content-Security-Policy: script-src 'nonce-abc123'

3 DOM隔离:沙箱iframe的渲染,放入沙箱iframe中:

<iframe sandbox="allow-scripts allow-same-origin" src="about:blank"></iframe>
// 在沙箱中安全显示用户内容
iframe.contentDocument.body.textContent = userData;

特定场景规避:框架与API安全使用

1 jQuery安全使用(常见误用)

🚫 高危

$('#output').html(userData);  // 同innerHTML
$('<div>'+userData+'</div>'); // 字符串拼接创建元素
$('#link').attr('href', userData); // 可注入javascript:协议

✅ 安全方式

$('#output').text(userData);         // 替换为text()
$('<div>').text(userData);           // 创建节点再入DOM
$('#link').prop('href', userData);   // 使用prop替代attr

2 DOM clobbering(DOM覆盖面)规避

防止攻击者通过idname属性覆盖全局变量:

// 检查变量是否存在
if (typeof window.userConfig === 'object' && window.userConfig !== null) {
    // 安全访问
}
// 使用let/const而不是var(也不会被DOM覆盖)

3 模板安全(如Vue/React)

现代框架默认有textContent绑定,但需注意:

// React:使用{}插值会自动转义
<div>{userData}</div>  // ✅ 安全
// React:dangerouslySetInnerHTML需手动清理
<div dangerouslySetInnerHTML={{__html: sanitize(userData)}} />
// Vue:使用{{}}会自动转义
<div>{{ userData }}</div>  // ✅ 安全
// Vue:v-html需清理
<div v-html="sanitize(userData)" />

工具与自动化检测

  • 动态检测:使用DOMPurify库对不可信HTML进行清理:
    const cleanHtml = DOMPurify.sanitize(unsafeHtml);
    element.innerHTML = cleanHtml; // 此时安全
  • 静态扫描:使用ESLint插件eslint-plugin-no-unsanitized检测innerHTML使用。
  • 浏览器测试:Chrome DevTools的Security面板可查看CSP违规报告。

避坑清单

操作 风险等级 正确做法
element.innerHTML = x 🚨高危 改用textContentDOMPurify
window.location = x 🚨高危 仅允许白名单URL + encodeURI
eval(x) 🔴禁用 使用JSON.parse(如果目标是对象)
document.write(x) 🔴禁用 使用DOM API创建节点
new Function(x) 🔴禁用 同上
setTimeout(x, 0) (字符串) 🔴禁用 传入函数而非字符串
postMessage接收数据 🟡中危 校验origin + 数据格式
jQuery .html(x) 🚨高危 替换为.text(x)
URL参数直接使用 🚨高危 正则校验 + 类型检查

核心原则任何从URL、Hash、localStorage、postMessage、Cookie、前端数据库等来源获取的数据,在输出到HTML、JS、URL、属性前,都必须进行上下文感知的转义或使用安全的API操作。

抱歉,评论功能暂时关闭!