FlawOpen

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 时使用持续维护的清理器,不要把清理结果视为在所有上下文中都安全。

参考资料

另请参阅 XSS 跨站脚本攻击(CWE-79):原理与各编程语言的防护方法 — flawopen.com WKWebView loadHTMLString 能安全显示不可信内容吗? 反射型、存储型与 DOM 型 XSS 有何区别? Android addJavascriptInterface 的安全风险 Angular XSS 防护:安全默认行为与绕过 API MIME 嗅探 XSS:Content-Type 与 nosniff CSP 与输出转义:两者都需要吗?