XSS 対策とホワイトリスト方式

1. 背景と目的

HTML Agility Pack (HAP) は「ゆるい」HTML も含めて DOM として解析・操作できるため、ユーザ入力を表示する CMS や掲示板で多用されます。しかし、入力 HTML にスクリプトや危険な属性が混在すると クロスサイト・スクリプティング(XSS) が発生します。XSS を確実に防ぐためには、解析後に 危険要素を除去してから 出力する「クリーニングとサニタイズ」工程が必須です。ここで主流となるのが ホワイトリスト方式 です。ブラックリスト方式と異なり、「安全と確認できた要素 だけ を残す」ため漏れが起こりにくいのが利点です。en.wikipedia.org


2. ホワイトリスト方式の基本設計

項目 内容
対象 タグ名 / 属性名 / 属性値(プロトコル・正規表現)
動作 リスト外は まるごと削除 するか テキスト化 して無効化
追加ルール リンク要素の rel="noopener noreferrer" 付与iframe の sandbox 付与 など安全強制
  1. タグレベル検査

    • <script>, <object>, <embed>, <link> など実行系タグを全除去

  2. 属性レベル検査

    • すべての on*(イベント)属性、style 属性を基本的に禁止

    • href, src には javascript:, data:text/html など危険プロトコルを拒否

  3. ネスト・再帰検査

    • HTML を走査しながら 子孫ノード にも再帰的にルールを適用し、汚染の取りこぼしを防止

Stack Overflow などでも「タグ/属性がホワイトリストにない場合は再帰的に削除せよ」と議論されています。stackoverflow.com


3. HAP でホワイトリストを実装する 3 つのアプローチ

アプローチ 特徴 向く場面
① 手書き DOM 走査 HtmlDocument.DocumentNode.Descendants() を走査し自前で判定 ルールが極端に少ない小規模フォーム
② HtmlRuleSanitizer HAP の上に実装された ホワイトリスト専用サニタイザ。流通する HTML5 ルールを同梱し、タグ/属性/CSS クラス単位で宣言的設定が可能 コメント欄・リッチテキスト入力など汎用用途
③ MarkupSanity / Rule-Based Sanitizer ③も HAP ベースの派生ライブラリ。より細かい正規表現チェックや URL 妥当性検証を内蔵 URL やメディア埋め込みを厳格に検証したい場合

HtmlRuleSanitizer は NuGet で入手でき、標準で「安全な HTML5 サブセット」を用意しています。github.com


4. HtmlRuleSanitizer の具体例

csharp
using Vereyon.Web; // 1. 既定ルール(見出し・段落・画像・リンク程度を許可) var sanitizer = HtmlSanitizer.SimpleHtml5Sanitizer(); // 2. 追加で <code> タグを許可し、リンクの target を必ず _blank に sanitizer.Configure .AllowTag("code") // タグ許可 .AllowAttribute("a", "target") // 属性許可 .AddAttribute("a", "target", "_blank") // 強制付与 .AddUrlAttribute("img", "src"); // URL 妥当性検査付き属性 string clean = sanitizer.Sanitize(dirtyHtml);

ポイント

  • AllowTag / AllowAttribute明示的に許可を与える

  • AddUrlAttribute を使うと javascript: など 危険スキーム自動拒否

  • 未許可要素は内容も丸ごと除去するため、影響範囲が限定的


5. 手書き DOM 走査での実装勘所

  1. タグ判定

    csharp
    var allowedTags = new HashSet<string>{ "p","a","img","ul","li","strong","em" }; foreach (var node in doc.DocumentNode.Descendants().ToList()) { if (!allowedTags.Contains(node.Name)) node.Remove(); // ノード単位で除去 }
  2. 属性フィルタ

    • Uri.TryCreate(attr.Value, UriKind.Absolute, out var u) で http/https か確認

    • 正規表現 ^[-a-zA-Z0-9@:%_+.~#?&//=]{1,2048}$ で余計な文字を排除

  3. 多重デコード対策

    • HttpUtility.UrlDecode繰り返し 適用し、隠れた javascript: を検出

  4. HTML エンティティ化

    • どうしても残したい特殊文字は HtmlEntity.Entitize(text) で無害化


6. ルール更新と運用のベストプラクティス

項目 推奨策
ルール管理 変更点を JSON / YAML で外部化し、CI で単体テストを自動実行
安全デフォルト 新タグは「禁止」から入り、必要になった時のみ許可
ライブラリ更新 HAP・サニタイザを常に最新安定版へ。脆弱性勃発時に迅速更新
テスト 代表的な XSS ペイロード集 (OWASP) を毎回投入し 回帰試験

7. まとめ

  • ホワイトリスト方式 は「安全と確定した最小集合のみを許可」するため、XSS 封じ込めに適しています。

  • HAP 単体でも DOM 走査で実装できますが、HtmlRuleSanitizer など既成ライブラリを使うと宣言的に設定でき保守性が高まります。

  • いずれの手法でも タグ・属性・プロトコル の三層防御と、ルール外要素の 再帰削除 を徹底してください。

  • ルールのバージョン管理と自動テストを取り入れ、「安全デフォルト → 必要に応じて許可」の運用を行うことが長期的な安全性を担保します。

ChatGPT4o 生成日:2025/06/23