所有分类
  • 所有分类
  • Html5资源
  • React资源
  • Vue资源
  • Php资源
  • ‌小程序资源
  • Python资源

 Vue安全神器!vue-dompurify-html让你的应用告别XSS攻击,一行代码解决HTML注入问题

Vue 开发中,我们经常需要动态渲染 HTML 内容,但使用v-html指令时存在 XSS 攻击风险。vue-dompurify-html 为我们提供了一个安全、优雅的解决方案。

什么是 vue-dompurify-html?

vue-dompurify-html 是一个基于 DOMPurify 的 Vue 插件,它为 Vue 应用提供了v-html指令的安全替代方案。通过 DOMPurify 的强大净化功能,它能在渲染 HTML 之前自动过滤掉恶意代码,确保应用安全。

核心特性

  • 安全可靠:基于 DOMPurify,业界认可的 HTML 净化库
  • 简单易用:零配置即可使用,API 友好
  • 高度可配置:支持自定义净化规则
  • 保持样式:智能保留安全的 CSS 样式
  • 轻量级:只是 DOMPurify 的简单包装器

安装配置

1. 安装依赖

# 使用npm
npm install vue-dompurify-html

# 使用yarn
yarn add vue-dompurify-html

# 使用pnpm
pnpm install vue-dompurify-html

2. 全局注册

在你的main.jsmain.ts中:

import { createApp } from "vue";
import App from "./App.vue";
import VueDOMPurifyHTML from "vue-dompurify-html";

const app = createApp(App);
app.use(VueDOMPurifyHTML);
app.mount("#app");

基础使用

简单使用

替换原来的v-html指令:

<template>
  <div>
    <!-- ❌ 不安全的方式 -->
    <div v-html="dangerousHtml"></div>

    <!-- ✅ 安全的方式 -->
    <div v-dompurify-html="safeHtml"></div>
  </div>
</template>

<script setup>
import { ref } from "vue";

const safeHtml = ref(`
    <h2 style="color: #4CAF50;">欢迎使用vue-dompurify-html</h2>
    <p>这段HTML已经被<strong>安全净化</strong>过了!</p>
    <${"script"}>alert('这段脚本会被自动移除')</${"script"}>`);
</script>

净化效果对比

输入 HTMLv-html 结果v-dompurify-html 结果
<script>alert('XSS')</script>🚨 执行脚本✅ 移除脚本标签
<img src="x" onerror="alert('XSS')">🚨 执行脚本✅ 移除事件处理器
<p style="color: red;">文本</p>✅ 正常显示✅ 正常显示

高级配置

命名配置

为不同场景创建专用配置:

// main.js
app.use(VueDOMPurifyHTML, {
  namedConfigurations: {
    // SVG配置
    svg: {
      USE_PROFILES: { svg: true },
    },
    // MathML配置
    mathml: {
      USE_PROFILES: { mathMl: true },
    },
    // 严格模式配置
    strict: {
      ALLOWED_TAGS: ["p", "b", "i", "em", "strong"],
      ALLOWED_ATTR: ["class"],
    },
    // 富文本编辑器配置
    editor: {
      ALLOWED_TAGS: [
        "h1",
        "h2",
        "h3",
        "h4",
        "h5",
        "h6",
        "p",
        "br",
        "strong",
        "em",
        "u",
        "s",
        "blockquote",
        "ul",
        "ol",
        "li",
        "a",
        "img",
        "table",
        "tr",
        "td",
        "th",
      ],
      ALLOWED_ATTR: ["href", "src", "alt", "title", "class", "style"],
    },
  },
});

在组件中使用命名配置:

<template>
  <div>
    <!-- 使用SVG配置 -->
    <div v-dompurify-html:svg="svgContent"></div>
    <hr class="my-8" />

    <!-- 使用严格配置 -->
    <div v-dompurify-html:strict="strictContent"></div>
    <hr class="my-8" />

    <!-- 使用富文本配置 -->
    <div v-dompurify-html:editor="editorContent"></div>
  </div>
</template>

<script setup>
import { ref } from "vue";

const svgContent = ref(`
  <svg width="200" height="150" viewBox="0 0 200 150">
    <rect x="10" y="10" height="50" width="100" fill="blue" stroke="black" stroke-width="2"></rect>
    <circle cx="150" cy="50" r="30" fill="red" stroke="black" stroke-width="2"></circle>
    <text x="20" y="100" font-family="Arial" font-size="14" fill="green">SVG 图形示例</text>
    <line x1="20" y1="120" x2="180" y2="120" stroke="purple" stroke-width="3"></line>
  </svg>
`);

const strictContent = ref(`
  <h3>严格模式内容</h3>
  <p class="highlight">只允许基本标签和安全的属性</p>
  <p>这是一个段落,包含<strong>粗体文本</strong>和<em>斜体文本</em></p>
  <p>支持换行<br>和基本格式</p>
  <div>这是一个div容器</div>
  <span>这是一个span元素</span>

  <!-- 以下标签在严格模式下会被过滤掉,不会显示 -->
  <iframe src="https://example.com"></iframe>
  <object data="malicious.swf"></object>
  <embed src="malicious.swf">
  <form action="https://evil.com" method="post">
    <input type="text" name="password" value="secret">
    <button type="submit">提交</button>
  </form>
  <img src="x" onerror="alert('XSS攻击')">
  <a href="javascript:alert('XSS')">危险链接</a>
  <style>body { background: red; }</style>
  <link rel="stylesheet" href="malicious.css">
  <meta http-equiv="refresh" content="0;url=https://evil.com">
  <video controls>
    <source src="malicious.mp4" type="video/mp4">
  </video>
  <audio controls>
    <source src="malicious.mp3" type="audio/mpeg">
  </audio>
  <details>
    <summary>点击展开</summary>
    <p>这个details标签可能被过滤</p>
  </details>
  <canvas id="myCanvas" width="200" height="100"></canvas>
  <svg onload="alert('SVG XSS')">
    <rect width="100" height="50" fill="red"/>
  </svg>
  <input type="file" accept=".exe">
  <textarea placeholder="这个textarea可能被过滤"></textarea>
  <select>
    <option value="1">选项1</option>
    <option value="2">选项2</option>
  </select>

  <p>注意:上面的危险标签在严格模式下都会被安全过滤,只显示安全的文本内容。</p>
`);

