スクリプト・スタイル削除ルールの概要

HTML を「クリーニング/サニタイズ」するうえで最優先となるのが <script><style> 要素、および JavaScript イベント属性 (onclick など) の排除 です。これらを無効化することで XSS や意図しないレイアウト崩れを防ぎ、取得したマークアップを安全に下流処理へ渡せます。


1. 削除対象の定義

種類 具体例 既定ルール 備考
スクリプト要素 <script>…</script><script src="…"> 必ず削除 src 属性の有無を問わない
スタイル要素 <style>…</style> 基本は削除
(ヘッダ内限定で許容する運用も可)
イベント属性 onclick, onload, onerror, … 必ず削除 HTML5 で定義される全 70 種前後が対象
style属性(インライン CSS) style="color:red" サイト方針に応じて
 - 全部削除
 - ホワイトリスト方式で一部残す
allowlist にはセレクタ/プロパティ単位で正規表現を用意

2. HtmlAgilityPack での実装ステップ

以下は 最もシンプルな「全部削除」実装 です(.NET 8 で動作確認)。

csharp
using HtmlAgilityPack; public static class HtmlCleaner { public static string RemoveScriptsAndStyles(string html, bool dropInlineStyle = true) { var doc = new HtmlDocument(); doc.LoadHtml(html); // ① <script> & <style> var nodes = doc.DocumentNode.SelectNodes("//script | //style"); if (nodes != null) { // 逆順ループで安全に削除 for (int i = nodes.Count - 1; i >= 0; i--) nodes[i].Remove(); // HtmlNode.Remove() :contentReference[oaicite:0]{index=0} } // ② イベント属性 & (任意で) style属性 foreach (var n in doc.DocumentNode.DescendantsAndSelf()) { // 後ろから削除しないと属性カウンタがずれる for (int i = n.Attributes.Count - 1; i >= 0; i--) { var attr = n.Attributes[i]; bool isEvent = attr.Name.StartsWith("on", StringComparison.OrdinalIgnoreCase); bool isStyle = dropInlineStyle && attr.Name.Equals("style", StringComparison.OrdinalIgnoreCase); if (isEvent || isStyle) n.Attributes.Remove(attr); // HtmlAttributeCollection.Remove(..) :contentReference[oaicite:1]{index=1} } } return doc.DocumentNode.OuterHtml; } }
  • XPath "//script | //style" は DOM を 1 回で走査できコストが低い
    (スクリプト削除コード例)htmlagilitypack.blogspot.com

  • .Attributes.Remove は引数無しなら「そのノードの全属性削除」、
    引数ありなら該当属性のみ削除できます。html-agility-pack.net


3. 高度なルール設定

シナリオ 実装アイディア
ヘッダ内の <style> だけ残したい CSS カスタマイズを許可する CMS //style[not(ancestor::head)] を削除対象に
CDN スクリプトを一部許可 cdnjs.cloudflare.com/vue.min.js <script> を全削除後に手動で再注入
または //script[not(@src='…')]
インライン style を部分許可 font-weight, color のみ保持 正規表現で style 属性値からホワイトリスト外プロパティを除去
既存ライブラリを活用 Vereyon/HtmlRuleSanitizer HAP 上にルールエンジンを重ね、安全プロファイルを JSON で管理 github.com

4. パフォーマンスと信頼性

  1. 逆順削除
    foreach で Remove すると内部インデックスが壊れ一部ノードが残存することがあるため、必ず for 逆順か .ToList() でコピー後に削除する。htmlagilitypack.blogspot.com

  2. 大規模文書対策

    • XPath を事前に HtmlNodeNavigator.Compile して再利用

    • 大量ページを並列処理する場合は Parallel.ForEach + スレッドローカル HtmlDocument

  3. ユニットテスト

    • xUnit + ApprovalTests で「危険要素が 0 件」であることを自動検証

    • 不正 HTML を含むケース(タグ未閉じなど)も含めて回帰テスト


5. まとめ

  • <script><style> は原則削除――最も簡単・安全なアプローチ。

  • イベント属性(onload など)は 属性レベルで必ず除去

  • どうしてもスタイルを残したい場合は ホワイトリスト方式 を徹底し、
    サニタイズ後に XSS 回帰テスト を必ず走らせる。

  • HtmlAgilityPack は「削除 API」自体はシンプルなので、ルール設計が肝
    運用ポリシーに合わせて XPath/正規表現/外部サニタイザを組み合わせることで、
    安全かつ保守性の高いクリーニングパイプラインを構築できます。

ChatGPT4o 生成日:2025/06/23