生成AIの回答をMarkdownからHTMLへ変換し、そのまま画面へ差し込む実装は危険です。LLMの出力は安全な文章とは限りません。利用者入力、RAG文書、ウェブページ、外部機能の結果を通じて、攻撃者が間接的に制御できる入力として扱います。本稿の対策はXSS経路を減らすもので、完全防止を保証しません。
OWASP GenAI Security Projectは、LLM出力を適切に検証・無害化せず後続処理へ渡す問題を「不適切な出力処理」と呼びます。ブラウザで解釈すれば、画面へ悪意あるスクリプトを混入させるXSSにつながります。サーバー側へ渡せば、そのサーバーから不正にアクセスさせるSSRFや、遠隔コード実行へ広がり得ます[1]。
Markdown解析器は安全境界ではない
MarkdownをHTMLへ変換できたことは、安全になったことを意味しません。生のHTMLを許す設定、javascript: URL、画像の外部読込、イベント処理属性、SVGやMathML、壊れた記述のブラウザ補正が攻撃面になります。
出力工程は、LLM文字列、Markdown解析、HTML無害化、DOM挿入の順に固定します。解析前の文字列置換だけで<script>を消しても、別のタグ、属性、URL、ブラウザ解析器の解釈を網羅できません。DOMPurifyは文字列の正規表現に頼らず、ブラウザと同じDOM解析器で記述を解釈し、許可一覧にない要素と属性を落とします[5]。
もっとも安全なのは、装飾文が不要な場所でHTMLを作らないことです。モデル名、状態、引用元、エラーなどはtextContent相当で表示します。Markdownが必要な本文だけ、許可するタグをp、ul、ol、li、strong、em、code、pre、blockquote、a程度へ絞ります。
無害化は表示先に合わせる
OWASPのXSS対策手引きは、HTML本文、属性、URL、JavaScript、CSSで必要な符号化が違うと説明します[2]。HTMLとして無害化した文字列を、style、スクリプト、属性、SVGへ再利用してはいけません。
DOMPurifyの脅威モデルも、HTML向けに無害化した出力を別の表示先へ移すと保護対象外になると明記します[6]。無害化後にリンクを書き換えると、検査後の内容が変わります。何度も加工せず、変換・無害化・挿入の責務を一つの部品へ閉じます。
サーバー側描画でも同じです。古いDOMライブラリや無害化処理を使えば、サーバーで処理したHTMLがブラウザで危険なDOMへ変わることがあります。無害化処理とDOM実装はセキュリティ上の依存部品として版を固定し、脆弱性情報を監視します。
リンクと画像は、安全なタグでも外部通信を起こす
<a>を許可する場合は、https:と必要な社内方式だけを許可し、javascript:、data:、未知の方式を拒否します。外部リンクには遷移先ドメインを表示し、新しいタブを開くならrel="noopener noreferrer"を付けます。LLMが表示文字列を「社内資料」と書いても、実URLが外部ドメインなら外部として扱います。
画像はXSSだけでなく、閲覧時の外部通信と追跡を起こします。AI回答内の外部画像を既定で読まない、許可ドメインを限定する、中継サーバーで取得して内容種別と容量を検査する、という選択肢があります。iframe、動画、音声、入力フォーム、style、SVG、MathMLは原則禁止し、必要性を説明できるものだけ許可します。
コードブロックは文字列として表示し、実行ボタンと分離します。AIが生成したシェル、SQL、HTML、JavaScriptを、確認表示のつもりで実行可能な隔離環境へ渡してはいけません。
Trusted Typesで危険な挿入先への抜け道を減らす
W3CのTrusted Typesは、innerHTML等の危険な挿入先で通常の文字列を拒み、規則が作った型付き値だけを渡すための仕様です[3]。MDNでは2026年2月から主要ブラウザでBaselineになったとされています[4]。
Content Security Policyでrequire-trusted-types-for 'script'を有効にし、無害化処理を通した箇所だけTrustedHTMLを作ります。すると生のLLM出力をinnerHTMLへ渡す事故をブラウザ側で止められます。最初は報告専用で違反箇所を集め、旧式の部品を洗い出してから強制へ移します。
ただしTrusted Typesは無害化処理の正しさを保証しません。危険な規則がすべての文字列を通せば防御にならず、未対応ブラウザも考慮が必要です。CSPもインラインスクリプトの一部を止める補助層であり、安全なDOM構築の代替ではありません。
五つの回帰試験を公開条件に置く
公開前には、次の五つを確認します。
- 生のHTMLを含むMarkdownが、許可タグだけへ縮むか。
javascript:、不正なdata:、プロトコル相対URLが拒否されるか。- SVG、MathML、template、壊れた入れ子が、実行可能なDOMにならないか。
- 無害化後の出力が、別の表示先へ渡らないか。
- 新しい部品でCSPやTrusted Typesの違反が増えていないか。
無害化処理自体も更新が必要です。DOMPurifyでは2026年にも特定構成の回避方法が脆弱性情報として修正されています[7]。一度の導入で安全とは言えません。版、設定、挿入先の組合せを試験します。
プロンプトインジェクション対策の後ろにも表示境界が要る
プロンプトインジェクションの検知や、RAG(検索した社内外文書を回答生成へ渡す仕組み)文書の信頼度判定が成功しても、LLM出力を無条件にHTMLとして扱ってよい理由にはなりません。モデルが自発的に危険な記述を返す可能性、解析器の差、後処理の不具合が残ります。
まず全出力を文字列で表示し、必要な画面だけMarkdownを許可します。許可するタグ、属性、URL方式を文書化し、無害化部品以外からHTMLの挿入先へ到達できないようにします。この小さな境界のほうが、禁止語を増やすより検証できます。
W3C Trusted TypesはWorking Draftです。採用時はブラウザ対応、無害化処理の版、実際の挿入先を再確認してください。
根拠資料
資料確認日:2026年9月28日。本文中の手順例は編集部の提案で、導入効果の実測値ではありません。