const editorContent = ref(`
  <h1>富文本编辑器内容</h1>
  <h2>二级标题</h2>
  <h3>三级标题</h3>

  <p>这是一个包含丰富格式的段落,支持<strong>粗体</strong>、<em>斜体</em>、<u>下划线</u>和<code>代码</code>。</p>

  <p>还可以包含<a href="https://example.com" target="_blank">链接</a>和<span style="color: red; background-color: yellow;">带样式的文本</span>。</p>

  <blockquote>
    <p>这是一个引用块,用于突出显示重要内容。</p>
  </blockquote>

  <ul>
    <li>无序列表项1</li>
    <li>无序列表项2</li>
    <li>无序列表项3</li>
  </ul>

  <ol>
    <li>有序列表项1</li>
    <li>有序列表项2</li>
    <li>有序列表项3</li>
  </ol>

  <table border="1" style="border-collapse: collapse; width: 100%;">
    <thead>
      <tr>
        <th>姓名</th>
        <th>年龄</th>
        <th>职业</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>张三</td>
        <td>25</td>
        <td>工程师</td>
      </tr>
      <tr>
        <td>李四</td>
        <td>30</td>
        <td>设计师</td>
      </tr>
    </tbody>
  </table>

  <hr>

  <p>最后,我们还可以包含<sub>下标</sub>和<sup>上标</sup>文本。</p>
`);
</script>

默认配置

设置全局默认净化规则:

app.use(VueDOMPurifyHTML, {
  default: {
    // 允许的标签
    ALLOWED_TAGS: [
      "div",
      "span",
      "p",
      "br",
      "strong",
      "em",
      "u",
      "h1",
      "h2",
      "h3",
      "h4",
      "h5",
      "h6",
      "ul",
      "ol",
      "li",
      "a",
      "img",
    ],
    // 允许的属性
    ALLOWED_ATTR: ["href", "src", "alt", "title", "class", "style"],
    // 保持相对URL
    KEEP_CONTENT: false,
    // 移除空属性
    SANITIZE_NAMED_PROPS: true,
  },
});

钩子函数

通过钩子函数实现自定义处理逻辑:

app.use(VueDOMPurifyHTML, {
  hooks: {
    // 在净化元素时执行
    uponSanitizeElement: (currentNode, data, config) => {
      // 为所有链接添加target="_blank"
      if (currentNode.tagName === "A") {
        currentNode.setAttribute("target", "_blank");
        currentNode.setAttribute("rel", "noopener noreferrer");
      }
    },
    // 在净化属性时执行
    uponSanitizeAttribute: (currentNode, hookEvent, config) => {
      // 确保所有图片都有alt属性
      if (currentNode.tagName === "IMG" && !currentNode.hasAttribute("alt")) {
        currentNode.setAttribute("alt", "图片描述");
      }
    },
    // 在净化完成后执行
    afterSanitizeElements: (currentNode, data, config) => {
      // 为代码块添加语法高亮class
      if (currentNode.tagName === "CODE") {
        currentNode.classList.add("syntax-highlight");
      }
    },
  },
});

局部使用

如果不想全局注册,也可以在组件中局部使用:

<template>
  <div v-dompurify-html="rawHtml"></div>
</template>

<script setup>
import { buildVueDompurifyHTMLDirective } from "vue-dompurify-html";

// 创建局部指令
const vDompurifyHtml = buildVueDompurifyHTMLDirective({
  ALLOWED_TAGS: ["p", "strong", "em"],
  ALLOWED_ATTR: ["class"],
});

const rawHtml = "<p><strong>局部使用示例</strong></p>";
</script>

注意事项

1. 版本兼容性

  • Vue 3.x:使用最新版本
  • Vue 2.x:使用 4.1.x 版本

2. 安全提醒

// ❌ 错误:过度信任配置
app.use(VueDOMPurifyHTML, {
  default: {
    ALLOWED_TAGS: ["script"], // 危险!
    SANITIZE: false, // 危险!
  },
});

// ✅ 正确:保持默认安全配置
app.use(VueDOMPurifyHTML, {
  default: {
    // 只添加必要的标签和属性
    ALLOWED_TAGS: ["p", "strong", "em"],
    ALLOWED_ATTR: ["class"],
  },
});

3. 性能考虑

  • 避免在循环中使用大量 HTML 净化
  • 对于静态内容,考虑服务端预处理
  • 使用 Vue 的计算属性缓存净化结果

总结

vue-dompurify-html 为 Vue 应用提供了一个完美的 XSS 防护解决方案:

  • 安全第一:基于业界认可的 DOMPurify
  • 简单易用:替换 v-html 即可使用
  • 高度可配置:满足各种业务场景
  • 性能优秀:轻量级,运行高效
  • 社区活跃:持续更新维护

在现代 Web 开发中,安全性不容忽视。使用 vue-dompurify-html,让你的 Vue 应用在享受动态 HTML 便利的同时,也能保持最高的安全标准。

原文链接:https://code.ifrontend.net/archives/1306,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?