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が必要なら保守されているサニタイザーを使い、サニタイズ結果があらゆる状況で安全だとは考えないでください。