1. 導入
この節は規範ではありません。
Web アプリケーションはクライアント側で HTML 文字列を扱う必要がしばしばあります。
これはクライアントサイドのテンプレート ソリューションの一部であったり、ユーザー生成コンテンツのレンダリングの一部であったりします。これを安全に行うのは難しいです。
文字列を安易に連結して
Element
の
innerHTML
に流し込む素朴な手法は、多くの予期しない形で JavaScript の実行を引き起こし得るため、危険が伴います。
[DOMPURIFY] のようなライブラリは、 挿入前に文字列を注意深く解析・サニタイズし、DOM を構築して許可リストを通してその構成要素をフィルタリングすることで、この問題に対処しようとします。 しかしこれは脆いアプローチであることが分かっています。というのも、Web に公開されている解析 API は、実際に 文字列を「実際の」DOM としてレンダリングする際のブラウザーの挙動と、必ずしも合理的に一致しないからです。 さらに、ライブラリは時間とともに変化するブラウザーの挙動を追い続ける必要があります。かつて安全だったものが、 プラットフォームレベルの新機能によって時限爆弾になってしまうこともあります。
ブラウザーは、自身がいつコードを実行するかについてかなり把握しています。任意の文字列から HTML を安全にレンダリングする方法を ブラウザー自身に教えることで、ユーザー空間のライブラリよりも改善できます。そうすれば、ブラウザーのパーサー実装の変更に合わせて 保守・更新される可能性もはるかに高くなります。本書は、そのための API を概説します。
1.1. 目標
-
ユーザー制御の HTML を安全に扱うメカニズムを開発者に提供し、注入時にスクリプトが直接実行されることを防ぐことで、 DOM ベースのクロスサイトスクリプティング攻撃のリスクを軽減する。
-
現在のユーザーエージェントの HTML 理解を考慮に入れ、その環境で安全に使用できる HTML 出力を実現する。
-
開発者が既定の要素および属性のセットを上書きできるようにする。 特定の要素や属性を追加することで、 スクリプト・ガジェット 攻撃を防げる場合がある。
1.2. API 概要
Sanitizer API は、HTML を含む文字列を DOM ツリーに解析し、結果のツリーをユーザー指定の構成に従って フィルタリングする機能を提供します。メソッドは 2 つの観点で用意されています:
-
Safe and unsafe: 「safe」なメソッドは、スクリプトを実行するマークアップを生成しません。つまり、XSS に対して安全であるはずです。 「unsafe」なメソッドは、与えられたものを解析しフィルタリングするだけです。 参照: § 4 セキュリティに関する考慮事項。
-
コンテキスト: メソッドは
ElementおよびShadowRootに定義され、これらのNodeの子を置き換えます。これは概ねinnerHTMLに相当します。さらにDocument上の静的メソッドもあり、 文書全体を解析し、概ねDOMParser.parseFromString()に相当します。
2. フレームワーク
2.1. サニタイザー API
Element
インターフェイスは、setHTML()
と
setHTMLUnsafe()
の 2 つのメソッドを定義します。いずれも HTML マークアップを含む DOMString
と、任意の構成を受け取ります。
partial interface Element { [CEReactions ]undefined setHTMLUnsafe ((TrustedHTML or DOMString ),html optional SetHTMLUnsafeOptions = {}); };options
partial interface Element { [CEReactions ]undefined setHTML (DOMString ,html optional SetHTMLOptions = {}); };options
Element
の
setHTMLUnsafe(html, options)
メソッドの手順は次のとおりです:
-
compliantHTML を、get trusted type compliant string アルゴリズムに
TrustedHTML、 this の 関連グローバルオブジェクト、html、"Element setHTMLUnsafe"、および "script" を与えて呼び出した結果とする。 -
target を、this が
template要素である場合はその template contents、そうでない場合は this とする。 -
Set and filter HTML を、target、this、 compliantHTML、options、false を与えて実行する。
Element
の
setHTML(html, options)
メソッドの手順は次のとおりです:
-
target を、this が
templateの場合はその template contents、 そうでない場合は this とする。 -
Set and filter HTML を、target、this、html、 options、true を与えて実行する。
partial interface ShadowRoot { [CEReactions ]undefined setHTMLUnsafe ((TrustedHTML or DOMString ),html optional SetHTMLUnsafeOptions = {}); };options
partial interface ShadowRoot { [CEReactions ]undefined setHTML (DOMString ,html optional SetHTMLOptions = {}); };options
これらのメソッドは ShadowRoot
にも反映されています:
ShadowRoot
の
setHTMLUnsafe(html, options)
メソッドの手順は次のとおりです:
-
compliantHTML を、get trusted type compliant string アルゴリズムに、
TrustedHTML、 this の 関連グローバルオブジェクト、html、"ShadowRoot setHTMLUnsafe"、および "script" を与えて呼び出した結果とする。 -
Set and filter HTML を this、 this の shadow host(コンテキスト要素として)、 compliantHTML、options、false を与えて実行する。
ShadowRoot
の
setHTML(html, options)
メソッドの手順は次のとおりです:
-
Set and filter HTML を、this(target として)、this(コンテキスト 要素として)、 html、options、true を与えて実行する。
Document
インターフェイスは、文書全体を解析する 2 つの新しいメソッドを得ます:
partial interface Document {static Document parseHTMLUnsafe ((TrustedHTML or DOMString ),html optional SetHTMLUnsafeOptions = {}); };options
partial interface Document {static Document parseHTML (DOMString ,html optional SetHTMLOptions = {}); };options
parseHTMLUnsafe(html, options)
メソッドの手順は次のとおりです:
-
compliantHTML を、get trusted type compliant string アルゴリズムに、
TrustedHTML、 現在のグローバルオブジェクト、html、"Document parseHTMLUnsafe"、および "script" を与えて呼び出した結果とする。 -
document を新しい
Documentとし、 その content type は "text/html" とする。注: document には browsing context がないため、 スクリプティングは無効です。
-
document の allow declarative shadow roots を true に設定する。
-
Parse HTML from a string を document と compliantHTML を与えて実行する。
-
sanitizer を、get a sanitizer instance from options を options と false を与えて呼び出した結果とする。
-
sanitize を document に対して sanitizer と false を与えて呼び出す。
-
document を返す。
parseHTML(html, options)
メソッドの手順は次のとおりです:
-
document を新しい
Documentとし、 その content type は "text/html" とする。注: document には browsing context がないため、 スクリプティングは無効です。
-
document の allow declarative shadow roots を true に設定する。
-
Parse HTML from a string を document と html を与えて実行する。
-
sanitizer を、get a sanitizer instance from options を options と true を与えて呼び出した結果とする。
-
sanitize を document に対して sanitizer と true を与えて呼び出す。
-
document を返す。
2.2. SetHTML のオプションと構成オブジェクト。
setHTML()
系の
メソッドはすべて options ディクショナリを受け取ります。現時点では、このディクショナリのメンバーは 1 つだけ定義されています:
enum {SanitizerPresets };"default" dictionary { (SetHTMLOptions Sanitizer or SanitizerConfig or SanitizerPresets )= "default"; };sanitizer dictionary { (SetHTMLUnsafeOptions Sanitizer or SanitizerConfig or SanitizerPresets )= {}; };sanitizer
Sanitizer
構成オブジェクトは、フィルター構成をカプセル化します。
同じ構成は "safe" または "unsafe"
の両方のメソッドで使用でき、「safe」メソッドは渡された構成に対して暗黙に
removeUnsafe
を行い、構成が渡されない場合は既定の構成を用います。既定は「safe」と「unsafe」で異なります:
「safe」メソッドは既定で安全であることを目指し制限的で、「unsafe」メソッドは既定で無制限です。
構成の利用意図としては、ページのライフタイムの早い段階で 1 つ(または少数)の構成を作成し、必要に応じて再利用することです。これにより実装は構成を前処理できます。
構成オブジェクトは、構成ディクショナリを返すようにクエリできます。また、直接変更することもできます。
[Exposed =Window ]interface {Sanitizer constructor (optional (SanitizerConfig or SanitizerPresets )= "default"); // Query configuration:configuration SanitizerConfig get (); // Modify a Sanitizer's lists and fields:boolean allowElement (SanitizerElementWithAttributes );element boolean removeElement (SanitizerElement );element boolean replaceElementWithChildren (SanitizerElement );element boolean allowProcessingInstruction (SanitizerPI );pi boolean removeProcessingInstruction (SanitizerPI );pi boolean allowAttribute (SanitizerAttribute );attribute boolean removeAttribute (SanitizerAttribute );attribute boolean setComments (boolean );allow boolean setDataAttributes (boolean ); // Remove markup that executes script.allow boolean removeUnsafe (); };
Sanitizer
には対応する SanitizerConfig
の
configuration が関連付けられています。
constructor(configuration)
メソッドの手順は次のとおりです:
-
configuration が
SanitizerPresetsの string である場合:-
configuration を built-in safe default configuration に設定する。
-
valid を、set a configuration を configuration と true を与えて this に対して実行した戻り値とする。
-
valid が false の場合、
TypeErrorを投げる。
get() メソッドの手順は以下のとおりです:
-
もし config["
elements"] が存在する場合:-
すべての config["
elements"] の element に対して:-
もし element["
attributes"] が存在する場合:-
element["
attributes"] を、昇順でソートする element["attributes"] の結果で設定し、attrA が 小さい項目として attrB と比較する。
-
-
もし element["
removeAttributes"] が存在する場合:-
element["
removeAttributes"] を、昇順でソートする element["removeAttributes"] の結果で設定し、attrA が 小さい項目とし attrB と比較する。
-
-
-
config["
elements"] を、昇順でソートする config["elements"] の結果で設定し、elementA が 小さい項目として elementB と比較する。
-
-
その他の場合:
-
config["
removeElements"] を、昇順でソートする config["removeElements"] の結果で設定し、elementA が 小さい項目として elementB と比較する。
-
-
もし config["
replaceWithChildrenElements"] が存在する場合:-
config["
replaceWithChildrenElements"] を、昇順でソートする config["replaceWithChildrenElements"] の結果で設定し、elementA が 小さい項目として elementB と比較する。
-
-
もし config["
processingInstructions"] が存在する場合:-
config["
processingInstructions"] を、昇順でソートする config["processingInstructions"] の結果で設定し、piA["target"] が コード単位比較でより小さい piB["target"] である。
-
-
その他の場合:
-
config["
removeProcessingInstructions"] を、昇順でソートする config["removeProcessingInstructions"] の結果で設定し、piA["target"] が コード単位比較でより小さい piB["target"] である。
-
-
もし config["
attributes"] が存在する場合:-
config["
attributes"] を、昇順でソートする config["attributes"] の結果で設定し、attrA が 小さい項目として attrB と比較する。
-
-
その他の場合:
-
config["
removeAttributes"] を、昇順でソートする config["removeAttributes"] の結果で設定し、attrA が 小さい項目として attrB と比較する。
-
-
config を返す。
allowElement(element) メソッドの手順は以下のとおりです:
-
グローバルな許可・除外リストがあるかどうか、
-
これらのリストに element がすでに含まれているかどうか、
-
element を サニタイザー要素と属性を正規化する の 結果にする。
-
もし configuration["
elements"] が存在する場合:-
modified を remove element を configuration["
replaceWithChildrenElements"] から実行した結果とする。 -
コメント: 個別要素の属性がグローバル属性と重複しないようにする必要があります。
-
もし configuration["
attributes"] が存在する場合:-
もし element["
attributes"] が存在する場合:-
element["
attributes"] を重複削除した element["attributes"] に設定する。 -
element["
attributes"] を 差集合 element["attributes"] と configuration["attributes"] の結果に設定する。 -
もし configuration["
dataAttributes"] が true の場合:-
すべての item を element["
attributes"] から削除する(itemが カスタムデータ属性の場合)。
-
-
-
もし element["
removeAttributes"] が存在する場合:-
element["
removeAttributes"] を重複削除した element["removeAttributes"] に設定する。 -
element["
removeAttributes"] を 共通集合 element["removeAttributes"] と configuration["attributes"] の結果に設定する。
-
-
-
その他の場合:
-
もし element["
attributes"] が存在する場合:-
element["
attributes"] を重複削除した element["attributes"] に設定する。 -
element["
attributes"] を、 差集合 element["attributes"] と element["removeAttributes"] (デフォルト« »付き) の結果に設定する。 -
削除 element["
removeAttributes"]。 -
element["
attributes"] を 差集合 element["attributes"] と configuration["removeAttributes"] の結果に設定する。
-
-
もし element["
removeAttributes"] が存在する場合:-
element["
removeAttributes"] を重複削除した element["removeAttributes"] に設定する。 -
element["
removeAttributes"] を 差集合 element["removeAttributes"] と configuration["removeAttributes"] の結果に設定する。
-
-
-
コメント: これは、グローバル許可リストにすでに element が存在する場合です。
-
current element を configuration["
elements"] 内で item["name"] が element["name"] と等しく、かつ item["namespace"] が element["namespace"] と等しい item とする。 -
もし element が 等しい current element なら modified を返す。
-
trueを返す。
-
-
その他の場合:
-
もし element["
attributes"] が存在するまたは element["removeAttributes"] (デフォルト« »付き) が 空でない場合:-
ユーザーエージェントは、この操作がサポートされていない旨を コンソールに警告出力してもよい。
-
falseを返す。
-
-
modified を remove element を configuration["
replaceWithChildrenElements"] から実行した結果とする。 -
もし configuration["
removeElements"] が 含まれていない element の場合:-
コメント: これはグローバル除外リストに element が含まれていない場合です。
-
modified を返す。
-
-
コメント: これは、グローバル除外リストに element が含まれている場合です。
-
削除 element を configuration["
removeElements"] から。 -
trueを返す。
-
replaceElementWithChildren(element)
メソッドの手順は次のとおりです:
-
configurationをthisのconfigurationとする。
-
elementに、サニタイザー要素を正規化するの結果(elementを用いて)を設定する。
-
もし組み込みの置換不可要素リストが含むなら、elementを:
-
falseを返す。
-
-
もしconfiguration["
replaceWithChildrenElements"] が含むなら、elementを:-
falseを返す。
-
-
削除:elementを configuration["
removeElements"] から。 -
追加: elementをconfiguration["
replaceWithChildrenElements"] へ。 -
trueを返す。
allowProcessingInstruction(pi)
メソッドの手順は以下の通りです:
-
pi を サニタイザー処理命令を正規化するの結果に設定する。
-
もし configuration["
processingInstructions"] が存在する場合:-
もし configuration["
processingInstructions"] が pi を含む場合:-
false を返す。
-
-
追加 pi を configuration["
processingInstructions"] に。 -
true を返す。
-
-
その他の場合:
-
もし configuration["
removeProcessingInstructions"] が pi を含む場合:-
削除 "
target" が pi["target"] に等しい項目を configuration["removeProcessingInstructions"] から。 -
true を返す。
-
-
false を返す。
-
removeProcessingInstruction(pi)
メソッドの手順は以下の通りです:
-
pi を サニタイザー処理命令を正規化するの結果に設定する。
-
もし configuration["
processingInstructions"] が存在する場合:-
もし configuration["
processingInstructions"] が pi を含む場合:-
削除 "
target" が pi["target"] に等しい項目を configuration["processingInstructions"] から。 -
true を返す。
-
-
false を返す。
-
-
その他の場合:
-
もし configuration["
removeProcessingInstructions"] が pi を含む場合:-
false を返す。
-
-
追加 pi を configuration["
removeProcessingInstructions"] に。 -
true を返す。
-
allowAttribute(attribute)
メソッドの手順は以下の通りです:
-
attribute を サニタイザー属性を正規化する の結果に設定する。
-
もし configuration["
attributes"] が存在する場合:-
コメント: グローバル許可リストがある場合、attribute を追加する必要がある。
-
もし configuration["
dataAttributes"] が true かつ attribute が カスタムデータ属性 である場合、false を返す。 -
もし configuration["
attributes"] が attribute を含む場合 false を返す。 -
コメント: 個別要素ごとに許可・除外リストの調整。
-
もし configuration["
elements"] が存在する場合:-
各 configuration["
elements"] の element について:-
もし element["
attributes"] (デフォルト « » 付きで) が attribute を含む場合:-
削除 attribute を element["
attributes"] から。
-
-
断言: element["
removeAttributes"] (デフォルト « » 付きで) が attribute を含まない。
-
-
-
追加 attribute を configuration["
attributes"] に。 -
true を返す。
-
-
その他の場合:
-
コメント: グローバル除外リストがある場合は attribute を除外する。
-
もし configuration["
removeAttributes"] が attribute を含まない場合:-
false を返す。
-
-
削除 attribute を configuration["
removeAttributes"] から。 -
true を返す。
-
setComments(allow) メソッドの手順は以下の通りです:
setDataAttributes(allow)
メソッドの手順は以下の通りです:
-
もし configuration["
attributes"] が存在しない場合、false を返す。 -
もし configuration["
dataAttributes"] が allow と等しい場合、false を返す。 -
もし allow が true の場合:
-
削除 configuration["
attributes"] から attr が カスタムデータ属性 であるすべての項目を。 -
もし configuration["
elements"] が存在する場合:-
各 configuration["
elements"] の element について:-
もし element["
attributes"] が存在する場合:-
削除 element["
attributes"] から attr が カスタムデータ属性 であるすべての項目を。
-
-
-
-
-
configuration["
dataAttributes"] を allow に設定する。 -
true を返す。
2.3. 構成ディクショナリ
dictionary {SanitizerElementNamespace required DOMString ;name DOMString ?= "http://www.w3.org/1999/xhtml"; }; // Used by "elements"_namespace dictionary :SanitizerElementNamespaceWithAttributes SanitizerElementNamespace {sequence <SanitizerAttribute >;attributes sequence <SanitizerAttribute >; };removeAttributes typedef (DOMString or SanitizerElementNamespace );SanitizerElement typedef (DOMString or SanitizerElementNamespaceWithAttributes );SanitizerElementWithAttributes dictionary {SanitizerProcessingInstruction required DOMString ; };target typedef (DOMString or SanitizerProcessingInstruction );SanitizerPI dictionary {SanitizerAttributeNamespace required DOMString ;name DOMString ?=_namespace null ; };typedef (DOMString or SanitizerAttributeNamespace );SanitizerAttribute dictionary {SanitizerConfig sequence <SanitizerElementWithAttributes >;elements sequence <SanitizerElement >;removeElements sequence <SanitizerElement >;replaceWithChildrenElements sequence <SanitizerPI >;processingInstructions sequence <SanitizerPI >;removeProcessingInstructions sequence <SanitizerAttribute >;attributes sequence <SanitizerAttribute >;removeAttributes boolean ;comments boolean ; };dataAttributes
2.4. 構成の不変条件
構成(configuration)は、開発者が自分の目的に合わせて修正することができ、また修正すべきものです。選択肢としては、新しく構成ディクショナリを一から書く、既存のSanitizerの構成を
修正用メソッドで変更する、あるいは既存のget()で
Sanitizerの構成を辞書として取得し、その辞書を編集して新しいSanitizerを作る、などがあります。
空の構成は(setHTMLUnsafe
のような "unsafe" メソッドで呼び出した場合に)すべてを許可します。
"default" 構成は組み込みの安全な既定構成を含みます。なお、「safe」と「unsafe」のサニタイザーメソッドは既定値が異なります。
すべての構成ディクショナリが有効とは限りません。有効な構成は、冗長(同じ要素を二重に許可するなど)や矛盾(同じ要素を削除と許可の両方に指定するなど)を避けます。
構成が有効であるためには、いくつかの条件を満たす必要があります:
-
グローバル許可リスト・削除リストを混在させる場合:
-
elementsまたはremoveElementsは存在できるが、両方は不可。 両方が存在しない場合、これはremoveElementsset to « » と同等である。 -
attributesまたはremoveAttributesは存在できるが、両方は不可。 両方が存在しない場合、これはremoveAttributesset to « » と同等である。 -
dataAttributesは概念的にattributes許可リストの拡張である。dataAttributes属性はattributesリストが使われているときのみ許可される。
-
-
異なるグローバルリスト間の重複:
-
elements,removeElements,replaceWithChildrenElements間で重複(同じ要素)は存在しない。 -
attributesまたはremoveAttributes間で重複(同じ属性)は存在しない。
-
-
同じ要素に対するローカル許可/削除リスト混在:
-
attributesリストが存在する場合、attributes、removeAttributesの両方、いずれか一方、もしくはどちらも許可される。 -
removeAttributesリストが存在する場合、attributesかremoveAttributesのいずれか一方もしくはどちらも許可だが、両方は不可。
-
-
同じ要素内での重複:
-
同じ要素内で
attributesとremoveAttributesの間で重複は存在しない。
-
-
組み込みの置換不可要素リスト のいずれの要素も
replaceWithChildrenElementsに現れない。なぜなら、これらの要素を子要素で置き換えると再パース問題や無効なノードツリーにつながることがあるためである。
elements
の要素許可リストは、特定の要素に対して、属性を許可または除去することも指定できる。これは [HTML] の構造(特定の要素に適用されるローカルな attributes と、
global attributes の両方を持つ)を反映することを意図している。グローバル属性とローカル属性は混在可能だが、
ある属性が一方のリストで許可され、他方のリストで禁止されるような曖昧な構成は、一般に無効であることに注意。
グローバル attributes
| グローバル removeAttributes
| |
|---|---|---|
ローカル attributes
| 属性は、いずれかのリストに一致すれば許可される。重複は許可されない。 | 属性はローカル許可リストにある場合にのみ許可される。 グローバル除去リストとローカル許可リストの間で重複エントリは許可されない。 この特定の要素に関してはグローバル除去リストは機能を持たないが、ローカル許可リストを持たない他の要素には適用されうる点に注意。 |
ローカル removeAttributes
| 属性は、グローバル許可リストに含まれ、かつローカル除去リストに含まれない場合に許可される。ローカル除去はグローバル許可リストの部分集合でなければならない。 | 属性はどちらのリストにもない場合に許可される。 グローバル除去リストとローカル除去リストの間で重複エントリは許可されない。 |
多くの場合、グローバルと要素ごとのリスト間で重複が許されない非対称性に注意されたい。ただし、グローバル許可リストと要素ごとの除去リストの組み合わせの場合は、後者は前者の部分集合でなければならない。上記表の重複にのみ焦点を当てた抜粋は次のとおり:
グローバル attributes
| グローバル removeAttributes
| |
|---|---|---|
ローカル attributes
| 重複は許可されない。 | 重複は許可されない。 |
ローカル removeAttributes
| ローカル除去はグローバル許可リストの部分集合でなければならない。 | 重複は許可されない。 |
dataAttributes
設定は、custom data attributes を許可する。上記の規則は、dataAttributes
を許可リストと見なせば、custom data attributes にも容易に拡張できる:
グローバル attributes
と dataAttributes
が設定されている
| |
|---|---|
ローカル attributes
| すべての custom data attributes が許可される。重複となるため、いかなる custom data attributes もいかなる許可リストにも列挙してはならない。 |
ローカル removeAttributes
| custom data attribute は、ローカル除去リストに列挙されていない限り許可される。 重複となるため、いかなる custom data attribute もグローバル許可リストに列挙してはならない。 |
これらの規則を言葉でまとめると:
-
グローバルとローカルの各リスト間の重複と相互作用:
-
グローバル
attributes許可リストが存在する場合、すべての要素のローカルリストは次を満たす:-
ローカル
attributes許可リストが存在する場合、これらのリスト間に重複エントリがあってはならない。 -
ローカル
removeAttributes除去リストが存在する場合、そのすべてのエントリはグローバルattributes許可リストにも列挙されていなければならない。 -
dataAttributesが true の場合、いかなる custom data attributes もいずれの許可リストにも列挙してはならない。
-
-
グローバル
removeAttributes除去リストが存在する場合:-
ローカル
attributes許可リストが存在する場合、これらのリスト間に重複エントリがあってはならない。 -
ローカル
removeAttributes除去リストが存在する場合、これらのリスト間に重複エントリがあってはならない。 -
ローカル
attributes許可リストとローカルremoveAttributes除去リストの両方が同時に存在してはならない。 -
dataAttributesは存在してはならない。
-
-
SanitizerConfig
config が
有効 かどうかを判定するには:
注記: 渡される設定は事前に 設定の正規化手順を実行されていることが想定されています。 ここではそのアルゴリズムで保証された条件を単に断言するだけです。
-
断言: config["
elements"] が存在する、または config["removeElements"] が存在する。 -
もし config["
elements"] が存在し、かつ config["removeElements"] が存在する場合、falseを返す。 -
断言: config["
processingInstructions"] が存在する、または config["removeProcessingInstructions"] が存在する。 -
もし config["
processingInstructions"] が存在し、かつ config["removeProcessingInstructions"] が存在する場合、falseを返す。 -
断言: config["
attributes"] が存在する、または config["removeAttributes"] が存在する。 -
もし config["
attributes"] が存在し、かつ config["removeAttributes"] が存在する場合、falseを返す。 -
断言: すべての
SanitizerElementNamespaceWithAttributes、SanitizerElementNamespace、SanitizerProcessingInstruction、 およびSanitizerAttributeNamespace項目は正規化済みでなければならない(該当する 要素の正規化、処理命令の正規化 または 属性の正規化 を経ている)。 -
その他の場合:
-
もし config["
removeElements"] 重複がある場合、falseを返す。
-
-
もし config["
replaceWithChildrenElements"] が存在かつ 重複がある場合、falseを返す。 -
もし config["
processingInstructions"] が存在する場合:-
もし config["
processingInstructions"] targetが重複している場合、falseを返す。
-
-
その他の場合:
-
もし config["
removeProcessingInstructions"] targetが重複している場合、falseを返す。
-
-
もし config["
attributes"] が存在する場合:-
もし config["
attributes"] 重複がある場合、falseを返す。
-
-
その他の場合:
-
もし config["
removeAttributes"] 重複がある場合、falseを返す。
-
-
もし config["
replaceWithChildrenElements"] が存在する場合:-
各 config["
replaceWithChildrenElements"] の element について:-
組み込み非置換要素リストが element を含む場合、falseを返す。
-
-
もし config["
elements"] が存在する場合:-
config["
elements"] と config["replaceWithChildrenElements"] の 共通部分(intersection)が 空でない場合、falseを返す。
-
-
その他の場合:
-
config["
removeElements"] と config["replaceWithChildrenElements"] の 共通部分(intersection)が 空でない場合、falseを返す。
-
-
-
もし config["
attributes"] が存在する場合:-
断言: config["
dataAttributes"] が存在する。 -
もし config["
elements"] が存在する場合:-
各 config["
elements"] の element について:-
もし element["
attributes"] が存在 かつ element["attributes"] 重複がある 場合、falseを返す。 -
もし element["
removeAttributes"] が存在かつ element["removeAttributes"] 重複がある 場合、falseを返す。 -
もし config["
attributes"] と element["attributes"] (デフォルト« »付き)の 共通部分が空でない場合、falseを返す。 -
もし element["
removeAttributes"] (デフォルト« »付き)が config["attributes"]の部分集合でない場合、 falseを返す。 -
もし config["
dataAttributes"] が true かつ element["attributes"] が カスタムデータ属性 を含む場合、falseを返す。
-
-
-
config["
dataAttributes"] が true かつ config["attributes"] が カスタムデータ属性 を含む場合、 falseを返す。
-
-
その他の場合:
-
もし config["
elements"] が存在する場合:-
各 config["
elements"] の element について:-
element["
attributes"] が存在 かつ element["removeAttributes"] が存在する場合、falseを返す。 -
もし element["
attributes"] が存在 かつ element["attributes"] 重複がある場合、falseを返す。 -
もし element["
removeAttributes"] が存在 かつ element["removeAttributes"] 重複がある場合、falseを返す。 -
もし config["
removeAttributes"] と element["attributes"] (デフォルト« »付き)で 共通部分が空でない場合、falseを返す。 -
もし config["
removeAttributes"] と element["removeAttributes"] (デフォルト« »付き)で 共通部分が空でない場合、falseを返す。
-
-
-
config["
dataAttributes"] が存在する場合、falseを返す。
-
-
trueを返す。
注記: 設定をセットする際、
ディクショナリからの場合は
正規化されます。特に、allow-listとremove-listが両方とも省略されている場合は、
空のremove-listとして扱われます。したがって {} 自体は 有効 な設定ではありませんが、
正規化により {removeElements:[],removeAttributes:[]} となり有効となります。この正規化処理は、
ディクショナリが省略された場合と空の場合を同じとするために選択されました。
すなわち setHTMLUnsafe(txt) と setHTMLUnsafe(txt, {sanitizer: {}})
の挙動を一致させるためです。
3. アルゴリズム
Element
または DocumentFragment
の target、Element
の contextElement、文字列 html、辞書
options、boolean safe を受け取り、次を行う:
-
もし safe かつ contextElement の ローカル名 が "
script" であり、 contextElement の 名前空間 が HTML 名前空間 または SVG 名前空間 の場合、何もせずリターンする。 -
sanitizer を get a sanitizer instance from options に options と safe を与えて呼び出した結果とする。
-
newChildren を HTML フラグメントパースアルゴリズムに contextElement、html、true を指定して実行した結果とする。
-
fragment を新しい
DocumentFragmentとし、 その ノードドキュメント は contextElement の ノードドキュメント とする。 -
newChildren の各 node について、fragment に append する。
-
sanitize を fragment に sanitizer と safe で呼び出す。
注記: このアルゴリズムは SetHTMLOptions
と
SetHTMLUnsafeOptions
の両方に対応しており、デフォルト値のみが異なります。
-
sanitizerSpec を "
default" にする。 -
もし options["
sanitizer"] が存在する場合:-
sanitizerSpec に options["
sanitizer"] の値をセットする。
-
-
断言: sanitizerSpec は
Sanitizerインスタンス・ SanitizerPresetsメンバーの文字列 または ディクショナリ のいずれかである。 -
もし sanitizerSpec が 文字列 である場合:
-
sanitizerSpec に 組み込みの安全なデフォルト設定 をセットする。
-
もし sanitizerSpec が ディクショナリ である場合:
-
sanitizer を新しい
Sanitizerインスタンスとする。 -
setConfigurationResult を set a configuration を sanitizerSpec と not safe で sanitizer に対して呼び出した結果とする。
-
sanitizerSpec に sanitizer をセットする。
-
-
sanitizerSpec を返す。
3.1. サニタイズ
ParentNode
node、
Sanitizer
sanitizer、および boolean safe を用いて、次のステップを実行する:
-
configuration を sanitizer の 設定の値とする。
-
もし safe が true ならば、configuration を remove unsafe を configuration に 実行した結果に設定する。
-
sanitize core を node、configuration、および handleJavascriptNavigationUrls を safe に設定して呼び出す。
ParentNode
node、SanitizerConfig
configuration、および
boolean
handleJavascriptNavigationUrls を用いて
nodeから始まるDOMツリーを再帰的に処理する。ステップは次の通り:
-
-
断言: child は
Text、Comment、Element、ProcessingInstructionまたはDocumentTypeを実装している。注記: 現在、このアルゴリズムはHTMLパーサの出力でのみ呼び出され、この断言は常に成り立つ。
DocumentTypeが現れるのはparseHTMLまたはparseHTMLUnsafeのみ。今後アルゴリズムが他文脈で使われる場合は要再検討。 -
もし child が
DocumentTypeを実装しているなら、次へ。 -
もし child が
Commentを実装している場合: -
もし child が
ProcessingInstructionを実装している場合:-
piTarget を child の targetとする。
-
configuration["
processingInstructions"] が存在する場合:-
configuration["
processingInstructions"] が piTarget を含まない場合:
-
-
その他の場合:
-
configuration["
removeProcessingInstructions"] が piTargetを含む場合:
-
-
-
その他の場合:
-
elementName を、child の ローカル名および 名前空間を持つ
SanitizerElementNamespaceとする。 -
configuration["
replaceWithChildrenElements"] が存在する、 かつ configuration["replaceWithChildrenElements"] が elementName を含む場合:-
child に対し、configuration および handleJavascriptNavigationUrls を伴って sanitize core を呼び出す。
-
fragment を、ノード文書が node のノード文書である、新しい
DocumentFragmentとする。 -
child の子である 各 innerChild について反復し、 innerChild を fragment に追加する。
-
node 内で child を fragment で置換する。
注: ここで置換は投げないはずである。 なぜなら、そのアルゴリズムが成功裏に実行されるための構造上の 事前条件が満たされているはずだからである。
-
続行する。
-
そうでない場合:
-
configuration["
removeElements"] が elementName を含む 場合:
-
-
elementName が «[ "
name" → "template", "namespace" → HTML 名前空間 ]» と等しい場合、 child のtemplate contentsに対し、 configuration および handleJavascriptNavigationUrls を伴って sanitize core を呼び出す。 -
child がシャドウホストである場合、 child のシャドウルートに対し、 configuration および handleJavascriptNavigationUrls を伴って sanitize core を呼び出す。
-
elementWithLocalAttributes を « [] » とする。
-
configuration["
elements"] が存在する、かつ configuration["elements"] が elementName を含む 場合:-
elementWithLocalAttributes を configuration["
elements"][elementName] に設定する。
-
-
child の属性リスト内の 各 attribute について反復する:
-
attrName を、attribute の ローカル名および 名前空間を持つ
SanitizerAttributeNamespaceとする。 -
elementWithLocalAttributes["
removeAttributes"] が、« » を既定値として attrName を含む 場合:-
attribute を削除する。
-
-
そうでなく、configuration["
attributes"] が存在する場合:-
configuration["
attributes"] が attrName を含まず、かつ elementWithLocalAttributes["attributes"] が、« » を既定値として attrName を含まず、かつ "data-" が attribute のローカル名の符号単位接頭辞でなく、かつ 名前空間がnullでない、または configuration["dataAttributes"] が true でない場合:-
attribute を削除する。
-
-
-
そうでない場合:
-
elementWithLocalAttributes["
attributes"] が存在する、かつ elementWithLocalAttributes["attributes"] が attrName を含まない 場合:-
attribute を削除する。
-
-
そうでなく、configuration["
removeAttributes"] が attrName を含む 場合:-
attribute を削除する。
-
-
-
handleJavascriptNavigationUrls の場合:
-
«[elementName, attrName]» が 組み込みの ナビゲーション URL 属性リスト内の項目に一致し、かつ attribute がjavascript: URL を 含む場合、attribute を削除する。
-
child の名前空間が MathML 名前空間であり、かつ attr のローカル名が "
href" であり、かつ attr の名前空間がnullである、または XLink 名前空間であり、かつ attr がjavascript: URL を 含む場合、 attribute を削除する。 -
組み込みの アニメーション URL 属性リスト が «[elementName, attrName]» を含み、かつ attr の 値が "
href" または "xlink:href" である場合、attribute を削除する。
-
-
-
child に対し、 configuration および handleJavascriptNavigationUrls を伴って sanitize core を呼び出す。
-
-
javascript: URL を
ナビゲーション時のみサポートしている。ナビゲーション自体は XSS 脅威でないため
javascript: へのナビゲーションのみ処理し、その他のナビゲーションは一般化しない。
宣言的ナビゲーションには主に次のカテゴリがある:
-
アンカー要素(HTMLやSVG名前空間の
<a>) -
フォーム送信時のアクションによりナビゲーションされるフォーム要素
-
[SVG11] のアニメーション。
最初の2つは 組み込みナビゲーションURL属性リスト でカバーされる。
MathML の場合はこの仕様で正式に「名前空間ごとのグローバル」ルールがないため、個別ルールでカバーされる。
SVGアニメーションの場合は 組み込みアニメーションURL属性リストでカバーされる。
ただしSVGアニメーション要素の意味合いはアニメーション対象に依存し、サニタイズ実行時に最終対象が分からないため、
sanitize アルゴリズムは href
属性のアニメーションを常にブロックする。
-
url を 基本 URL パーサ を attribute の 値 で実行した結果とする。
-
もし url が
failureなら、false を返す。 -
url の スキーム が "
javascript" かどうか返す。
3.2. 構成の変更
構成修飾メソッドは Sanitizer
上のメソッドであり、
その構成(設定)を変更します。
これらは有効性基準を維持します。
また、構成が実際に変更されたかどうかを呼び出し元に伝える真偽値を返します。
let s= new Sanitizer({ elements: [ "div" ]}); s. allowElement( "p" ); // true を返す。 div. setHTML( "<div><p>" , { sanitizer: s}); // `<div>`と`<p>`を許可する。
let s= new Sanitizer({ elements: [ "div" ]}); s. removeElement( "p" ); // false を返す。<p> はもともと許可されていなかった。 div. setHTML( "<div><p>" , { sanitizer: s}); // `<div>`を許可し、`<p>` は削除される。
SanitizerElement
element
を SanitizerConfig
configuration から削除する:
-
グローバル許可リストかグローバル除外リストか、
-
それらにすでにelementが含まれているかどうか。
-
element を サニタイザー要素の正規化の結果に設定する。
-
modified を removeで configuration["
replaceWithChildrenElements"] から element を削除した結果に設定する。 -
その他の場合:
-
もし configuration["
removeElements"] に element が含まれる場合:-
コメント: グローバル除外リストで、すでに element を含んでいる。
-
modified を返す。
-
-
コメント: グローバル除外リストだがelementは含まれていない。
-
addでconfiguration["
removeElements"] に element を追加する。 -
true を返す。
-
SanitizerConfig
configuration から削除する:
注記: このメソッドは2つの場合、つまりグローバル許可リストか、グローバル除外リストか、を区別します。 グローバル除外リストに attribute を追加するなら、妥当性基準を維持するため追加の処理が必要かもしれません。 グローバル許可リストから attribute を削除する場合も、ローカル除外リストからも削除が必要なことがあります。
-
attribute を サニタイザー属性の正規化の結果に設定する。
-
もし configuration["
attributes"] が存在する場合:-
コメント: グローバル許可リストの場合、attributeを除去する必要がある。
-
modified を removeで configuration["
attributes"] から attribute を削除した結果に設定する。 -
コメント: 各要素ごとの許可・除外リストを修正する。
-
もし configuration["
elements"] が存在する場合:-
各 configuration["
elements"] の element について:-
もし element["
attributes"] (デフォルト« »付き) attributeが含まれる場合:-
modified を true に設定する。
-
removeで element["
attributes"] から attribute を削除。
-
-
もし element["
removeAttributes"] (デフォルト« »付き) attributeが含まれる場合:-
断言: modified は true。
-
removeで element["
removeAttributes"] から attribute を削除。
-
-
-
-
modified を返す。
-
-
その他の場合:
-
コメント: グローバル除外リストの場合、attributeを追加する。
-
もし configuration["
removeAttributes"] が attribute を含む場合、 falseを返す。 -
コメント: 各要素ごとの許可・除外リストを修正する。
-
もし configuration["
elements"] が存在する場合:-
各 configuration["
elements"] の elementについて:-
もし element["
attributes"] (デフォルト« »付き) attributeが含まれる場合:-
removeで element["
attributes"] から attribute を削除。
-
-
もし element["
removeAttributes"] (デフォルト« »付き) attributeが含まれる場合:-
removeで element["
removeAttributes"] から attribute を削除。
-
-
-
-
appendで attribute を configuration["
removeAttributes"] に追加する。 -
true を返す。
-
SanitizerConfig
configuration
に対して、次を実行する:
注記: このアルゴリズムは remove unsafe と呼ばれているが、 この仕様での「unsafe」は厳密には、 ドキュメント挿入時に JavaScript が実行される内容のみを指します。 つまり、このメソッドはXSSの機会を除去します。
-
アサート:built-in safe baseline configuration のキーセット が次と等しい: « [ "
removeElements", "removeAttributes" ] ». -
result を false にする。
-
各 element を 組み込み安全ベースライン構成["
removeElements"] の 要素について:-
要素を削除するで element を configuration から削除する。
-
呼び出し結果が true なら result を true にする。
-
-
各 attribute を 組み込み安全ベースライン構成["
removeAttributes"] の属性について:-
属性を削除するで attribute を configuration から削除する。
-
呼び出し結果が true なら result を true にする。
-
-
各 attribute を イベントハンドラーコンテンツ属性で:
-
属性を削除するで attribute を configuration から削除する。
-
呼び出し結果が true なら result を true にする。
-
-
result を返す。
3.3. 構成を設定する
Sanitizer
sanitizer が与えられたとき:
3.4. 構成の正規化
Sanitizerは、設定を正規化された形式で格納します。これは、複数の処理手順を簡略化するためです。
elements
リスト {elements: ["div"]} は
{elements: [{name: "div", namespace: "http://www.w3.org/1999/xhtml"}]
のように格納されます。
SanitizerConfig
configuration
を、ブール値
allowCommentsPIsAndDataAttributes
で構成を正規化するには:
注: configuration は、JavaScript
値を SanitizerConfig
に変換した [WebIDL]
の結果であると仮定する。
-
configuration["
elements"] も configuration["removeElements"] も存在しない場合、configuration["removeElements"] を « » に設定する。 -
configuration["
processingInstructions"] も configuration["removeProcessingInstructions"] も存在しない場合:-
allowCommentsPIsAndDataAttributes が true の場合、configuration["
removeProcessingInstructions"] を « » に設定する。 -
そうでない場合、configuration["
processingInstructions"] を « » に設定する。
-
-
configuration["
attributes"] も configuration["removeAttributes"] も存在しない場合、configuration["removeAttributes"] を « » に設定する。 -
configuration["
elements"] が存在する場合:-
elements を « » とする。
-
configuration["
elements"] の各 element について反復し、次を行う:-
element について属性付き sanitizer 要素を正規化する結果を elements に追加する。
-
-
configuration["
elements"] を elements に設定する。
-
-
configuration["
removeElements"] が存在する場合:-
elements を « » とする。
-
configuration["
removeElements"] の各 element について反復し、次を行う:-
element についてsanitizer 要素を正規化する結果を elements に追加する。
-
-
configuration["
removeElements"] を elements に設定する。
-
-
configuration["
replaceWithChildrenElements"] が存在する場合:-
elements を « » とする。
-
configuration["
replaceWithChildrenElements"] の各 element について反復し、次を行う:-
element についてsanitizer 要素を正規化する結果を elements に追加する。
-
-
configuration["
replaceWithChildrenElements"] を elements に設定する。
-
-
configuration["
processingInstructions"] が存在する場合:-
processingInstructions を « » とする。
-
configuration["
processingInstructions"] の各 pi について反復する:-
pi についてsanitizer 処理命令を正規化する結果を processingInstructions に追加する。
-
-
configuration["
processingInstructions"] を processingInstructions に設定する。
-
-
configuration["
removeProcessingInstructions"] が存在する場合:-
processingInstructions を « » とする。
-
configuration["
removeProcessingInstructions"] の各 pi について反復する:-
pi についてsanitizer 処理命令を正規化する結果を processingInstructions に追加する。
-
-
configuration["
removeProcessingInstructions"] を processingInstructions に設定する。
-
-
configuration["
attributes"] が存在する場合:-
attributes を « » とする。
-
configuration["
attributes"] の各 attribute について反復し、次を行う:-
attribute についてsanitizer 属性を正規化する結果を attributes に追加する。
-
-
configuration["
attributes"] を attributes に設定する。
-
-
configuration["
removeAttributes"] が存在する場合:-
attributes を « » とする。
-
configuration["
removeAttributes"] の各 attribute について反復し、次を行う:-
attribute についてsanitizer 属性を正規化する結果を attributes に追加する。
-
-
configuration["
removeAttributes"] を attributes に設定する。
-
-
configuration["
comments"] が存在しない場合、configuration["comments"] を allowCommentsPIsAndDataAttributes に設定する。 -
configuration["
attributes"] が存在し、かつ configuration["dataAttributes"] が存在しない場合、 configuration["dataAttributes"] を allowCommentsPIsAndDataAttributes に設定する。
SanitizerElementWithAttributes
element
を属性付き sanitizer 要素を
正規化するには:
-
result を、element でsanitizer 要素を正規化する結果とする。
-
element が辞書である場合:
-
element["
attributes"] が存在する場合:-
attributes を « » とする。
-
element["
attributes"] の各 attribute について反復する:-
attribute でsanitizer 属性を正規化する結果を attributes に追加する。
-
-
result["
attributes"] を attributes に設定する。
-
-
element["
removeAttributes"] が存在する場合:-
attributes を « » とする。
-
element["
removeAttributes"] の各 attribute について反復する:-
attribute でsanitizer 属性を正規化する結果を attributes に追加する。
-
-
result["
removeAttributes"] を attributes に設定する。
-
-
-
result["
attributes"] も result["removeAttributes"] も存在しない場合:-
result["
removeAttributes"] を « » に設定する。
-
-
result を返す。
SanitizerElement
element と サニタイザー名を正規化する を使い、
elementとデフォルト名前空間「HTML namespace」を渡し、その結果を返す。
SanitizerAttribute
attributeについて、サニタイザー名を正規化する を使い、
attributeとデフォルト名前空間nullを渡して、その結果を返す。
3.5. 補助アルゴリズム
-
もし itemA["namespace"] が null の場合:
-
もし itemB["namespace"] が null でなければ、true を返す。
-
-
そうでなければ:
-
もし itemB["namespace"] が null なら、false を返す。
-
もし itemA["namespace"] が コード単位比較でより小さい itemB["namespace"] なら、true を返す。
-
もし itemA["namespace"] が itemB["namespace"] と異なるなら、 false を返す。
-
-
itemA["name"] が コード単位比較でより小さい itemB["name"] かどうかを返す。
SanitizerElement
を含む 2 つのリスト
A と B
の積集合は、集合の積集合と同じである。
ただし、集合の項目は、あらかじめ正規化されている:
-
set A を « [] » とする。
-
set B を « [] » とする。
-
A の各 entry について反復し、 entry について sanitizer 名を正規化する結果を set A に追加する。
-
B の各 entry について反復し、 entry について sanitizer 名を正規化する結果を set B に追加する。
-
set A と set B の積集合を返す。
3.6. 組み込み
組み込みは5つあります:
組み込みの安全な既定構成は次の通りです:
{ "elements" : [ { "name" : "math" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "merror" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mfrac" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mi" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mmultiscripts" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mn" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mo" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [ { "name" : "fence" , "namespace" : null }, { "name" : "form" , "namespace" : null }, { "name" : "largeop" , "namespace" : null }, { "name" : "lspace" , "namespace" : null }, { "name" : "maxsize" , "namespace" : null }, { "name" : "minsize" , "namespace" : null }, { "name" : "movablelimits" , "namespace" : null }, { "name" : "rspace" , "namespace" : null }, { "name" : "separator" , "namespace" : null }, { "name" : "stretchy" , "namespace" : null }, { "name" : "symmetric" , "namespace" : null } ] }, { "name" : "mover" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [ { "name" : "accent" , "namespace" : null } ] }, { "name" : "mpadded" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [ { "name" : "depth" , "namespace" : null }, { "name" : "height" , "namespace" : null }, { "name" : "lspace" , "namespace" : null }, { "name" : "voffset" , "namespace" : null }, { "name" : "width" , "namespace" : null } ] }, { "name" : "mphantom" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mprescripts" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mroot" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mrow" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "ms" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mspace" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [ { "name" : "depth" , "namespace" : null }, { "name" : "height" , "namespace" : null }, { "name" : "width" , "namespace" : null } ] }, { "name" : "msqrt" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mstyle" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "msub" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "msubsup" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "msup" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mtable" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mtd" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [ { "name" : "columnspan" , "namespace" : null }, { "name" : "rowspan" , "namespace" : null } ] }, { "name" : "mtext" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "mtr" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "munder" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [ { "name" : "accentunder" , "namespace" : null } ] }, { "name" : "munderover" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [ { "name" : "accent" , "namespace" : null }, { "name" : "accentunder" , "namespace" : null } ] }, { "name" : "semantics" , "namespace" : "http://www.w3.org/1998/Math/MathML" , "attributes" : [] }, { "name" : "a" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "href" , "namespace" : null }, { "name" : "hreflang" , "namespace" : null }, { "name" : "type" , "namespace" : null } ] }, { "name" : "abbr" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "address" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "article" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "aside" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "b" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "bdi" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "bdo" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "blockquote" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "cite" , "namespace" : null } ] }, { "name" : "body" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "br" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "caption" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "cite" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "code" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "col" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "span" , "namespace" : null } ] }, { "name" : "colgroup" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "span" , "namespace" : null } ] }, { "name" : "data" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "value" , "namespace" : null } ] }, { "name" : "dd" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "del" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "cite" , "namespace" : null }, { "name" : "datetime" , "namespace" : null } ] }, { "name" : "dfn" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "div" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "dl" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "dt" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "em" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "figcaption" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "figure" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "footer" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "h1" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "h2" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "h3" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "h4" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "h5" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "h6" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "head" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "header" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "hgroup" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "hr" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "html" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "i" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "ins" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "cite" , "namespace" : null }, { "name" : "datetime" , "namespace" : null } ] }, { "name" : "kbd" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "li" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "value" , "namespace" : null } ] }, { "name" : "main" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "mark" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "menu" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "nav" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "ol" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "reversed" , "namespace" : null }, { "name" : "start" , "namespace" : null }, { "name" : "type" , "namespace" : null } ] }, { "name" : "p" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "pre" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "q" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "rp" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "rt" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "ruby" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "s" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "samp" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "search" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "section" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "small" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "span" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "strong" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "sub" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "sup" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "table" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "tbody" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "td" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "colspan" , "namespace" : null }, { "name" : "headers" , "namespace" : null }, { "name" : "rowspan" , "namespace" : null } ] }, { "name" : "tfoot" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "th" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "abbr" , "namespace" : null }, { "name" : "colspan" , "namespace" : null }, { "name" : "headers" , "namespace" : null }, { "name" : "rowspan" , "namespace" : null }, { "name" : "scope" , "namespace" : null } ] }, { "name" : "thead" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "time" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [ { "name" : "datetime" , "namespace" : null } ] }, { "name" : "title" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "tr" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "u" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "ul" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "var" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "wbr" , "namespace" : "http://www.w3.org/1999/xhtml" , "attributes" : [] }, { "name" : "a" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "href" , "namespace" : null }, { "name" : "hreflang" , "namespace" : null }, { "name" : "type" , "namespace" : null } ] }, { "name" : "circle" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "cx" , "namespace" : null }, { "name" : "cy" , "namespace" : null }, { "name" : "pathLength" , "namespace" : null }, { "name" : "r" , "namespace" : null } ] }, { "name" : "defs" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [] }, { "name" : "desc" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [] }, { "name" : "ellipse" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "cx" , "namespace" : null }, { "name" : "cy" , "namespace" : null }, { "name" : "pathLength" , "namespace" : null }, { "name" : "rx" , "namespace" : null }, { "name" : "ry" , "namespace" : null } ] }, { "name" : "foreignObject" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "height" , "namespace" : null }, { "name" : "width" , "namespace" : null }, { "name" : "x" , "namespace" : null }, { "name" : "y" , "namespace" : null } ] }, { "name" : "g" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [] }, { "name" : "line" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "pathLength" , "namespace" : null }, { "name" : "x1" , "namespace" : null }, { "name" : "x2" , "namespace" : null }, { "name" : "y1" , "namespace" : null }, { "name" : "y2" , "namespace" : null } ] }, { "name" : "marker" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "markerHeight" , "namespace" : null }, { "name" : "markerUnits" , "namespace" : null }, { "name" : "markerWidth" , "namespace" : null }, { "name" : "orient" , "namespace" : null }, { "name" : "preserveAspectRatio" , "namespace" : null }, { "name" : "refX" , "namespace" : null }, { "name" : "refY" , "namespace" : null }, { "name" : "viewBox" , "namespace" : null } ] }, { "name" : "metadata" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [] }, { "name" : "path" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "d" , "namespace" : null }, { "name" : "pathLength" , "namespace" : null } ] }, { "name" : "polygon" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "pathLength" , "namespace" : null }, { "name" : "points" , "namespace" : null } ] }, { "name" : "polyline" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "pathLength" , "namespace" : null }, { "name" : "points" , "namespace" : null } ] }, { "name" : "rect" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "height" , "namespace" : null }, { "name" : "pathLength" , "namespace" : null }, { "name" : "rx" , "namespace" : null }, { "name" : "ry" , "namespace" : null }, { "name" : "width" , "namespace" : null }, { "name" : "x" , "namespace" : null }, { "name" : "y" , "namespace" : null } ] }, { "name" : "svg" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "height" , "namespace" : null }, { "name" : "preserveAspectRatio" , "namespace" : null }, { "name" : "viewBox" , "namespace" : null }, { "name" : "width" , "namespace" : null }, { "name" : "x" , "namespace" : null }, { "name" : "y" , "namespace" : null } ] }, { "name" : "text" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "dx" , "namespace" : null }, { "name" : "dy" , "namespace" : null }, { "name" : "lengthAdjust" , "namespace" : null }, { "name" : "rotate" , "namespace" : null }, { "name" : "textLength" , "namespace" : null }, { "name" : "x" , "namespace" : null }, { "name" : "y" , "namespace" : null } ] }, { "name" : "textPath" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "lengthAdjust" , "namespace" : null }, { "name" : "method" , "namespace" : null }, { "name" : "path" , "namespace" : null }, { "name" : "side" , "namespace" : null }, { "name" : "spacing" , "namespace" : null }, { "name" : "startOffset" , "namespace" : null }, { "name" : "textLength" , "namespace" : null } ] }, { "name" : "title" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [] }, { "name" : "tspan" , "namespace" : "http://www.w3.org/2000/svg" , "attributes" : [ { "name" : "dx" , "namespace" : null }, { "name" : "dy" , "namespace" : null }, { "name" : "lengthAdjust" , "namespace" : null }, { "name" : "rotate" , "namespace" : null }, { "name" : "textLength" , "namespace" : null }, { "name" : "x" , "namespace" : null }, { "name" : "y" , "namespace" : null } ] } ], "processingInstructions" : [], "attributes" : [ { "name" : "alignment-baseline" , "namespace" : null }, { "name" : "baseline-shift" , "namespace" : null }, { "name" : "clip-path" , "namespace" : null }, { "name" : "clip-rule" , "namespace" : null }, { "name" : "color" , "namespace" : null }, { "name" : "color-interpolation" , "namespace" : null }, { "name" : "cursor" , "namespace" : null }, { "name" : "dir" , "namespace" : null }, { "name" : "direction" , "namespace" : null }, { "name" : "display" , "namespace" : null }, { "name" : "displaystyle" , "namespace" : null }, { "name" : "dominant-baseline" , "namespace" : null }, { "name" : "fill" , "namespace" : null }, { "name" : "fill-opacity" , "namespace" : null }, { "name" : "fill-rule" , "namespace" : null }, { "name" : "font-family" , "namespace" : null }, { "name" : "font-size" , "namespace" : null }, { "name" : "font-size-adjust" , "namespace" : null }, { "name" : "font-stretch" , "namespace" : null }, { "name" : "font-style" , "namespace" : null }, { "name" : "font-variant" , "namespace" : null }, { "name" : "font-weight" , "namespace" : null }, { "name" : "lang" , "namespace" : null }, { "name" : "letter-spacing" , "namespace" : null }, { "name" : "marker-end" , "namespace" : null }, { "name" : "marker-mid" , "namespace" : null }, { "name" : "marker-start" , "namespace" : null }, { "name" : "mathbackground" , "namespace" : null }, { "name" : "mathcolor" , "namespace" : null }, { "name" : "mathsize" , "namespace" : null }, { "name" : "opacity" , "namespace" : null }, { "name" : "paint-order" , "namespace" : null }, { "name" : "pointer-events" , "namespace" : null }, { "name" : "scriptlevel" , "namespace" : null }, { "name" : "shape-rendering" , "namespace" : null }, { "name" : "stop-color" , "namespace" : null }, { "name" : "stop-opacity" , "namespace" : null }, { "name" : "stroke" , "namespace" : null }, { "name" : "stroke-dasharray" , "namespace" : null }, { "name" : "stroke-dashoffset" , "namespace" : null }, { "name" : "stroke-linecap" , "namespace" : null }, { "name" : "stroke-linejoin" , "namespace" : null }, { "name" : "stroke-miterlimit" , "namespace" : null }, { "name" : "stroke-opacity" , "namespace" : null }, { "name" : "stroke-width" , "namespace" : null }, { "name" : "text-anchor" , "namespace" : null }, { "name" : "text-decoration" , "namespace" : null }, { "name" : "text-overflow" , "namespace" : null }, { "name" : "text-rendering" , "namespace" : null }, { "name" : "title" , "namespace" : null }, { "name" : "transform" , "namespace" : null }, { "name" : "transform-origin" , "namespace" : null }, { "name" : "unicode-bidi" , "namespace" : null }, { "name" : "vector-effect" , "namespace" : null }, { "name" : "visibility" , "namespace" : null }, { "name" : "white-space" , "namespace" : null }, { "name" : "word-spacing" , "namespace" : null }, { "name" : "writing-mode" , "namespace" : null } ], "comments" : false , "dataAttributes" : false }
注: 含まれる[MathML] マークアップは [SafeMathML] に基づいています。
組み込みの安全なベースライン構成は、スクリプト内容のみをブロックすることを目的としています。内容は以下の通りです:
{ "removeElements" : [ { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "embed" }, { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "frame" }, { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "iframe" }, { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "object" }, { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "script" }, { "namespace" : "http://www.w3.org/2000/svg" , "name" : "script" }, { "namespace" : "http://www.w3.org/2000/svg" , "name" : "use" } ], "removeAttributes" : [] }
警告: remove unsafe アルゴリズムは、 イベントハンドラー内容属性 ([HTML] で定義)も追加で除去することを規定します。 もしユーザーエージェントが [HTML]仕様に独自の イベントハンドラー内容属性 を拡張して定義する場合は、その扱い方を決める責任があります。現在の イベントハンドラー内容属性 リストを使うと、安全なベースライン構成は実質的に次のようになります:
{ "removeElements" : [ { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "embed" }, { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "frame" }, { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "iframe" }, { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "object" }, { "namespace" : "http://www.w3.org/1999/xhtml" , "name" : "script" }, { "namespace" : "http://www.w3.org/2000/svg" , "name" : "script" }, { "namespace" : "http://www.w3.org/2000/svg" , "name" : "use" } ], "removeAttributes" : [ "onafterprint" , "onauxclick" , "onbeforeinput" , "onbeforematch" , "onbeforeprint" , "onbeforeunload" , "onbeforetoggle" , "onblur" , "oncancel" , "oncanplay" , "oncanplaythrough" , "onchange" , "onclick" , "onclose" , "oncontextlost" , "oncontextmenu" , "oncontextrestored" , "oncopy" , "oncuechange" , "oncut" , "ondblclick" , "ondrag" , "ondragend" , "ondragenter" , "ondragleave" , "ondragover" , "ondragstart" , "ondrop" , "ondurationchange" , "onemptied" , "onended" , "onerror" , "onfocus" , "onformdata" , "onhashchange" , "oninput" , "oninvalid" , "onkeydown" , "onkeypress" , "onkeyup" , "onlanguagechange" , "onload" , "onloadeddata" , "onloadedmetadata" , "onloadstart" , "onmessage" , "onmessageerror" , "onmousedown" , "onmouseenter" , "onmouseleave" , "onmousemove" , "onmouseout" , "onmouseover" , "onmouseup" , "onoffline" , "ononline" , "onpagehide" , "onpagereveal" , "onpageshow" , "onpageswap" , "onpaste" , "onpause" , "onplay" , "onplaying" , "onpopstate" , "onprogress" , "onratechange" , "onreset" , "onresize" , "onrejectionhandled" , "onscroll" , "onscrollend" , "onsecuritypolicyviolation" , "onseeked" , "onseeking" , "onselect" , "onslotchange" , "onstalled" , "onstorage" , "onsubmit" , "onsuspend" , "ontimeupdate" , "ontoggle" , "onunhandledrejection" , "onunload" , "onvolumechange" , "onwaiting" , "onwheel" ] }
javascript:"
ナビゲーションが「安全でない」とされる)は以下の通りです:
«[
[
{ "name" → "a", "namespace" → HTML名前空間
},
{ "name" → "href", "namespace" → null }
],
[
{ "name" → "area", "namespace" → HTML名前空間
},
{ "name" → "href", "namespace" → null }
],
[
{ "name" → "base", "namespace" → HTML名前空間
},
{ "name" → "href", "namespace" → null }
],
[
{ "name" → "button", "namespace" → HTML名前空間
},
{ "name" → "formaction", "namespace" → null }
],
[
{ "name" → "form", "namespace" → HTML名前空間
},
{ "name" → "action", "namespace" → null }
],
[
{ "name" → "input", "namespace" → HTML名前空間
},
{ "name" → "formaction", "namespace" → null }
],
[
{ "name" → "a", "namespace" → SVG名前空間 },
{ "name" → "href", "namespace" → null }
],
[
{ "name" → "a", "namespace" → SVG名前空間 },
{ "name" → "href", "namespace" → XLink名前空間 }
],
]»
組み込みのアニメーションURL属性リストは、
[SVG11]において
ナビゲーション要素を宣言的に「javascript:」URLへ変更する用途に使うことができ、
次のようになります:
«[
[
{ "name" → "animate", "namespace" → SVG名前空間 },
{ "name" → "attributeName", "namespace" → null] }
],
[
{ "name" → "animateTransform", "namespace" → SVG名前空間 },
{ "name" → "attributeName", "namespace" → null }
],
[
{ "name" → "set", "namespace" → SVG名前空間 },
{ "name" → "attributeName", "namespace" → null }
],
]»
組み込みの置換不可要素リストには、 子要素への置換を許可すると再パース問題や無効なノードツリーになる可能性があるため絶対に置換してはいけない要素が含まれます。
«[
{ "name" → "html", "namespace" → HTML名前空間 },
{ "name" → "svg", "namespace" → SVG名前空間 },
{ "name" → "math", "namespace" → MathML名前空間 },
]»
これらの制限だけでは、例えば次のようなすべての再パース攻撃を防ぐには不十分です:
// BAD EXAMPLE(悪い例) let sanitizer= { replaceWithChildrenElements: [ "tr" ] }; div. setHTML( "<table><tbody><tr><td><div>" , { sanitizer}); div. innerHTML= div. innerHTML; // div.innerHTML は現在 `<div></div><table><tbody><tr></tr></tbody></table>`
しかし、この例の <iframe> については再パースされて通常のHTML iframeとして有効になってしまうことは防げます:
4. セキュリティ考慮事項
Sanitizer API は、与えられた HTML コンテンツを走査し、構成に従って要素や属性を除去することで DOM ベースのクロスサイトスクリプティングを防ぐことを目的としています。指定された API は、スクリプト実行可能なマークアップを残すような Sanitizer オブジェクトの構築を許してはならず、もしそうなった場合は脅威モデル上のバグです。
とはいえ、Sanitizer API の正しい利用だけでは防げないセキュリティ上の問題も存在し、それについては以下のセクションで説明します。
4.1. サーバーサイド反射型および保存型XSS
このセクションは規範的ではありません。
Sanitizer API は DOM 内だけで動作し、既存の DocumentFragment を走査・フィルターする機能を提供します。Sanitizer はサーバーサイドの反射型XSSや保存型XSSには対応しません。
4.2. DOMクラッタリング
このセクションは規範的ではありません。
DOMクラッタリングとは、攻撃者が id や name 属性を使って要素に名前をつけ、HTML要素の children
などのプロパティを悪意のある内容で上書きしてアプリケーションを混乱させる攻撃です。
Sanitizer API はデフォルトでは DOMクラッタリング攻撃を防ぎませんが、構成で id や name 属性を除去することができます。
4.3. スクリプトガジェットによるXSS
このセクションは規範的ではありません。
スクリプトガジェットとは、攻撃者が有名なJavaScriptライブラリなどの既存アプリケーションコードを利用して、攻撃者自身のコードを実行させる手法です。これは無害に見えるコードや一見無害なDOMノードを注入し、フレームワークがそれを解析・解釈することで JavaScript の実行につなげます。
Sanitizer API ではこの種の攻撃を防ぐことはできません。ページ作成者は未知の要素を許可する場合は特に注意し、data- や slot
属性、<slot> や <template>
など広く使われるテンプレート・フレームワーク固有のマークアップも明示的に設定する必要があります。これらの制限は網羅的ではないので、ページ作成者はサードパーティライブラリの挙動もよく確認してください。
4.4. 変異型XSS
このセクションは規範的ではありません。
変異型XSS(mXSS)は、HTMLスニペットを誤った文脈でパースした際にパーサの文脈不一致を利用する攻撃です。特に、一度パースしたHTMLフラグメントを文字列としてシリアライズし、別の親要素に再挿入した場合、同じように解釈される保証はありません。例えば外部コンテンツやタグの入れ子ミスによるパース挙動の変化を利用します。
Sanitizer API は文字列をノードツリー化する機能のみを提供します。文脈は各サニタイザー関数によって暗黙的に与えられ、Element.setHTML()
は現在の要素、Document.parseHTML() は新しいドキュメントを作成します。そのためSanitizer API自体は変異型XSSの直接的な影響は受けません。
もし開発者がサニタイズ済みノードツリーを .innerHTML
などで文字列にし、再度パースする場合はmXSSが発生しうるため、この運用は推奨されません。どうしても文字列でHTMLを扱う場合は、その文字列は信頼できないものとして扱い、DOM挿入時に再度サニタイズすべきです。つまり、一度サニタイズ→シリアライズしたHTMLは、もはやサニタイズ済みとは考えられません。
mXSSのより完全な解説は [MXSS] を参照してください。
5. 謝辞
本仕様は cure53 の [DOMPURIFY]、
Internet Explorer の window.toStaticHTML()
、そして Ben Bucksch による
[HTMLSanitizer]
に着想を得ています。Anne van Kesteren、Krzysztof Kotowicz、
Andrew C. H. Mc Millan、Tom Schuster、Luke Warlow、Guillaume Weghsteen、
Mike West 各氏の貴重なフィードバックにも感謝します。