DOM汚染の入り口:Vue.js v-html がもたらす「無防備な」レンダリングの罠
フロントエンド開発の現場で、マークダウン記法のレンダリングやCMSからのリッチテキストを扱う際、つい安易に v-html を使っていないだろうか。セキュリティアーキテクトの視点から言わせてもらえば、v-html は「ブラウザの実行エンジンに対して、信頼できない入力を直結させるショートカット」に他ならない。
多くの開発者は、これを単なる「HTMLを文字列として展開する機能」と捉えているが、本質は違う。これは「攻撃者がブラウザのDOMツリーを直接操作し、XSS(クロスサイトスクリプティング)ペイロードを注入するための高速道路」だ。
1. v-html が引き起こすメモリ破壊と攻撃のメカニズム
なぜ v-html が危険なのか。その根本的な理由は、Vue.jsのリアクティブシステムが、その入力値をテンプレートコンパイルの過程で「静的なデータ」として保護せず、解析された文字列をそのまま innerHTML プロパティに代入するからだ。
ここで何が起きているか。ブラウザのレンダリングエンジンは、innerHTML に流し込まれた文字列をパースする際、HTMLタグだけでなく、中に含まれる javascript: 疑似プロトコルや、onerror などのイベントハンドラをDOMノードの一部としてメモリ上に展開する。
攻撃者はこれを利用し、以下のようなペイロードを注入する。
この文字列がサニタイズされずに v-html へ渡されると、ブラウザは「画像読み込み失敗」のイベントをトリガーとして、攻撃者のエンドポイントへ機密データを送信するスクリプトを実行してしまう。これは単なるスクリプト実行ではない。ブラウザのサンドボックス内における「特権昇格」の第一歩なのだ。
2. DOMPurifyによる防衛の「ガードレイル」設計
では、どう守るか。ここで「自作の置換関数」などで対処しようとするエンジニアがいれば、今すぐ止めるべきだ。HTMLの仕様は複雑怪奇であり、エンコーディングの不備や、ブラウザごとの解釈の揺らぎ(Mutation XSSなど)を考慮し切ることは不可能に近い。
ここで唯一の選択肢となるのが、DOMPurify の導入である。DOMPurifyは、ブラウザがサポートするDOM Parserを用いて一度パースし、ホワイトリストベースで安全なタグと属性のみを抽出する「強力なフィルタ」として機能する。
実装サンプル:Vue.jsでの安全なラッパーコンポーネント
v-html を直接使うのではなく、サニタイズを強制するラッパーを定義する。
import DOMPurify from ‘dompurify’;
/
- 構成管理:サニタイズ設定を集中管理する
- 必要に応じて、特定のカスタム要素のみ許可するなど、厳格なポリシーを適用する
/
const sanitizeOptions = {
ALLOWED_TAGS: [‘b’, ‘i’, ‘em’, ‘strong’, ‘a’, ‘p’], // 最小限の許可リスト
ALLOWED_ATTR: [‘href’, ‘title’],
RETURN_DOM_FRAGMENT: false,
RETURN_TRUSTED_TYPE: true // Trusted Types API対応:ブラウザ側の防御層を活用
};
// コンポーネント内の計算プロパティでサニタイズを実行
export default {
props: [‘rawHtml’],
computed: {
sanitizedHtml() {
// DOMPurifyで汚染された文字列を浄化する
return DOMPurify.sanitize(this.rawHtml, sanitizeOptions);
}
}
};
3. 次世代の防衛層:Trusted Types API への視座
我々セキュリティアーキテクトが次に注視すべきは、Trusted Types API だ。これは、DOMの「シンク(危険な実行点)」に代入できる型を厳格に制限するブラウザの機能である。
現在、v-html は文字列をそのまま受け入れるが、Trusted Typesを有効化すれば、定義されたポリシーを通らない文字列の代入をブラウザ自体がブロックするようになる。
- 防御の多層化:
1. 開発時: 静的解析(ESLint等)で v-html の使用を禁止する。
2. 実行時: DOMPurifyによるサニタイズを必須化する。
3. ブラウザ側: Content-Security-Policy: require-trusted-types-for 'script'; を設定し、不当なDOM操作をハードウェア/ブラウザレベルで遮断する。
結論:技術の盲点を突くのが我々の仕事
セキュリティにおいて「絶対に安全」という言葉は存在しない。生成AI時代の今、プロンプトインジェクションによって生成されたHTMLが、意図せずアプリケーションの v-html に流れ込むリスクも考慮しなければならない。
v-html を使うという判断は、「私はブラウザのセキュリティ境界を一時的に無効化する」という宣言と同義だ。その重みを理解し、サニタイズを単なる機能実装ではなく、システムを守るための「ガードレイル」として設計せよ。
コードは嘘をつかない。しかし、脆弱性は常に、油断した開発者の「少しだけなら大丈夫だろう」という甘い推測の中に潜んでいる。
コメント