flawopen.com/JavaScript 中的 XSS/Vue v-html
Vue v-html 的 XSS 风险
参考页面
通俗解释
Vue 的 {{ comment }} 插值就像把便条隔着玻璃展示:尖括号仍是文本。v-html 则要求浏览器把便条作为 HTML 打开。如果标记由用户控制,浏览器可能会将其当作页面结构或活动内容,形成 XSS 风险。
有风险 — 用户提供的原始 HTML
<p v-html="comment"></p>
// comment 会被解析为实际标记
已修复 — 清理后的富 HTML
<script setup>
import { computed } from 'vue'
import DOMPurify from 'dompurify'
const props = defineProps({ comment: String })
const safeComment = computed(() =>
DOMPurify.sanitize(props.comment ?? '', {
ALLOWED_TAGS: ['p', 'strong', 'em'],
ALLOWED_ATTR: []
})
)
</script>
<template>
<div v-html="safeComment"></div>
</template>
在 v-html 前清理富 HTML
如果内容应作为文本显示,请优先使用 Vue 的 {{ }} 插值。如果产品功能确实需要用户编写的富 HTML,请使用 DOMPurify 等持续维护的库,并设置严格的允许列表;只将清理后的结果绑定到 v-html。保持清理器更新,并测试允许的标记策略。清理可以降低风险,但不能让任意 HTML 在所有上下文中都安全;如果活动内容必须保持不可信,请使用隔离的 iframe。Vue 不会把 v-html 内容编译为 Vue 模板。
审计 v-html 并追踪数据流
搜索所有 v-html 绑定。逐项确认数据来源、清理器及其配置,以及哪些用户能看到渲染结果。标记直接用户输入、存储的评论、CMS 字段和 API 响应;确认普通文本使用插值输出。
grep -rn "v-html" --include="*.vue" .常见问题
这和 React 的 dangerouslySetInnerHTML 风险相同吗?
两者都会绕过框架通常的文本转义路径,并将 HTML 插入 DOM。确需富 HTML 时使用持续维护的清理器,不要把清理结果视为在所有上下文中都安全。