您现在的位置是:网站首页 > DOM 型 XSS文章详情
DOM 型 XSS
陈川
【
前端安全
】
62320人已围观
6143字
DOM 型 XSS 的基本概念
DOM 型 XSS 是一种特殊类型的跨站脚本攻击,其恶意代码的执行完全发生在客户端,不经过服务器端处理。与反射型或存储型 XSS 不同,DOM 型 XSS 的漏洞根源在于客户端 JavaScript 对不可信数据的处理方式。攻击者通过操纵 DOM 环境,诱使受害者的浏览器执行恶意脚本。
典型的 DOM 型 XSS 攻击流程:
- 用户访问包含漏洞的网页
- 浏览器创建页面的 DOM 表示
- 恶意数据被插入到 DOM 中
- 浏览器解析并执行插入的恶意代码
DOM 型 XSS 的工作原理
DOM 型 XSS 的核心在于 JavaScript 对 document.location
、document.URL
或 document.referrer
等客户端对象的处理。当这些值被直接用于动态生成 DOM 内容而没有适当过滤时,就可能产生漏洞。
// 危险的示例
const userInput = document.location.hash.substring(1);
document.getElementById('output').innerHTML = userInput;
在这个例子中,攻击者可以构造如下 URL:
http://example.com/page.html#<script>alert('XSS')</script>
当用户访问这个 URL 时,恶意脚本就会被执行。
常见的 DOM XSS 触发点
1. 基于 URL 的注入
URL 参数是最常见的攻击向量:
// 从URL获取参数的不安全方式
const searchParams = new URLSearchParams(window.location.search);
const name = searchParams.get('name');
document.write(`Hello ${name}`);
攻击者可以构造恶意链接:
http://example.com/?name=<img src=x onerror=alert(1)>
2. HTML5 localStorage/sessionStorage
// 不安全的storage使用
const userPref = localStorage.getItem('user-preference');
document.body.innerHTML = userPref;
3. 第三方脚本操作
// 不安全的第三方脚本集成
const script = document.createElement('script');
script.src = untrustedSource;
document.body.appendChild(script);
高级 DOM XSS 技术
1. 基于 DOM clobbering 的攻击
<!-- 恶意HTML注入 -->
<form id="document" action="javascript:alert('XSS')"></form>
<a id="URL" href="javascript:alert('XSS')"></a>
2. 原型链污染
// 原型污染示例
const maliciousPayload = '{"__proto__":{"isAdmin":true}}';
JSON.parse(maliciousPayload);
// 之后在代码中
if (user.isAdmin) {
// 授予管理员权限
}
防御 DOM 型 XSS 的策略
1. 安全的 DOM 操作方法
// 使用textContent代替innerHTML
const userInput = document.location.hash.substring(1);
document.getElementById('output').textContent = userInput;
// 使用createElement和appendChild
const div = document.createElement('div');
div.textContent = userInput;
document.body.appendChild(div);
2. 内容安全策略 (CSP)
<!-- 严格的CSP策略 -->
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self' 'unsafe-inline'">
3. 输入验证和清理
// 使用DOMPurify清理HTML
const clean = DOMPurify.sanitize(dirtyInput);
document.getElementById('output').innerHTML = clean;
// 自定义验证函数
function sanitizeInput(input) {
return input.replace(/</g, '<').replace(/>/g, '>');
}
现代框架中的 DOM XSS 防护
1. React 的安全特性
// React默认会转义内容
const userInput = window.location.hash.substring(1);
return <div>{userInput}</div>; // 自动转义
// 危险地设置innerHTML
<div dangerouslySetInnerHTML={{__html: userInput}} />; // 需要显式确认
2. Vue 的防护机制
<template>
<!-- 自动转义 -->
<div>{{ userInput }}</div>
<!-- 显式原始HTML -->
<div v-html="userInput"></div>
</template>
3. Angular 的安全上下文
// Angular的安全处理
import { DomSanitizer } from '@angular/platform-browser';
constructor(private sanitizer: DomSanitizer) {}
safeHtml = this.sanitizer.bypassSecurityTrustHtml(untrustedHtml);
自动化检测工具
1. 静态分析工具
- ESLint 插件:
eslint-plugin-security
- 自定义规则示例:
{
"rules": {
"security/detect-unsafe-regex": "error",
"security/detect-eval-with-expression": "error"
}
}
2. 动态测试工具
- OWASP ZAP
- Burp Suite DOM Invader
- 浏览器扩展:DOM XSS Scanner
3. 单元测试中的检测
// 使用Jest测试XSS漏洞
test('should sanitize user input', () => {
const maliciousInput = '<script>alert(1)</script>';
const safeOutput = sanitizeInput(maliciousInput);
expect(safeOutput).not.toContain('<script>');
});
实际案例分析
1. 基于 hashchange 的事件攻击
// 不安全的hashchange处理
window.addEventListener('hashchange', function() {
document.getElementById('content').innerHTML = location.hash.slice(1);
});
// 攻击者可以构造:
// http://example.com/#<img src=x onerror=alert(document.cookie)>
2. JSONP 回调漏洞
// 不安全的JSONP实现
function handleResponse(data) {
document.write('User: ' + data.username);
}
const script = document.createElement('script');
script.src = 'https://api.example.com/user?callback=handleResponse';
document.body.appendChild(script);
// 攻击者可以注入恶意回调
https://api.example.com/user?callback=alert(1)//
3. 动态样式表注入
// 不安全的样式表处理
const userTheme = localStorage.getItem('theme');
const style = document.createElement('style');
style.innerHTML = userTheme;
document.head.appendChild(style);
// 攻击者可以注入:
// background: url('javascript:alert(1)');
浏览器安全机制与绕过
1. XSS Auditor 的局限性
虽然现代浏览器有内置的XSS防护,但DOM型XSS经常能绕过:
// 绕过XSS Auditor的技巧
const payload = 'javascript:alert(1)'.split('').reverse().join('');
document.location = payload;
2. Trusted Types API
现代浏览器的防护机制:
// 启用Trusted Types
if (window.trustedTypes && window.trustedTypes.createPolicy) {
const escapePolicy = trustedTypes.createPolicy('escapePolicy', {
createHTML: str => str.replace(/</g, '<').replace(/>/g, '>')
});
}
// 使用策略
element.innerHTML = escapePolicy.createHTML(untrustedInput);
3. 同源策略的边界情况
// 跨源窗口引用
const win = window.open('https://attacker.com');
setTimeout(() => {
win.location = 'javascript:alert(document.domain)';
}, 1000);
开发流程中的安全实践
1. 安全代码审查清单
- [ ] 是否避免使用
innerHTML
/outerHTML
? - [ ] 所有动态内容是否经过适当转义?
- [ ] 是否限制了
eval()
、setTimeout(string)
等危险函数? - [ ] CSP 策略是否适当配置?
2. 安全编码规范示例
// 安全的数据绑定模式
function safeTemplate(strings, ...values) {
let result = strings[0];
for (let i = 0; i < values.length; i++) {
result += escapeHtml(values[i]) + strings[i + 1];
}
return result;
}
function escapeHtml(str) {
return str.toString()
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
3. 持续集成中的安全检查
# .github/workflows/security.yml
name: Security Checks
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm install
- run: npm run lint-security
- run: npm run test-xss
上一篇: 存储型 XSS(持久型)
下一篇: XSS 攻击的危害与影响