阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > DOM 型 XSS

DOM 型 XSS

作者:陈川 阅读数:10710人阅读 分类: 前端安全

DOM 型 XSS 的基本概念

DOM 型 XSS 是一种特殊类型的跨站脚本攻击,其恶意代码的执行完全发生在客户端的 DOM 环境中,不经过服务器端处理。与反射型或存储型 XSS 不同,DOM 型 XSS 的漏洞根源在于前端 JavaScript 代码对用户输入的不安全处理。

// 典型漏洞示例
const userInput = window.location.hash.substring(1);
document.getElementById("output").innerHTML = userInput;

在这个例子中,攻击者可以构造类似 #<img src=x onerror=alert(1)> 的 URL,当用户访问时就会执行任意 JavaScript 代码。

漏洞产生原理

DOM 型 XSS 的产生通常涉及以下几个关键环节:

  1. 污染源(Sources):用户可控的输入点

    • document.URL
    • document.location
    • document.referrer
    • window.name
    • localStorage/sessionStorage
    • 通过 postMessage 接收的数据
  2. 传播途径(Propagation):不安全的 API 使用

    • innerHTML/outerHTML
    • document.write/document.writeln
    • eval/setTimeout/setInterval 动态执行
    • location.href/location.assign
  3. 执行点(Sinks):最终触发代码执行的上下文

    • HTML 解析上下文
    • JavaScript 执行上下文
    • URL 上下文

常见危险场景分析

动态内容插入

// 危险示例:直接插入未转义内容
function showSearchResults(query) {
  const results = searchDatabase(query);
  document.getElementById("results").innerHTML = 
    `<div>您搜索的 "${query}" 结果如下:</div>`;
}

攻击者可以提交 "><script>alert(1)</script> 这样的查询参数来注入脚本。

基于 URL 的 XSS

// 从URL片段中获取参数
const token = window.location.hash.slice(1);
document.write(`<input type="hidden" value="${token}">`);

如果 URL 包含 #"><script>alert(1)</script>,就会导致 XSS 漏洞。

JSONP 回调漏洞

function handleResponse(data) {
  document.getElementById("user-data").innerHTML = data.userInfo;
}

const script = document.createElement("script");
script.src = "https://api.example.com/user?callback=handleResponse";
document.body.appendChild(script);

如果 API 返回 {"userInfo": "<img src=x onerror=alert(1)>"},就会触发 XSS。

现代前端框架中的 DOM XSS

即使是 React、Vue 等现代框架,如果使用不当也会存在 DOM XSS 风险:

React 中的危险

// 危险的使用方式
function UserProfile({ userInput }) {
  return <div dangerouslySetInnerHTML={{ __html: userInput }} />;
}

Vue 中的 v-html

<!-- 潜在XSS风险 -->
<template>
  <div v-html="userProvidedContent"></div>
</template>

Angular 的 bypassSecurityTrust

// 不安全的使用方式
constructor(private sanitizer: DomSanitizer) {}
displayContent() {
  this.trustedContent = this.sanitizer.bypassSecurityTrustHtml(userInput);
}

防御策略与技术

输入验证与净化

// 使用DOMPurify进行HTML净化
import DOMPurify from "dompurify";

const clean = DOMPurify.sanitize(dirtyInput, {
  ALLOWED_TAGS: ["b", "i", "em", "strong"],
  ALLOWED_ATTR: ["style"]
});

安全的 DOM 操作方式

// 使用textContent代替innerHTML
element.textContent = userInput;

// 使用DOM API创建元素
const div = document.createElement("div");
div.appendChild(document.createTextNode(userInput));

内容安全策略(CSP)

<!-- 严格的CSP策略 -->
<meta http-equiv="Content-Security-Policy" 
      content="default-src 'self'; script-src 'self' 'unsafe-inline'">

现代框架的最佳实践

React 示例:

// 安全的使用方式
function SafeComponent({ text }) {
  return <div>{text}</div>;
}

Vue 示例:

<!-- 安全的内容绑定 -->
<template>
  <div>{{ userContent }}</div>
</template>

高级攻击手法与防御

基于原型链污染的 XSS

