1. はじめに
このセクションは規範的ではありません。
ウェブアプリケーションは、クライアント側でHTML文字列を扱う必要がある場合が多くあります。
例えばクライアント側テンプレートソリューションの一部として、あるいはユーザー生成コンテンツのレンダリングとして等です。安全にこれを行うのは困難です。
文字列を単純に結合して
Element
の
innerHTML
に詰め込むという安易な方法は、
予期しない方法でJavaScriptが実行されるリスクを伴います。
[DOMPURIFY]のようなライブラリは、 挿入前に文字列を注意深くパースしてサニタイズし、DOMを構築し、 許可リストを通してそのメンバーをフィルタリングすることでこの問題に対処しようとします。 しかし、このアプローチは脆弱であることが判明しています。 ウェブ向けに公開されているパースAPIは、 実際にブラウザが「本物の」DOMとして文字列をレンダリングする際の挙動と 必ずしも合理的に一致しません。 さらに、ライブラリ側はブラウザの挙動の変化に常に追随する必要があります。 以前は安全だったものが、プラットフォームレベルの新機能によって 時限爆弾に変わることもあります。
ブラウザは、コードを実行するタイミングをかなり正確に把握しています。 ユーザ空間のライブラリよりも、ブラウザ自身に任意の文字列から安全にHTMLをレンダリングする方法を教えることで、 ブラウザのパーサ実装の変更に合わせて維持・更新される可能性が高い形で、 より良い方法を提供できます。 本文書はそのためのAPIの概要を示します。
1.1. 目的
-
開発者がユーザーが制御するHTMLを扱う際に、注入時に直接スクリプトが実行されないようにする メカニズムを提供することで、DOMベースのクロスサイトスクリプティング攻撃のリスクを軽減します。
-
現在のユーザーエージェントにおけるHTMLの理解を考慮して、 HTML出力を安全に利用できるようにします。
-
開発者がデフォルトの要素や属性のセットを上書きできるようにします。 特定の要素や属性を追加することで スクリプトガジェット 攻撃を防ぐことができます。
1.2. API概要
Sanitizer APIは、HTMLを含む文字列を解析してDOMツリーに変換し、 ユーザーが指定した設定に従ってそのツリーをフィルタリングする機能を提供します。 メソッドは2つの種類があります:
-
安全・非安全: 「安全」メソッドはスクリプトを実行するマークアップを生成しません。 つまり、XSSから安全なはずです。「非安全」メソッドは、指定された内容をそのまま解析・フィルタします。 詳細は§ 4 セキュリティに関する考慮事項も参照してください。
-
コンテキスト:メソッドは
Element
およびShadowRoot
上に定義されており、 これらのNode
の 子を置き換えます。 おおむねinnerHTML
に類似しています。Document
にも 静的メソッドがあり、 ドキュメント全体を解析する点でDOMParser
のparseFromString()
に類似しています。
2. フレームワーク
2.1. Sanitizer API
Element
インターフェースは、setHTML()
と
setHTMLUnsafe()
の2つのメソッドを定義します。
どちらもHTMLマークアップを含むDOMString
と、オプションの設定を受け取ります。
partial interface Element { [CEReactions ]undefined setHTMLUnsafe ((TrustedHTML or DOMString ),
html optional SetHTMLUnsafeOptions = {}); [
options CEReactions ]undefined setHTML (DOMString ,
html optional SetHTMLOptions = {}); };
options
Element
の
setHTMLUnsafe(html, options)
メソッドの手順は次のとおり:
-
compliantHTMLを、 Get Trusted Type compliant stringアルゴリズムに
TrustedHTML
、 thisの relevant global object、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 CEReactions ]undefined setHTML (DOMString ,
html optional SetHTMLOptions = {}); };
options
これらのメソッドはShadowRoot
にも反映されています:
ShadowRoot
の
setHTMLUnsafe(html, options)
メソッドの手順は次のとおり:
-
compliantHTMLを、 Get Trusted Type compliant stringアルゴリズムに
TrustedHTML
、 thisの relevant global object、html、 "ShadowRoot setHTMLUnsafe"、"script"を渡して呼び出すことで得る。 -
Set and filter HTMLに this、 thisの shadow host(コンテキスト要素)、 compliantHTML、options、falseを渡して呼び出す。
ShadowRoot
の
setHTML(html, options)
メソッドの手順は次のとおり:
-
HTML の設定とフィルタ処理を、this(ターゲット)、this(コンテキスト要素)、 html、options、および true を使って行う。
Document
インターフェイスは、Document
全体をパースする2つの新たなメソッドを持つようになる:
partial interface Document {static Document parseHTMLUnsafe ((TrustedHTML or DOMString ),
html optional SetHTMLUnsafeOptions = {});
options 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 はブラウジングコンテキストを持たないため、スクリプトは無効化される。
-
documentのallow declarative shadow rootsをtrueに設定する。
-
文字列からHTMLをパースする。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 はブラウジングコンテキストを持たないため、スクリプトは無効化される。
-
documentのallow declarative shadow rootsをtrueに設定する。
-
文字列からHTMLをパースする。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」メソッドはデフォルトで制限がない。設定の利用意図は
ページのライフサイクルの早い段階で1つまたは複数の設定を作成し、必要なときに再利用すること。これにより実装側で事前処理ができる。
設定オブジェクトは、設定辞書として取得できる。また、直接変更可能。
[Exposed =Window ]interface {
Sanitizer constructor (optional (SanitizerConfig or SanitizerPresets )= "default"); // 設定の取得:
configuration SanitizerConfig get (); // Sanitizerのリストやフィールドの変更:boolean allowElement (SanitizerElementWithAttributes );
element boolean removeElement (SanitizerElement );
element boolean replaceElementWithChildren (SanitizerElement );
element boolean allowAttribute (SanitizerAttribute );
attribute boolean removeAttribute (SanitizerAttribute );
attribute boolean setComments (boolean );
allow boolean setDataAttributes (boolean ); // スクリプトを実行するマークアップの除去
allow boolean removeUnsafe (); };
Sanitizer
には関連付けられたSanitizerConfig
configurationがある。
constructor(configuration)
メソッドの手順は次のとおり:
-
もしconfigurationが
SanitizerPresets
文字列であれば、次を行う:-
configurationを組み込みの安全なデフォルト設定に設定する。
-
validにset a configuration をconfigurationとtrueで
this
で呼び出した戻り値を設定する。 -
もしvalidがfalseなら、
TypeError
をスローする。
get()
メソッドの手順は以下の通りです:
-
config を this の configuration とする。
-
もし config["
elements
"] が存在する場合:-
すべての config["
elements
"] の element について:-
もし element["
attributes
"] が存在する場合:-
element["
attributes
"] を 昇順でソート した結果に設定する。 element["attributes
"] を attrA が less than item attrB で比較する。
-
-
もし element["
removeAttributes
"] が存在する場合:-
element["
removeAttributes
"] を 昇順でソート した結果に設定する。 element["removeAttributes
"] を attrA が less than item attrB で比較する。
-
-
-
config["
elements
"] を 昇順でソート した結果に設定する。 config["elements
"] を elementA が less than item elementB で比較する。
-
-
もし config["
removeElements
"] が存在する場合:-
config["
removeElements
"] を 昇順でソート した結果に設定する。 config["removeElements
"] を elementA が less than item elementB で比較する。
-
-
もし config["
replaceWithChildrenElements
"] が存在する場合:-
config["
replaceWithChildrenElements
"] を 昇順でソート した結果に設定する。 config["replaceWithChildrenElements
"] を elementA が less than item elementB で比較する。
-
-
もし config["
attributes
"] が存在する場合:-
config["
attributes
"] を 昇順でソート した結果に設定する。 config["attributes
"] を attrA が less than item attrB で比較する。
-
-
もし config["
removeAttributes
"] が存在する場合:-
config["
removeAttributes
"] を 昇順でソート した結果に設定する。 config["removeAttributes
"] を attrA が less than item attrB で比較する。
-
-
config を返す。
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 {
SanitizerAttributeNamespace required DOMString ;
name DOMString ?=
_namespace null ; };typedef (DOMString or SanitizerAttributeNamespace );
SanitizerAttribute dictionary {
SanitizerConfig sequence <SanitizerElementWithAttributes >;
elements sequence <SanitizerElement >;
removeElements sequence <SanitizerElement >;
replaceWithChildrenElements sequence <SanitizerAttribute >;
attributes sequence <SanitizerAttribute >;
removeAttributes boolean ;
comments boolean ; };
dataAttributes
2.4. 設定の不変条件
設定は開発者が目的に合わせて変更でき、また変更すべきである。方法としては、新たな設定辞書を一から作成する、既存のSanitizer
の
設定を修正メソッドで操作する、get()
で既存のSanitizer
の
設定を辞書として取得して修正し、新たなSanitizer
を作成する、などがある。
空の設定はすべてを許可する("unsafe"系メソッド、例えば
setHTMLUnsafe
で呼び出した場合)。"default"
の設定は組み込みの安全なデフォルト設定を含む。"safe"と"unsafe"サニタイザーメソッドではデフォルトが異なる点に注意。
全ての構成辞書が有効とは限りません。有効な構成は冗長(同じ要素を二重に許可するなど)や矛盾(同じ要素を許可と除去両方に指定するなど)を避けます。
有効な構成となるために守るべき条件がいくつかあります:
-
グローバル許可リストと削除リストの混在:
-
elements
またはremoveElements
は存在できるが、両方同時には存在できない。 両方とも欠落している場合、これはremoveElements
を « » に設定したことと同等である。 -
attributes
またはremoveAttributes
は存在できるが、両方同時には存在できない。 両方とも欠落している場合、これはremoveAttributes
を « » に設定したことと同等である。 -
dataAttributes
は概念的にattributes
許可リストの拡張である。dataAttributes
属性は、attributes
リストが使用される場合のみ許可される。
-
-
異なるグローバルリスト間の重複エントリ:
-
elements
,removeElements
, またはreplaceWithChildrenElements
の間に重複エントリ(同じ要素)は存在しない。 -
attributes
またはremoveAttributes
の間に重複エントリ(同じ属性)は存在しない。
-
-
同じ要素にローカルの許可リストと除去リストを混在させる場合:
-
attributes
リストが存在する場合、attributes
およびremoveAttributes
のリストは、両方・どちらか・またはどちらも同じ要素上で許可されます。 -
removeAttributes
リストが存在する場合、attributes
またはremoveAttributes
のリストは同じ要素上で許可されますが、両方は許可されません。
-
-
同じ要素での重複エントリ:
-
attributes
とremoveAttributes
は、同じ要素上で重複エントリはありません。
-
elements
要素許可リストでは、特定の要素に対して属性の許可や削除も指定できる。これは [HTML] の構造を反映したものであり、
グローバル属性と、特定要素に適用されるローカル属性の両方を知っている。
グローバル属性とローカル属性は混在可能だが、特定の属性が一方のリストで許可され、他方のリストで禁止されるような曖昧な構成は基本的に無効である点に注意。
グローバル attributes
| グローバル removeAttributes
| |
---|---|---|
ローカル attributes
| 属性は、いずれかのリストに一致する場合に許可されます。重複は許可されません。 | 属性はローカル許可リストに含まれている場合のみ許可されます。 グローバル除去リストとローカル許可リスト間で重複エントリは許可されません。 グローバル除去リストは、この要素には機能しませんが、ローカル許可リストを持たない他の要素には適用される場合があります。 |
ローカル removeAttributes
| 属性はグローバル許可リストに含まれていて、ローカル除去リストに含まれていない場合に許可されます。ローカル除去リストはグローバル許可リストの部分集合でなければなりません。 | 属性は両方のリストに含まれていない場合にのみ許可されます。 グローバル除去リストとローカル除去リスト間で重複エントリは許可されません。 |
ほとんどの場合、グローバルリストと要素ごとのリスト間で重複は許可されませんが、グローバル許可リストと要素ごとの除去リストの場合は、後者が前者の部分集合である必要があるという非対称性にご注意ください。上記の表の抜粋として、重複に関する部分だけを示すと以下の通りです:
グローバル attributes
| グローバル removeAttributes
| |
---|---|---|
ローカル attributes
| 重複は許可されません。 | 重複は許可されません。 |
ローカル removeAttributes
| ローカル除去リストはグローバル許可リストの部分集合でなければなりません。 | 重複は許可されません。 |
dataAttributes
設定により、カスタムデータ属性が許可されます。上記のルールは
カスタムデータ属性にも簡単に拡張できます。dataAttributes
を許可リストとみなした場合:
グローバル attributes
および dataAttributes
が設定されている場合
| |
---|---|
ローカル attributes
| すべてのカスタムデータ属性が許可されます。カスタムデータ属性がいずれかの許可リストに載っている場合は重複となるため許可されません。 |
ローカル removeAttributes
| カスタムデータ属性はローカル除去リストに載っていなければ許可されます。 カスタムデータ属性がグローバル許可リストに載っている場合は重複となるため許可されません。 |
これらのルールを言葉でまとめると:
-
グローバルリストとローカルリスト間の重複と相互作用:
-
グローバル
attributes
許可リストが存在する場合、すべての要素のローカルリストは:-
ローカル
attributes
許可リストが存在する場合、これらのリスト間で重複エントリは許可されません。 -
ローカル
removeAttributes
除去リストが存在する場合、その全てのエントリはグローバルattributes
許可リストにも含まれていなければなりません。 -
dataAttributes
が true の場合は、カスタムデータ属性がいずれの許可リストにも載っていてはいけません。
-
-
グローバル
removeAttributes
除去リストが存在する場合:-
ローカル
attributes
許可リストが存在する場合、これらのリスト間で重複エントリは許可されません。 -
ローカル
removeAttributes
除去リストが存在する場合、これらのリスト間で重複エントリは許可されません。 -
ローカル
attributes
許可リストとローカルremoveAttributes
除去リストの両方が存在することはできません。 -
dataAttributes
は存在してはいけません。
-
-
SanitizerConfig
config は、以下のすべての条件を満たす場合に 有効 です:
-
config は
elements
またはremoveElements
キーのいずれかを持ち、両方は持たないこと。 -
config は
attributes
またはremoveAttributes
キーのいずれかを持ち、両方は持たないこと。 -
Assert: すべての
SanitizerElementNamespaceWithAttributes
、SanitizerElementNamespace
、 およびSanitizerAttributeNamespace
アイテムは正規化済み(canonicalize a sanitizer element または canonicalize a sanitizer attribute を実行済み)であること。 -
config[
elements
]、 config[removeElements
]、 config[replaceWithChildrenElements
]、 config[attributes
]、 または config[removeAttributes
] が 存在する場合、 重複を含まないこと。 -
両方の config[
elements
] と config[replaceWithChildrenElements
] が 存在する場合、 両者の共通部分は 空でなければなりません。 -
両方の config[
removeElements
] と config[replaceWithChildrenElements
] が 存在する場合、 両者の共通部分は 空でなければなりません。 -
config[
attributes
] が 存在する場合:-
-
各 element について config[
elements
] の中で:-
element[
attributes
] および element[removeAttributes
] が 存在する場合、重複を含まないこと。 -
config[
attributes
] と element[attributes
] デフォルト付き « » の 共通集合 は 空 である。 -
element[
removeAttributes
] デフォルト付き « » は 部分集合である config[attributes
] の。 -
dataAttributes
が存在していてdataAttributes
が true の場合:-
element[
attributes
] に カスタムデータ属性 が含まれていないこと。
-
-
-
-
dataAttributes
が true の場合:-
config[
attributes
] に カスタムデータ属性 が含まれていないこと。
-
-
-
config[
removeAttributes
] が 存在する場合:-
config[
elements
] が 存在する場合、 各 element について config[elements
] の中で:-
element[
attributes
] と element[removeAttributes
] の両方が 存在してはいけません。 -
element[
attributes
] および element[removeAttributes
] が 存在する場合、重複を含まないこと。 -
共通集合(config[
removeAttributes
] と element[attributes
] デフォルト付き « » )は 空である。 -
共通集合(config[
removeAttributes
] と element[removeAttributes
] デフォルト付き « » )は 空である。
-
-
config[
dataAttributes
] は 存在しないこと。
-
注: 設定のセットを
dictionaryから行う場合、多少の正規化が行われます。
特に、許可リストと除去リストの両方が存在しない場合は、空の除去リストとして解釈されます。したがって {}
自体は
有効な設定ではありませんが、
{removeElements:[],removeAttributes:[]}
に正規化され、これは有効です。
この正規化ステップは、欠落した dictionary を空のものと一貫性を持たせるために選ばれました。つまり、
setHTMLUnsafe(txt)
を setHTMLUnsafe(txt, {sanitizer: {}})
と一貫性を持たせるためです。
3. アルゴリズム
Element
またはDocumentFragment
であるtarget、Element
であるcontextElement、文字列のhtml、辞書のoptions、真偽値のsafeを受け取る:
-
safeがtrueで、contextElementのローカル名が"
script
"、 かつcontextElementの名前空間がHTML名前空間 またはSVG名前空間である場合、returnする。 -
sanitizerをget a sanitizer instance from options にoptionsとsafeを渡して得る。
-
newChildrenをHTMLフラグメントパースアルゴリズム にcontextElement、html、trueを渡して得る。
-
fragmentを新しい
DocumentFragment
(ノード文書はcontextElementのノード文書)として作成する。 -
sanitizeを fragmentにsanitizerとsafeで実行する。
-
Replace allで target内をfragmentで置換する。
注: このアルゴリズムはSetHTMLOptions
と
SetHTMLUnsafeOptions
両方に対応しています。違いはデフォルトのみです。
-
sanitizerSpecを"
default
"に設定する。 -
もしoptions["
sanitizer
"] 存在するなら:-
sanitizerSpecにoptions["
sanitizer
"]を設定する。
-
-
Assert: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のconfigurationの値とする。
-
safeがtrueなら、configurationをremove unsafeをconfigurationに対して呼び出した結果に設定する。
-
sanitize core をnode、configuration、handleJavascriptNavigationUrlsはsafeで呼び出す。
ParentNode
node、SanitizerConfig
configuration、
boolean
handleJavascriptNavigationUrlsを使い、
nodeからDOMツリーを再帰的に処理します。手順は以下の通り:
-
-
Assert:childは
Text
、Comment
、Element
、 またはDocumentType
を 実装している。注: 現在、このアルゴリズムはHTMLパーサの出力に対してのみ呼ばれ、このアサーションは成立するはずです。
DocumentType
が現れるのはparseHTML
やparseHTMLUnsafe
のみです。 将来的に他のコンテキストで使う場合はこの前提を再検討する必要があります。 -
childが
DocumentType
を実装している場合は、continue。 -
childが
Comment
を実装している場合: -
その他の場合:
-
elementName を child の ローカル名 と 名前空間を持つ
SanitizerElementNamespace
とする。 -
configuration["
replaceWithChildrenElements
"] が存在し、 configuration["replaceWithChildrenElements
"] が elementName を含む場合:-
child に対して configuration と handleJavascriptNavigationUrls を用いて sanitize core を呼び出す。
-
続ける。
-
-
configuration["
removeElements
"] が存在し、 configuration["removeElements
"] が elementName を含む場合: -
configuration["
elements
"] が存在し、 configuration["elements
"] が 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] に設定する。
-
-
各 attribute を child の 属性リスト の中で:
-
attrName を
SanitizerAttributeNamespace
とし、attribute の ローカル名 と 名前空間 を設定する。 -
もし 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。
-
-
-
sanitize core を child に configuration と handleJavascriptNavigationUrls を渡して呼び出す。
-
-
javascript:
URLをナビゲーション時のみサポートしています。ナビゲーション自体はXSSの脅威ではないため、javascript:
URLへのナビゲーションのみを処理し、その他のナビゲーションは一般的に処理しません。
宣言的ナビゲーションは以下のカテゴリに分類されます:
-
アンカー要素(HTMLやSVG名前空間の
<a>
) -
フォーム要素(form actionとしてナビゲーションを発生させるもの)
-
[MathML]は どの要素でもアンカーのように扱えることを許容しています。
-
[SVG11]のアニメーション。
最初の2つは組み込みナビゲーションURL属性リストでカバーされます。
MathMLのケースは、この仕様に「名前空間単位のグローバル」な規則の定義がないため、個別の規則でカバーされます。
SVGアニメーションのケースは組み込みアニメーションURL属性リストでカバーされますが、SVGアニメーション要素の解釈はアニメーション対象によって変わり、サニタイズ中には最終的な対象が分からないため、sanitizeアルゴリズムはhref
属性のアニメーションはすべてブロックします。
-
urlをbasic URL parser にattributeの値を渡して得る。
-
urlが
failure
なら、falseを返す。 -
urlのschemeが"
javascript
"かどうかを返す。
3.2. 構成を変更する
構成変更メソッドは、Sanitizer
上のメソッドであり、
その構成を変更します。
これらのメソッドは有効性基準を維持します。
呼び出し元に対して、構成が変更されたかどうかを示すブール値を返します。
let s= new Sanitizer({ elements: [ "div" ]}); s. allowElement( "p" ); // Returns true. div. setHTML( "<div><p>" , { sanitizer: s}); // Allows `<div>` and `<p>`.
let s= new Sanitizer({ elements: [ "div" ]}); s. removeElement( "p" ); // Return false, as <p> was not previously allowed. div. setHTML( "<div><p>" , { sanitizer: s}); // Allows `<div>`. `<p>` is removed.
SanitizerElementWithAttributes
element with a SanitizerConfig
configuration:
-
グローバルの許可リストか除去リストのどちらを採用しているか、
-
そしてこれらのリストがすでに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 なら:-
削除する。 element["
attributes
"] から全ての item( 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 で、item[name
] 一致 element[name
] かつ item[namespace
] 一致 element[namespace
] であるものに設定する。 -
もし 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 を返す。
-
SanitizerElement
element を
SanitizerConfig
configuration から除去する:
-
グローバル許可リストまたは除去リストを持つかどうか
-
それらがすでに element を含んでいるかどうか
-
element を canonicalize a sanitizer element の結果に設定する。
-
modified を remove element を configuration["
replaceWithChildrenElements
"] から除去した結果に設定する。 -
それ以外の場合:
-
もし configuration["
removeElements
"] が element を含む場合:-
コメント: グローバル削除リストがあり、すでに element を含んでいる。
-
modified を返す。
-
-
コメント: グローバル削除リストがあり、element を含まない。
-
add element を configuration["
removeElements
"] に追加する。 -
true を返す。
-
SanitizerElement
element を SanitizerConfig
configuration に対して行うには:
-
element を、canonicalize a sanitizer element を element に対して実行した結果に設定する。
-
もし configuration["
replaceWithChildrenElements
"] が element を含むなら:-
false を返す。
-
-
Remove を用いて element を configuration["
removeElements
"] から除去する。 -
Add を用いて element を configuration["
replaceWithChildrenElements
"] に追加する。 -
true を返す。
SanitizerAttribute
attribute を SanitizerConfig
configuration に対して行うには:
Note: このメソッドは、グローバル許可リストかグローバル削除リストかの2つの場合を区別する。もしグローバル許可リストに attribute を追加するなら、妥当性基準を維持するために、要素ごとの許可/削除リストを調整する追加作業が必要となる場合がある。
-
attribute を、canonicalize a sanitizer attribute を attribute に対して実行した結果に設定する。
-
もし configuration["
attributes
"] が存在するなら:-
Comment: グローバル許可リストがある場合、attribute を追加する必要がある。
-
もし configuration["
dataAttributes
"] が true で、かつ attribute が カスタムデータ属性なら、false を返す。 -
もし configuration["
attributes
"] が 含む attribute 場合は false を返す。 -
Comment: 要素ごとの許可/削除リストを調整する。
-
もし configuration["
elements
"] が 存在するなら:-
各 element を configuration["
elements
"] の中で:-
もし element["
attributes
"] デフォルト付き « » 含む attribute なら:-
削除する。attribute を element["
attributes
"] から。
-
-
検証:element["
removeAttributes
"] デフォルト付き « » が 含まない attribute。
-
-
-
追加する。attribute を configuration["
attributes
"] に。 -
true を返す。
-
-
それ以外の場合:
-
コメント:グローバル削除リストがある場合、attribute を削除する必要がある。
-
もし configuration["
removeAttributes
"] が 含まない attribute なら:-
false を返す。
-
-
削除する。attribute を configuration["
removeAttributes
"] から。 -
true を返す。
-
SanitizerConfig
configuration から削除する:
Note: このメソッドはグローバル許可リストかグローバル削除リストかの2つの場合を区別する。 グローバル削除リストに attribute を追加した場合、妥当性基準を保つために要素ごとの許可・削除リストの修正が必要となることがある。 グローバル許可リストから attribute を削除する場合も、ローカル削除リストからも削除する必要がある場合がある。
-
attribute を サニタイザー属性の正規化の結果に設定する。 attribute を渡す。
-
もし configuration["
attributes
"] 存在する場合:-
コメント:グローバル許可リストがある場合、attribute を追加する必要がある。
-
もし configuration["
attributes
"] が 含まない attribute なら:-
false を返す。
-
-
コメント:要素ごとの許可・削除リストを修正する。
-
もし configuration["
elements
"] 存在する場合:-
各 element を configuration["
elements
"] の中で:-
もし element["
removeAttributes
"] デフォルト付き « » 含む attribute なら:-
削除する。attribute を element["
removeAttributes
"] から。
-
-
-
-
削除する。attribute を configuration["
attributes
"] から。 -
true を返す。
-
-
それ以外の場合:
-
コメント:グローバル削除リストがある場合、attribute を追加する必要がある。
-
もし configuration["
removeAttributes
"] が 含む attribute なら false を返す。 -
コメント:要素ごとの許可・削除リストを修正する。
-
もし configuration["
elements
"] 存在する場合:-
各 element を configuration["
elements
"] の中で:-
もし element["
attributes
"] デフォルト付き « » 含む attribute なら:-
削除する。attribute を element["
attributes
"] から。
-
-
もし element["
removeAttributes
"] デフォルト付き « » 含む attribute なら:-
削除する。attribute を element["
removeAttributes
"] から。
-
-
-
-
追加する。attribute を configuration["
removeAttributes
"] に。 -
true を返す。
-
SanitizerConfig
configuration に設定する:
SanitizerConfig
configuration に設定する:
-
もし configuration["
attributes
"] が 存在しないなら、false を返す。 -
もし configuration["
dataAttributes
"] が allow と等しいなら、false を返す。 -
もし allow が true なら:
-
削除する。configuration["
attributes
"] から attr( attr が カスタムデータ属性の場合)。 -
もし configuration["
elements
"] 存在する場合:-
各 element を configuration["
elements
"] の中で:-
もし element[
attributes
] 存在する場合:-
削除する。element[
attributes
] から attr( attr が カスタムデータ属性の場合)。
-
-
-
-
-
configuration["
dataAttributes
"] に allow を設定する。 -
true を返す。
SanitizerConfig
configuration から次を行う:
Note: このアルゴリズム名は 安全でないものを削除する だが、 この仕様における「unsafe」 という用語は、 ドキュメントに挿入したときに JavaScript が実行される内容を指すことに限定して使っている。このメソッドは XSS の機会を除去する。
-
検証:キー集合が 組み込み安全ベースライン設定 等しい «[ "
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:
-
configurationを allowCommentsAndDataAttributesで正規化する。
-
もしconfigurationが有効でないなら、falseを返す。
-
sanitizerのconfigurationをconfigurationに設定する。
-
trueを返す。
3.4. 構成を正規化する
The Sanitizer
は処理を容易にするためにconfigurationを正規化された形式で保存する。
elements
リスト {elements: ["div"]}
は
{elements: [{name: "div", namespace: "http://www.w3.org/1999/xhtml"}]
} として保存される)。
SanitizerConfig
configuration と、boolean の allowCommentsAndDataAttributes を用いる:
Note: configuration は
JavaScript の値を SanitizerConfig
に変換した [WebIDL]
の結果であると仮定する。
-
configuration["
elements
"] と configuration["removeElements
"] のいずれも存在しない場合、 set configuration["removeElements
"] を « » にする。 -
configuration["
attributes
"] と configuration["removeAttributes
"] のいずれも存在しない場合、 set configuration["removeAttributes
"] を « » にする。 -
configuration["
elements
"] が存在する場合:-
elements を « » とする。
-
各 configuration["
elements
"] の element について、次を行う:-
Append として、 canonicalize a sanitizer element with attributes の結果 element を elements に追加する。
-
-
configuration["
elements
"] を elements に設定する。
-
-
configuration["
removeElements
"] が存在する場合:-
elements を « » とする。
-
各 configuration["
removeElements
"] の element について、次を行う:-
Append として、 canonicalize a sanitizer element の結果 element を elements に追加する。
-
-
configuration["
removeElements
"] を elements に設定する。
-
-
configuration["
replaceWithChildrenElements
"] が存在する場合:-
elements を « » とする。
-
各 configuration["
replaceWithChildrenElements
"] の element について、次を行う:-
Append として、 canonicalize a sanitizer element の結果 element を elements に追加する。
-
-
configuration["
replaceWithChildrenElements
"] を elements に設定する。
-
-
configuration["
attributes
"] が存在する場合:-
attributes を « » とする。
-
各 configuration["
attributes
"] の attribute について、次を行う:-
Append として、 canonicalize a sanitizer attribute の結果 attribute を attributes に追加する。
-
-
configuration["
attributes
"] を attributes に設定する。
-
-
configuration["
removeAttributes
"] が存在する場合:-
attributes を « » とする。
-
各 configuration["
removeAttributes
"] の attribute について、次を行う:-
Append として、 canonicalize a sanitizer attribute の結果 attribute を attributes に追加する。
-
-
configuration["
removeAttributes
"] を attributes に設定する。
-
-
configuration["
comments
"] が存在しない場合、 set configuration["comments
"] を allowCommentsAndDataAttributes に設定する。 -
configuration["
attributes
"] が存在し、 configuration["dataAttributes
"] が存在しない場合、 set configuration["dataAttributes
"] を allowCommentsAndDataAttributes に設定する。
SanitizerElementWithAttributes
element に対して行うには:
-
result を サニタイザー要素の正規化の結果に設定する。 element を渡す。
-
element が dictionary である場合:
-
もし element["
attributes
"] 存在する場合:-
attributes を « » に設定する。
-
各 attribute を element["
attributes
"] の中で:-
追加する。サニタイザー属性の正規化の結果(attribute を渡す)を attributes に。
-
-
設定する。result["
attributes
"] に attributes を。
-
-
もし element["
removeAttributes
"] 存在する場合:-
attributes を « » に設定する。
-
各 attribute を element["
removeAttributes
"] の中で:-
追加する。サニタイザー属性の正規化の結果(attribute を渡す)を attributes に。
-
-
設定する。result["
removeAttributes
"] に attributes を。
-
-
-
もし result["
attributes
"] も result["removeAttributes
"] も 存在しない場合:-
設定する。result["
removeAttributes
"] に « » を。
-
-
result を返す。
SanitizerElement
element に対し、
canonicalize a sanitizer name を
element と HTML namespace を既定の名前空間として実行した結果を返す。
SanitizerAttribute
attribute に対し、
canonicalize a sanitizer name を
attribute と 既定の名前空間として null を用いて実行した結果を返す。
-
Assert: name は
DOMString
か、dictionary のいずれかである。 -
もし name が
DOMString
なら、«[ "name
" → name, "namespace
" → defaultNamespace]» を返す。 -
Assert: name は dictionary であり、 name["name"] と name["namespace"] の両方が 存在する。
-
もし name["namespace"] が空文字列なら、null に設定する。
-
次を返す: «[
"name
" → name["name"],
"namespace
" → name["namespace"]
]»。
3.5. 補助アルゴリズム
この仕様で使用される、正規化済みの
element
と attribute name
のリストにおける要素の所属判定は、"name
" と "namespace
" の両方が一致するかどうかで行われる:
SanitizerElement
を含む)が
集合の積集合 と同じになるが、
集合
の各エントリは事前に 正規化 される:
-
set A を « [] » にする
-
set B を « [] » にする
-
各 entry について A を繰り返し、 append で canonicalize a sanitizer name の結果を set A に追加する。
-
各 entry について B を繰り返し、 append で canonicalize a sanitizer name の結果を set B に追加する。
-
set A と set B の 積集合 を返す。
3.6. 組み込み
組み込みは四つある:
組み込みの安全なデフォルト構成は次の通りである:
{ "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" : "rel" , "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" : "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 } ] } ], "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:
」によるナビゲーションが「unsafe」となるものであり、次の通りです:
«[
[
{ "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
" → "iframe
", "namespace
" → HTML 名前空間
},
{ "name
" → "src
", "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
" → "animateMotion
", "namespace
" → SVG 名前空間 },
{ "name
" → "attributeName
", "namespace
" → null
}
],
[
{ "name
" → "animateTransform
", "namespace
" → SVG 名前空間 },
{ "name
" → "attributeName
", "namespace
" → null
}
],
[
{ "name
" → "set
", "namespace
" → SVG 名前空間 },
{ "name
" → "attributeName
", "namespace
" → null
}
],
]»
4. セキュリティに関する考慮事項
Sanitizer APIは、供給されたHTMLコンテンツを走査し、構成に従って要素や属性を除去することで、DOMベースのクロスサイトスクリプティング(XSS)を防ぐことを目的としています。指定されたAPIは、スクリプト可能なマークアップを残すSanitizerオブジェクトの構築をサポートしてはならず、そうすることは脅威モデル上のバグとなります。
とはいえ、Sanitizer APIの正しい使用では保護できないセキュリティ上の問題もあり、これらのシナリオは以下のセクションで説明します。
4.1. サーバーサイド反射型・保存型XSS
このセクションは規範的ではありません。
Sanitizer APIはDOM内のみで動作し、既存のDocumentFragmentを走査・フィルタする機能を追加します。Sanitizerはサーバーサイドの反射型または保存型XSSには対応しません。
4.2. DOMクラッタリング
このセクションは規範的ではありません。
DOMクラッタリングとは、悪意のあるHTMLがid
やname
属性を使って要素に名前を付けることで、DOM内の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は、文字列をノードツリーへ変換する関数のみを提供します。すべてのSanitizer関数が暗黙的にコンテキストを指定します:Element.setHTML()
は現在の要素を使用し、Document.parseHTML()
は新しいドキュメントを作成します。そのためSanitizer
APIは直接的に変異型XSSの影響を受けません。
開発者が例えば.innerHTML
などによってサニタイズされたノードツリーを文字列として取得し、それを再度パースする場合、変異型XSSが発生する可能性があります。このような利用は推奨されません。HTMLを文字列として処理・渡す必要がある場合でも、その文字列は信頼できないものとみなし、DOMに挿入する際は再度サニタイズする必要があります。言い換えれば、サニタイズ後に直列化したHTMLツリーはもはや「サニタイズ済み」とみなすことはできません。
mXSSのより詳しい解説は、[MXSS]に記載されています。
5. 謝辞
本仕様はcure53の[DOMPURIFY]、
Internet Explorerのwindow.toStaticHTML()
、
そしてBen Buckschによる
[HTMLSanitizer]に触発されています。
また、Anne van Kesteren、Krzysztof Kotowicz、Tom Schuster、Luke Warlow、
Guillaume Weghsteen、Mike Westには貴重なフィードバックをいただきました。