我々セキュリティプロフェッショナルにとって、インジェクション攻撃は常に最前線で対峙すべき脅威であり続けています。特にウェブアプリケーションにおいて、ユーザーからの信頼できない入力がHTMLとして処理される場面は枚挙にいとまがなく、その隙を突くクロスサイトスクリプティング(XSS)は、攻撃者にクライアントサイドでの任意コード実行を許す、極めて危険な脆弱性です。
世の中には様々なサニタイズライブラリが存在しますが、DOMPurifyはその堅牢性と信頼性から、多くのプロダクション環境で採用されています。しかし、「ライブラリを使えば安心」という安易な思考は、我々が最も警戒すべき「盲点」を生み出します。本稿では、DOMPurifyを単なるツールとしてではなく、多層防御アーキテクチャの一部として最大限に活用するための、深い洞察と実践的な設定戦略を提示します。
DOMPurifyの本質と、その防衛メカニズム
DOMPurifyは、信頼できないHTML、SVG、MathMLの文字列を受け取り、潜在的に悪意のあるコードを除去して、安全なHTML文字列を返すためのライブラリです。その内部動作は、正規表現ベースのサニタイザーとは一線を画します。DOMPurifyは、ブラウザのDOMパーサーを内部的に利用し、実際にDOMツリーを構築します。そして、構築されたDOMツリーを走査し、ホワイトリスト方式で定義された安全なタグや属性のみを許可し、それ以外を削除または無害化します。
この「DOMパーサーを利用する」という点が極めて重要です。なぜなら、攻撃者はブラウザのHTMLパーサーの挙動の差異や、エッジケースを突いてXSSペイロードを仕掛けます。正規表現ベースのサニタイザーでは、これらのパーシングの複雑性を完全に模倣することは困難であり、結果として正規表現の漏れや誤動作によるバイパスを許しがちです。DOMPurifyは、ブラウザ自身と同じロジックでDOMを解釈するため、この種のパーシングロジックの差異に起因する脆弱性を効果的に軽減できるのです。
しかし、この堅牢なメカニズムをもってしても、設定の誤りや、多層防御の欠如は、容易に防御壁を崩壊させます。
DOMPurifyの安全な設定とベストプラクティス
DOMPurifyのデフォルト設定は非常にセキュアですが、アプリケーションの要件によっては、デフォルトでは許可されない特定のHTML要素や属性を許容する必要が生じます。このカスタマイズこそが、攻撃者が狙う「設定ミス」の温床となり得ます。
1. デフォルト設定の理解と最小限のカスタマイズ
まず、DOMPurify.sanitize() を引数なしで実行した場合、どのような要素や属性が許可されるのかを理解することが出発点です。基本的には、テキスト装飾(, , )、リスト(
- ,
style属性の取り扱い: CSSは強力な表現力を持つ一方で、XSSの新たな経路となり得ます。expression()、url()内のjavascript:スキーム、CSSインジェクションによる情報漏洩など、攻撃ベクトルは多岐にわたります。もしstyle属性を許可する必要がある場合は、CSSサニタイザーを別途適用するか、style属性が適用される要素とそのプロパティを厳格にホワイトリストで管理することを検討してください。DOMPurify自体はstyle属性内の悪意あるCSSプロパティを検出しますが、完全に安全であるとは限りません。iframe/object/embedタグ: これらは外部コンテンツを埋め込むためのタグであり、サンドボックス化されていない限り、非常に危険です。信頼できないユーザー入力でこれらを許可することは、ほぼ常に避けるべきです。もし許可する必要がある場合は、sandbox属性を適切に設定し、allow-scriptsなどの権限を厳格に管理する必要があります。しかし、その設定も複雑であり、攻撃者はサンドボックスのバイパス手法を常に模索しています。data:スキーム:data:スキームは、HTML要素のsrcやhref属性などでインラインデータを埋め込むために使用されます。data:text/html,のような形でXSSペイロードを埋め込むことが可能です。ALLOW_DATA_ATTR: falseを設定することで、これを禁止できます。
,
)、リンク()、画像(![]()
)など、基本的なコンテンツ表示に必要な要素に限定されます。スクリプト実行につながる可能性のあるタグ(、
内の@import、、、など)や属性(onerror、onloadなどのイベントハンドラ、javascript:スキームのhrefやsrcなど)は、デフォルトで厳格に除去されます。
カスタマイズは、本当に必要なものに限定し、ホワイトリスト方式を徹底してください。
import DOMPurify from 'dompurify';
// 基本的なサニタイズ(デフォルト設定) const cleanHtml = DOMPurify.sanitize(dirtyHtml);
// 必要なオプションのみ追加する例 const cleanHtmlWithCustomOptions = DOMPurify.sanitize(dirtyHtml, { USE_PROFILES: { html: true, svg: false, mathMl: false }, // HTMLのみを処理対象とし、SVG/MathMLは明示的に無効化 FORBID_TAGS: ['style', 'iframe', 'script'], // デフォルトで禁止されているが、念のため明示的に禁止タグを指定 FORBID_ATTR: ['style', 'onerror', 'onload'], // デフォルトで禁止されているが、念のため明示的に禁止属性を指定 ADD_TAGS: ['details', 'summary'], // アコーディオン表示など、特定のUI要素を許可する場合 ADD_ATTR: ['id'], // 特定の属性(例: ID)を許可する場合 ALLOW_DATA_ATTR: false, // data-属性の使用を許可しない(デフォルトは許可) // target="_blank" のリンクに rel="noopener noreferrer" を自動追加(セキュリティとパフォーマンス向上のため) ADD_ATTR: ['target'], SET_ATTR: { target: '_blank', rel: 'noopener noreferrer' } });
2. FORBID_TAGS と FORBID_ATTR の活用
アプリケーションによっては、デフォルトで許可されている特定のタグや属性が不要、あるいはセキュリティ上のリスクとなる場合があります。例えば、ユーザーが投稿する内容に画像は不要だが、リンクは許可したい、といったケースです。
const cleanHtmlNoImages = DOMPurify.sanitize(dirtyHtml, { FORBID_TAGS: ['img'] // 画像タグを明示的に禁止 });
FORBID_ATTR は、style属性や各種イベントハンドラ (onload, onerror, onclickなど) を禁止するために不可欠です。DOMPurifyはこれらをデフォルトで禁止していますが、万が一のケースや、将来のバージョンアップでデフォルトが変わった場合に備え、特に注意すべき属性は明示的に禁止リストに加えることを推奨します。
3. ADD_TAGS と ADD_ATTR の慎重な利用
ここが最も攻撃者に狙われやすいポイントです。新しいタグや属性を追加する際は、そのタグや属性がどのようなセキュリティ上の影響を持つかを深く理解してください。
// 例えば、text-alignとcolorプロパティのみを許可するCSSサニタイザーを別途実装し、 // DOMPurifyのフックで適用する、といった高度な制御が必要になる。 // これはDOMPurifyの直接の範囲外であり、別途CSSパーサーとサニタイザーの実装が必要となる。
4. SVG/MathML プロファイルの理解と制御
DOMPurifyは、HTMLだけでなくSVGとMathMLのサニタイズもサポートしています。これらのXMLベースのマークアップ言語は、独自のスクリプト実行メカニズムやイベントハンドラを持っており、XSSの温床となり得ます。
デフォルトでは、HTMLプロファイルが有効化され、SVGとMathMLは無効化されています。もしユーザーがSVGやMathMLを投稿する可能性がある場合、USE_PROFILESオプションでこれらを有効にする必要があります。しかし、これを行う際は、それぞれの仕様に存在する攻撃ベクトルを深く理解し、適切なホワイトリストを構築しなければなりません。
// SVGを許可する場合の例(極めて慎重に行うべき)
const cleanSvg = DOMPurify.sanitize(dirtySvg, {
USE_PROFILES: { svg: true } // SVGプロファイルを有効化
// さらに、FORBID_TAGS や FORBID_ATTR でSVG固有の危険な要素/属性を禁止する
// 例:
コメント