// 攻击者可能污染原型链导致XSS
Object.prototype.innerHTML = "<script>alert(1)</script>";
document.body.appendChild(document.createElement("div"));

防御方法:

// 冻结Object.prototype
Object.freeze(Object.prototype);

// 使用hasOwnProperty检查
if (element.hasOwnProperty("innerHTML")) {
  element.innerHTML = safeContent;
}

SVG 文件中的 XSS

<!-- 恶意SVG文件示例 -->
<svg xmlns="http://www.w3.org/2000/svg">
  <script>alert(1)</script>
</svg>

防御策略:

  • 服务器设置正确的 Content-Type
  • 使用专门的 SVG 净化库
  • 避免直接渲染用户上传的 SVG

Web Worker 中的 XSS

// Worker中也可能存在XSS风险
const workerCode = `onmessage=function(e){eval(e.data)}`;
const blob = new Blob([workerCode]);
const worker = new Worker(URL.createObjectURL(blob));
worker.postMessage("alert(1)");

安全做法:

// 使用结构化克隆算法传递数据
worker.postMessage({ command: "render", data: sanitizedHTML });

自动化检测工具

  1. 静态分析工具

    • ESLint 插件:eslint-plugin-security
    • Semgrep 规则库
  2. 动态检测工具

    • OWASP ZAP
    • Burp Suite DOM Invader
    • Chrome DevTools 的 XSS 审计功能
  3. 单元测试方案

// 使用Jest测试XSS防护
test("sanitize function prevents XSS", () => {
  const malicious = "<script>alert(1)</script>";
  expect(sanitize(malicious)).not.toContain("<script>");
});

实际案例分析

案例1:单页应用路由 XSS

// 不安全的实现
router.beforeEach((to, from, next) => {
  document.title = to.query.title || "Default Title";
  next();
});

攻击者可以构造 ?title=<script>alert(1)</script> 的 URL。

修复方案:

// 安全实现
router.beforeEach((to, from, next) => {
  document.title = sanitizeTitle(to.query.title) || "Default Title";
  next();
});

function sanitizeTitle(str) {
  return String(str).replace(/[<>]/g, "");
}

案例2:富文本编辑器漏洞

// 不安全的富文本处理
editor.on("save", (content) => {
  saveToDatabase(content);
  preview.innerHTML = content;
});

防御方案应包含:

  1. 服务器端净化
  2. iframe 沙箱隔离
  3. 严格的 CSP 策略
  4. 纯文本预览选项

浏览器安全机制

  1. Trusted Types API
// 启用Trusted Types
if (window.trustedTypes && trustedTypes.createPolicy) {
  const escapePolicy = trustedTypes.createPolicy("escapePolicy", {
    createHTML: (str) => str.replace(/</g, "&lt;")
  });
}
  1. XSS Auditor(已弃用):
  • Chrome 曾实现的反射型 XSS 防护
  • 被 CSP 取代
  1. 跨源隔离
// 启用跨源隔离
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp

性能与安全的平衡

// 安全但性能较差的做法
function safeButSlow(content) {
  const temp = document.createElement("div");
  temp.textContent = content;
  return temp.innerHTML;
}

// 平衡方案
const escapeMap = {
  "<": "&lt;",
  ">": "&gt;",
  // ...其他需要转义的字符
};

function fastEscape(str) {
  return String(str).replace(/[<>]/g, (m) => escapeMap[m]);
}

相关漏洞模式扩展

  1. DOM Clobbering
<!-- 通过DOM污染全局变量 -->
<form id="xss"><input name="innerHTML" value="<script>alert(1)</script>"></form>
<script>
  // xss.innerHTML 已被污染
  document.body.appendChild(xss);
</script>
  1. jQuery 中的 XSS
// 不安全的jQuery用法
$("#container").html(userControlledInput);

// 安全用法
$("#container").text(userControlledInput);
  1. 动态脚本加载
// 不安全的动态脚本
const script = document.createElement("script");
script.src = untrustedURL;
document.body.appendChild(script);

安全替代方案:

// 使用import()实现动态加载
import(/* webpackIgnore: true */ trustedURL)
  .catch(() => { /* 错误处理 */ });

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