1. はじめに
ウェブプラットフォームは、機能やAPIの拡大を続けており、より豊かな機能、開発者にとって使いやすい設計、パフォーマンスの向上を提供しています。しかし、開発者がこれらのブラウザ機能やAPIの一部をアプリケーション内で選択的に有効化・無効化・挙動変更できる仕組みが不足しています:
- 開発者は、アプリケーションの安全性やパフォーマンス確保のため、特定のブラウザ機能やAPIへのアクセスを選択的に無効化したい場合があります。これは、アプリケーション内で自身や第三者コンテンツが不要または予期しない挙動を持ち込むことを防ぐためです。
- 開発者は、デフォルトで無効化されている場合がある特定のブラウザ機能やAPIへのアクセスを選択的に有効化したい場合があります。例えば、一部の機能は埋め込みコンテキストでは明示的に有効化しない限り無効化されている場合があり、他のポリシー要件に従う必要がある場合もあります。
- 開発者は、ポリシーを利用して、特定の機能やAPIの利用有無についてクライアントや埋め込み先へ約束を示したい場合があります。例えば、ブラウザで特定の「高速経路」最適化を有効にする、あるいは他の埋め込み先(SNS、検索エンジンなど)が定める要件への準拠を約束するためです。
この仕様は、上記のユースケースに対応するポリシー機構を定義します。
本仕様は以前「Feature Policy」という名称でした。
2. 例
SecureCorp Inc.は、アプリケーション内でFullscreenおよびGeolocation APIの利用を無効化したいと考えています。以下のHTTPレスポンスヘッダーを配信することで、パーミッションポリシーを定義できます:
Permissions-Policy: fullscreen=(), geolocation=()
空のオリジンリストを指定することで、指定した機能はオリジンに関係なく、すべてのドキュメント(ネストされたドキュメントも含む)で無効化されます。
Geolocationは、すべてのクロスオリジンフレームでデフォルトで無効化されています。FastCorp
Inc.は、自サイト上の特定のクロスオリジンiframeでジオロケーションを有効化したいと考えています。これにはiframe要素に「allow」属性を追加します:
<iframe src="https://other.com/map" allow="geolocation"></iframe>
iframe属性を使うことで、同じオリジンのドキュメントであっても、特定のフレームだけ機能を選択的に有効化できます。
SecureCorp
Inc.は、攻撃者が自身のiframeをSecureCorpのページに埋め込める場合でも、独自オリジンおよび「https://example.com」のオリジンを除き、すべての子孫ナビゲーション可能内でGeolocation
APIの利用を完全に無効化したいと考えています。このために、以下のようなHTTPレスポンスヘッダーを配信してGeolocation用の制限付きパーミッションポリシーを定義します:
Permissions-Policy: geolocation=(self "https://example.com")
許可リストは1つ以上のオリジンのリストであり、アプリケーションのオリジン(オプションで「self」キーワード付き)や第三者オリジンを含めることができます。
このポリシーが有効の場合、通常通りiframe属性「allow」でジオロケーションを許可できますが、APIの利用権限が与えられるのはhttp://example.comまたはSecureCorp自身のコンテンツのみです。
SecureCorp Inc.はドメインを再編し、Geolocation
APIの利用権限を自身のオリジン(「https://example.com」)および3つのサブドメイン(「https://geo.example.com」、「https://geo2.example.com」、「https://new.geo2.example.com」)に委譲する必要があります。他の閲覧コンテキストではGeolocation
APIの利用を無効化したままにする必要があります。これには以下のHTTPレスポンスヘッダーを配信します:
Permissions-Policy: geolocation=(self "https://example.com" "https://geo.example.com" "https://geo2.example.com" "https://new.geo2.example.com")
これで目的は達成できますが、SecureCorp
Inc.が「https://example.com」の任意のサブドメインへの委譲も安全と判断する場合、HTTPレスポンスヘッダーは次のようにできます:
Permissions-Policy: geolocation=(self "https://example.com" "https://*.example.com")
上記のヘッダーでは、「https://geo.example.com」、「https://geo2.example.com」、「https://new.geo2.example.com」などのサブドメインだけでなく、「https://example.com」の他のサブドメインでもAPIを利用できます。ただし、「https://example.com」自体は「https://*.example.com」の許可リストには含まれないため、別途追加する必要があります。
SecureCorp Inc.はサービスを再編し、Geolocation
APIの利用権限を自身のオリジン(「https://example.com」)および3つの非デフォルトポート(「https://example.com:444」、「https://example.com:445」、「https://example.com:446」)に委譲する必要があります。他の閲覧コンテキストではGeolocation
APIの利用を無効化したままにします。これには以下のHTTPレスポンスヘッダーを配信します:
Permissions-Policy: geolocation=(self "https://example.com" "https://example.com:444" "https://example.com:445" "https://example.com:446")
これで目的は達成できますが、SecureCorp
Inc.が「https://example.com」の任意のポートへの委譲も安全と判断する場合、HTTPレスポンスヘッダーは次のようにできます:
Permissions-Policy: geolocation=(self "https://example.com:*")
上記ヘッダーでは、「https://example.com:444」、「https://example.com:445」などのポートだけでなく、「https://example.com」の他のポートでもAPIを利用できます。
JSPlaygroundCorp Inc.はユーザー生成ウェブアプリケーションをホストしたいと考えていますが、ブラウザに各アプリケーションの 強力な機能利用権限を個別に管理させたいと考えています。これには各ウェブコンテンツや制作者ごとに個別のサブドメインを発行し、トップレベルドキュメントとしてナビゲートすることで実現できます(フレームワークとユーザーコンテンツはsame-originのiframeで分離可能)。
ユーザーはブラウザで操作対象のドメイン(トップレベルドメイン)に権限を付与するため、この分離が必要です。
この場合、JSPlaygroundCorpは自社ドメインからiframeのallow属性を使ってユーザー生成アプリケーションをiframe化することは避けるべきです。そうすると全てに自社ドメインの権限が与えられてしまうためです。
PlatformCorp Inc.は、トップレベルドメインで組み込み可能な第三者コンポーネントのマーケットプレイスを提供したいと考えています。「強力な機能」であるgetUserMedia()
APIなどの利用権限を責任を持って委譲したいと考えています。どのコンポーネントアプリが機能を必要とするかは独自の「インストール」UXで管理し、エンドユーザーが主導権を持てるようにします。
カメラとマイクは、すべてのクロスオリジンフレームでデフォルトで無効化されています。各第三者コンポーネントはサブドメインを持ち、クロスオリジンiframeで埋め込むことができます。PlatformCorpはallow属性をiframe要素に指定することで、各サブドメインごとにカメラやマイクへのアクセス権限を委譲できます。
例えば、コンポーネント「app1」はカメラを、「app2」はマイクを、「app3」は両方を使えるようにするには、以下のようなiframeになります:
<iframe
allow="camera https://app1.site.com https://app3.site.com;
microphone https://app2.site.com https://app3.site.com"
src="https://doc1.site.com"
sandbox="allow-same-origin allow-scripts">
</iframe>
iframe属性を使うことで、同じオリジンのドキュメントであっても、特定のフレームだけ機能を選択的に有効化できます。実際のサンドボックストークンのリストはもっと長くなる場合もあります。
ブラウザは通常、ユーザーがトップレベルドメインに権限を付与するため、ユーザーがPlatformCorpを既に信頼している場合、コンポーネントがカメラやマイクアクセスを要求しても追加の許可プロンプトは表示されないことがあります。
3. その他および関連する仕組み
[HTML5]では、sandbox属性がiframe要素に定義されており、開発者が潜在的に信頼できないコンテンツを含めるリスクを減らすために、フォーム送信やスクリプト・プラグイン実行の禁止などの制限を課すことができます。sandboxディレクティブは、[CSP2]で定義されており、フレーム以外のリソースにも同様の制限をHTTPレスポンスヘッダー(Content-Security-Policy: sandbox)などで要求できます。これらの仕組みにより開発者は以下のことが可能です:
- CSPであらゆるリソースに対してサンドボックスポリシーを設定・カスタマイズできる。
- アプリケーション内の各
iframe要素ごとに個別のサンドボックスポリシーを設定・カスタマイズできる。
しかし、上記の仕組みにはいくつか制限があります。開発者はすべてのコンテキストに自動的にポリシーを適用できず、第三者コンテンツによるフレーム挿入など、制御できない場合は一貫性ある強制が困難です。デフォルトで無効化されている機能を選択的に有効化する仕組みもありません。サンドボックスはすべての機能を自動的に無効化し、個別に再有効化する必要があるため、機能セットの拡張が互換性リスクなしには不可能です。
Permissions Policyはsandbox機構と組み合わせて使うことを想定しており(sandboxで既に制御可能な機能は重複して制御しません)、上記の制限を解決するための拡張性ある仕組みを提供します。
4. フレームワーク
4.1. ポリシー制御機能
ポリシー制御機能とは、permissions policyで参照することでドキュメント内で有効化・無効化できるAPIや挙動です。
ポリシー制御機能はトークンによって識別されます。これは、ポリシー指令で用いられる文字列です。
各ポリシー制御機能には、デフォルト許可リストがあり、トップレベルナビゲーション内のドキュメントでその機能が利用可能か、および子ナビゲーションでの継承方法が定義されています。
ユーザーエージェントは、サポート機能のセットを持ちます。これは、ポリシーによって制御可能な機能の集合です。ユーザーエージェントは、すべての機能をサポートする必要はありません。
4.2. ポリシー
permissions policyは、以下の構造体であり、次の項目を持ちます:
空のpermissions
policyは、すべてのサポート機能に対して"Enabled"を含む継承ポリシーを持ち、宣言およびレポート設定がいずれも空の順序付きマップとなっているpermissions
policyです。
4.3. 継承ポリシー
機能の継承ポリシーfeatureは、継承ポリシーのfeatureキーの値です。permissions policyが初期化されると、継承ポリシーにはすべてのサポート機能に対する値が含まれます。
4.4. ヘッダーポリシー
ヘッダーポリシーとは、ポリシー指令のリストであり、HTTPヘッダーを通じてドキュメントと共に配信されます。これがドキュメントのpermissions policyの宣言ポリシーを形成します。
4.5. コンテナポリシー
ヘッダーポリシーに加え、各子ナビゲーションにはコンテナポリシーがあります。これはポリシー指令であり、空の場合もあります。コンテナポリシーはナビゲーションコンテナの属性で設定できます。
コンテナポリシーは、子ナビゲーションに読み込まれる任意のDocumentの継承ポリシーに影響を与えます。(§ 9.7 コンテナ内の機能の継承ポリシー定義参照)
iframeのallowfullscreenやallow属性によって間接的に設定されます。今後の改訂で、完全なコンテナポリシーを明示的に宣言する仕組みが導入される可能性があります。
4.6. ポリシー指令
ポリシー指令とは、順序付きマップであり、ポリシー制御機能を対応する許可リストにマッピングします。
ポリシー指令は、HTTPヘッダーではsf-dictionary構造のシリアル化、HTML属性ではASCIIシリアル化として表現されます。
4.7. 許可リスト
パーミッションポリシーの許可リストは、概念的にオリジンの集合です。許可リストは以下のいずれかです:
- 特別な値
*(すべてのオリジンを表します) - 構造体(以下を含む):
- expressions:順序付きのpermissions-source-expressionの集合
- self-origin:originまたは
null - src-origin:originまたは
null
'self'、'src'、'none'は、ヘッダーや属性文字列で許可リストのテキスト表現として使われます。これらのキーワードは解析時に文脈で解釈され、参照するオリジンのみが許可リストに保存されます。キーワード自体は許可リストの一部ではありません。
許可リストがオリジンoriginに一致するか判定するには、以下の手順を実行します:
注:HTTPSスキームが必要なため、CSP形式のワイルドカード一致は使用しません。
-
許可リストのself-originがnullでなく、originと同一オリジンドメインなら、trueを返す。
-
許可リストのsrc-originがnullでなく、originと同一オリジンドメインなら、trueを返す。
-
もしoriginが不透明オリジンなら、falseを返す。
-
urlを、url parserにoriginのserializationを渡した結果とする。
-
各permissions-source-expressionitemについて、許可リストのexpressions内で実行する:
-
Does url match expression in origin with redirect count?をurl、item、origin、0で実行してtrueなら、trueを返す。
-
-
falseを返す。
4.8. デフォルト許可リスト
すべてのポリシー制御機能にはデフォルト許可リストが存在します。デフォルト許可リストは、Documentが宣言ポリシーなしでトップレベルナビゲーションで利用される場合、その機能が許可されるかどうか、またその機能へのアクセスが子ナビゲーションに自動的に委譲されるかどうかを決定します。
デフォルト許可リストは、以下のいずれかです:
*- この機能は、デフォルトでDocumentがトップレベルナビゲーションおよびすべての子ナビゲーションで許可されます。コンテナポリシーをナビゲーションコンテナで明示的に指定することで、子ナビゲーションで無効化できます(または、任意のナビゲーションで
Documentに適切なPermissions-Policyヘッダーを付与することで無効化できます)。 'self'- この機能は、デフォルトでドキュメントが
トップレベルのナビゲーションにある場合、または
子ナビゲーションで、そのドキュメントが
親の親のドキュメントと同一オリジンであり、かつその
Documentで許可されている場合に利用可能です。 一方、子ナビゲーションで、そのドキュメントが親の親のドキュメントとクロスオリジンの場合、デフォルトでは許可されません。
5. Permissions Policy シリアル化
5.1. HTML属性のシリアル化
ポリシー指令は、HTML属性内では以下のABNFの通りASCIIシリアル化で表現されます:
serialized-permissions-policy = serialized-policy-directive *(";" serialized-policy-directive)
serialized-policy-directive = feature-identifier RWS allow-list
feature-identifier = 1*( ALPHA / DIGIT / "-")
allow-list = allow-list-value *(RWS allow-list-value)
allow-list-value = permissions-source-expression / "*" / "'self'" / "'src'" / "'none'"
permissions-source-expression = scheme-source / host-source
'self'" は、allowlist 内でオリジンとして使用できます。そのように使用された場合、それは オリジン を指し、Document
に含まれる permissions policy になります.
5.2. 構造化ヘッダーのシリアル化
ポリシー指令は、HTTPヘッダーではStructured Fieldとして表現されます。[RFC8941]この表現では、ポリシー指令はDictionaryとして表されます。
各Dictionaryのメンバーは、featureと許可リストを関連付けます。メンバー名はTokenでなければなりません。Tokenがユーザーエージェントの対応機能名でない場合、そのDictionaryメンバーは処理手順で無視されます。
メンバー値は許可リストを表し、次のいずれかでなければなりません:
-
ASCII permissions-source-expressionを含む文字列
-
Token
* -
Token
self -
上記いずれかの要素を0個以上含むInner List
メンバー値には、"report-to"という名前のパラメータを持てます。その値は文字列でなければなりません。他のパラメータは無視されます。
Inner List内の他の項目は処理手順で無視され、メンバー値はそれらが存在しないものとして扱われます。その他の形式のメンバー値は、そのDictionaryメンバー全体が処理手順で無視されます。
6. 配信
6.1. `Permissions-Policy`
HTTPヘッダーフィールド
`Permissions-Policy` HTTPヘッダーフィールドは、response(サーバーからクライアント)で利用され、クライアントが適用すべきpermissions
policyを伝達できます。
`Permissions-Policy`は構造化ヘッダーであり、値はDictionaryでなければなりません。そのABNFは次の通りです:
PermissionsPolicy = sf-dictionary
Dictionaryの意味論は§ 5.2 構造化ヘッダーのシリアル化で定義されています。
処理手順は§ 9.2 辞書とオリジンからポリシーを構築で定義されています。
6.2.
iframe要素のallow属性
iframe要素はallow属性を持ち、この属性にはASCIIシリアル化されたポリシー指令が含まれます。
属性で指定された機能の許可リストが空の場合、デフォルト値は'src'となり、これはiframeのsrc属性に指定されたURLのオリジンを表します。
空でない場合、allow属性は、認識された各機能の許可リストを、iframe要素のcontent navigableのコンテナポリシーに追加することになります(生成時)。
6.3. レガシー機能をサポートする追加属性
Permissions Policyで制御されるいくつかの機能には、既存のiframe属性が定義されています。本仕様では、これらの属性がiframeのcontent navigableのコンテナポリシーに影響を与えるように再定義します。
6.3.1. allowfullscreen
allowfullscreen
iframe
属性は
requestFullscreen()
へのアクセスを制御します。
iframe要素がallow属性を持ち、その値に"fullscreen"トークンが含まれる場合、allowfullscreen属性は効果を持ちません。
それ以外の場合、iframe要素にallowfullscreen属性があると、"fullscreen"機能に対して*の許可リストが生成時にiframe要素のcontent navigableのコンテナポリシーに追加されます。
<iframe allow="fullscreen">の動作とは異なります。既存のallowfullscreen利用との互換性のためです。iframe要素にallow="fullscreen"とallowfullscreenが両方ある場合は、より制限的なallow="fullscreen"の許可リストが使われます。
7. スクリプトによるポリシーの内省
7.1. 概要
ドキュメントで有効な現在のポリシーは、スクリプトによって観察できます。これは、例えば機能が有効かどうかを他の方法で判断できない場合に、どのユーザーインターフェースを表示するべきかの判断に利用できます。(一部の機能は失敗モードが観察できなかったり、機能検出の副作用が望ましくない場合があります。)
ドキュメントとiframeの両方は、PermissionsPolicyオブジェクトを提供しており、それを使って適用されているパーミッションポリシーを調査できます。
7.1.1. ドキュメントポリシー
現在有効なポリシーを取得するには、document.permissionsPolicyを使います。これはPermissionsPolicyオブジェクトを返し、以下のことが可能です:
-
指定した機能が現在のドキュメントで許可されているか否かの状態を問い合わせる
-
現在のドキュメントで利用可能な(許可されていなくても)すべての機能の一覧を取得する
-
現在のドキュメントで許可されている機能の一覧を取得する
-
現在のドキュメントにおける指定機能の許可リストを取得する
<!doctype html>
<script>
const policy = document.permissionsPolicy;
// このドキュメントでWebUSBが使える場合はtrueになる。
const can_use_usb = policy.allowsFeature('usb');
// https://example.com の新しいフレームでWebXRが許可されている場合はtrue。
if (policy.allowsFeature('xr-spatial-tracking', 'https://example.com')) {
// UIで https://example.com のフレーム作成を表示。
} else {
// 別のUIを表示。
}
// 決済リクエストが許可されているオリジンのリスト取得。すべてのオリジン許可の場合は ['*']。
const allowed_payment_origins = policy.getAllowlistForFeature('payment');
// このドキュメントでサポートされている(許可されていないものも含む)すべての機能一覧。
// 結果は文字列の配列。
const all_features = policy.features();
if (all_features.includes('geolocation')) {
// サードパーティの地図サービスを子フレームとして追加。
}
</script>
7.1.2. フレームポリシー
iframe要素のポリシーも、それを含むドキュメントから調査可能です。この場合のポリシーオブジェクトは、そのフレームの可観測ポリシーを表し、現在のドキュメントおよびiframe要素の属性のみに依存します。実際にフレーム内で機能が許可されているかどうかは示されません。フレーム内のドキュメントがHTTPヘッダーで独自のポリシーを適用している場合や、初期位置から他のオリジンに遷移している場合もあるためです。iframe要素のネストされたナビゲーション内の有効なポリシーを公開すると、クロスオリジン文書の挙動について情報漏洩が発生する可能性があります。
<!doctype html>
<iframe id="frame" allow="fullscreen; xr-spatial-tracking"></iframe>
<script>
const iframe_element = document.getElementById("frame");
const iframe_policy = iframe_element.permissionsPolicy;
// フレーム内のドキュメントでWebXRが許可されている場合はtrue
if (iframe_policy.allowsFeature('xr-spatial-tracking')) {
// VRコントロールを表示
}
</script>
iframe要素の可観測ポリシーは、フレームに実際に読み込まれているコンテンツ(クロスオリジン情報漏洩防止のため)や、ドキュメントツリーに存在するかどうかに依存しません。
<!doctype html>
<!-- このフレームは、src属性で指定されたドキュメントがロードされた時、fullscreenが許可されないはず -->
<iframe id="frame" allow="fullscreen https://example.com" src="https://example.net/" ></iframe>
<script>
const iframe_element = document.getElementById("frame");
const iframe_policy = iframe_element.permissionsPolicy;
// src属性に指定されたURLはポリシーでfullscreenが許可されていないためfalseになる。
const is_fullscreen_allowed_in_frame = iframe_policy.allowsFeature('fullscreen');
const new_frame = document.createElement('iframe');
new_frame.allow = 'sync-xhr';
// このiframeは、src属性で指定されるURLに関係なくsync-xhrが許可されているためtrue。
const is_sync_xhr_allowed = new_frame.permissionsPolicy.allowsFeature('sync-xhr');
</script>
7.2. permissionsPolicyオブジェクト
[Exposed =Window ]interface {PermissionsPolicy boolean (allowsFeature DOMString ,feature optional DOMString );origin sequence <DOMString >();features sequence <DOMString >();allowedFeatures sequence <DOMString >(getAllowlistForFeature DOMString ); };feature partial interface Document { [SameObject ]readonly attribute PermissionsPolicy ; };permissionsPolicy partial interface HTMLIFrameElement { [SameObject ]readonly attribute PermissionsPolicy ; };permissionsPolicy
PermissionsPolicyオブジェクトは、関連ノードを持ちます。これはNodeです。関連ノードは、PermissionsPolicyオブジェクト作成時に設定されます。
PermissionsPolicyオブジェクトは、デフォルトオリジンを持ちます。これはoriginであり、値はPermissionsPolicyオブジェクトの関連ノードの状態によって決まります:
-
PermissionsPolicyオブジェクトの関連ノードがDocumentの場合、そのDocumentのoriginがデフォルトオリジンになります。 -
PermissionsPolicyオブジェクトの関連ノードがElementの場合、そのElementの宣言オリジンがデフォルトオリジンになります。
各Documentはポリシーオブジェクトを持ちます。これはPermissionsPolicyインスタンスであり、関連ノードがそのDocumentです。
DocumentのpermissionsPolicyIDL属性は、取得時にDocumentのポリシーオブジェクトを返さなければなりません。
各iframe要素はポリシーオブジェクトを持ちます。これはPermissionsPolicyインスタンスであり、関連ノードがその要素です。
iframeのpermissionsPolicyIDL属性は、取得時にiframeのポリシーオブジェクトを返さなければなりません。
allowsFeature(feature, origin)メソッドは以下の手順を実行しなければなりません:
-
originが省略された場合、この
PermissionsPolicyオブジェクトのデフォルトオリジンをoriginに設定する。 -
policyをこの
PermissionsPolicyオブジェクトの関連ノードに対する可観測ポリシーとする。 -
featureがpolicyでoriginに許可されていればtrueを返す。
-
それ以外はfalseを返す。
features()メソッドは以下の手順を実行しなければなりません:
-
resultを空の順序付き集合にする。
-
各対応機能featureについて:
-
featureをresultに追加する。
-
-
resultを返す。
allowedFeatures()メソッドは以下の手順を実行しなければなりません:
-
resultを空の順序付き集合にする。
-
originをこの
PermissionsPolicyオブジェクトのデフォルトオリジンとする。 -
policyをこの
PermissionsPolicyオブジェクトの関連ノードに対する可観測ポリシーとする。 -
各対応機能featureについて:
-
featureがpolicyでoriginに許可されていれば、featureをresultに追加する。
-
-
resultを返す。
getAllowlistForFeature(feature)メソッドは以下の手順を実行しなければなりません:
-
resultを空リストにする。
-
originをこの
PermissionsPolicyオブジェクトのデフォルトオリジンとする。 -
policyをこの
PermissionsPolicyオブジェクトの関連ノードに対する可観測ポリシーとする。 -
featureがpolicyでoriginに許可されていなければresultを返す。
-
allowlistが特別な値
*の場合:-
"
*"をresultに追加する -
resultを返す
-
-
allowlistのself-originがnullでない場合、そのシリアル化をresultに追加する。
-
allowlistのsrc-originがnullでない場合、そのシリアル化をresultに追加する。
-
それ以外の場合、各permissions-source-expressionitemについて、allowlistのexpressions内で:
-
itemをresultに追加する
-
-
resultを返す。
任意のNodeの可観測ポリシーは、permissions policyであり、そのNodeが表すナビゲーション可能な部分について、現在のドキュメントから見えるポリシー情報を含みます。
Document documentの可観測ポリシーを取得するには、documentのpermissions policyを返します。
Element nodeの可観測ポリシーを取得するには、次の手順を実行します:
-
inherited policyを空の順序付きマップにする。
-
各対応機能featureについて:
-
isInheritedを、コンテナで機能の継承ポリシーを定義をfeature、node、nodeの宣言オリジンで実行した結果とする。
-
inherited policy[feature]にisInheritedを設定する。
-
-
新しいpermissions policyを返す。継承ポリシーはinherited policy、宣言ポリシーは両方とも新しい構造体(宣言とレポート設定が空の順序付きマップ)とする。
Element nodeの宣言オリジンを取得するには、次の手順を実行します:
-
nodeのnode documentのサンドボックス化オリジンブラウジングコンテキストフラグが設定されている場合、新しい不透明オリジンを返す。
-
nodeの
sandbox属性が設定されており、allow-same-originキーワードが含まれていない場合、新しい不透明オリジンを返す。 -
nodeの
srcdoc属性が設定されている場合、nodeのnode documentのオリジンを返す。 -
nodeの
src属性が設定されている場合:-
urlを、nodeのsrc属性をnodeのnode documentからの相対URLとしてパースした結果とする。
-
urlが失敗でなければ、urlのオリジンを返す。
-
-
nodeのnode documentのオリジンを返す。
宣言オリジンの概念は、埋め込みページがフレームに読み込もうとしているドキュメントのオリジンを表すことを意図しています。つまり、ブラウザが
sandboxやsrcdoc属性をサポートしていない場合、それらの属性は宣言オリジンの計算に考慮すべきではありません。
8. レポート
Permissions policy 違反レポートは、Documentの何らかの動作がpermissions policyに違反したことを示します。どのような場合に違反と見なすか、またその違反がいつ発生したかの判断方法は、各ポリシー制御機能の仕様で定義されます。
Permissions Policy 違反レポートは、report typeとして "permissions-policy-violation" を持ちます。
Permissions Policy 違反レポートは、ReportingObserver から参照可能です。
[Exposed =Window ]interface :PermissionsPolicyViolationReportBody ReportBody { [Default ]object ();toJSON readonly attribute DOMString ;featureId readonly attribute DOMString ?;sourceFile readonly attribute long ?;lineNumber readonly attribute long ?;columnNumber readonly attribute DOMString ;disposition readonly attribute DOMString ?;allowAttribute readonly attribute DOMString ?; };srcAttribute
Permissions Policy 違反レポートのbodyは、JavaScriptではPermissionsPolicyViolationReportBodyで表現され、以下のフィールドを持ちます:
-
featureId: ポリシー制御機能の識別文字列。違反した機能を示し、関連するレポートのグルーピングや集計に利用できます。
-
sourceFile: 違反が発生したファイル(わかる場合のみ)。わからない場合はnull。
-
lineNumber: 違反が発生したsourceFileの行番号(わかる場合のみ)。わからなければnull。
-
columnNumber: 違反が発生したsourceFileの列番号(わかる場合のみ)。わからなければnull。
-
disposition: このケースで違反したpermissions policyが実際に適用されたかどうかを示す文字列。適用された場合は "enforce"、違反がレポートのみ(他の処理はされない)場合は "report"。
-
allowAttribute: 潜在的違反が特定の
iframe要素に帰属できる場合、その要素のallow属性の値(それ以外の場合は省略)。 -
srcAttribute: 潜在的違反が特定の
iframe要素に帰属できる場合、その要素のsrc属性の値(それ以外の場合は省略)。
8.1. `Permissions-Policy-Report-Only`
HTTPヘッダーフィールド
`Permissions-Policy-Report-Only`
HTTPヘッダーフィールドは、response(サーバーからクライアント)で利用でき、クライアント側で実際にポリシーを強制せず、もしポリシーが有効だった場合に違反となる挙動があった時のみレポート送信を行います。
`Permissions-Policy-Report-Only`は構造化ヘッダーであり、値はディクショナリでなければなりません。
ディクショナリの意味論は§ 5.2 構造化ヘッダーのシリアル化で定義されています。
処理手順は§ 9.2 辞書とオリジンからポリシーを構築で定義されています。
9. アルゴリズム
9.1. レスポンスポリシーの処理
response(response)、origin(origin)、boolean(report-only)を受け取り、このアルゴリズムは宣言ポリシーを返します。
-
report-onlyがTrueなら、header nameを "
Permissions-Policy-Report-Only"、そうでなければ "Permissions-Policy" にする。 -
parsed headerを、responseのheader listからheader nameと "dictionary" を指定してget a structured field valueを実行した結果とする。
-
parsed headerがnullなら、空の順序付きマップを返す。
-
policyを、parsed headerとoriginで辞書とオリジンからポリシーを構築を実行した結果とする。
-
policyを返す。
9.2. 辞書とオリジンからポリシーを構築
順序付きマップ(dictionary)とorigin(origin)を受け取り、このアルゴリズムは宣言ポリシーを返します。
-
declarationsを空の順序付きマップにする。
-
reporting-configを空の順序付きマップにする。
-
各 feature-name → (value, params) をdictionaryで:
-
featureをfeature-nameで識別されるポリシー制御機能とする。
-
params["report-to"]が存在し、文字列なら、reporting-config[feature]にparams["report-to"]をセットする。
-
allowlistを新しいallowlistにする。
-
valueがトークン
*、またはリスト内にトークン*が含まれる場合、allowlistを特別な値*とする。 -
それ以外の場合:
-
valueがトークン
selfなら、allowlistのself-originをoriginにする。 -
それ以外でvalueがリストなら、各elementをvalueで:
-
elementがトークン
selfなら、allowlistのself-originをoriginにする。 -
elementが有効なpermissions-source-expressionなら、appendでallowlistのexpressionsにelementを追加。
-
-
-
declarations[feature]にallowlistをセット。
-
«declarations, reporting-config»を返す。
9.3. ポリシー指令の解析
文字列(value)、origin(container origin)、オプションのorigin(target origin)を受け取り、このアルゴリズムはポリシー指令を返します。
-
directiveを空の順序付きマップにする。
-
strictly splitting value on the delimiter U+003B (;)で返された各serialized-declarationについて:
-
tokensを、splitting serialized-declaration on ASCII whitespaceの結果とする。
-
tokensが空リストなら、continue。
-
feature-nameをtokensの最初の要素とする。
-
featureをfeature-nameで識別されるポリシー制御機能とする。
-
targetlistをtokensの残りの要素(存在する場合)とする。
-
allowlistを新しいallowlistにする。
-
targetlistに文字列"
*"が含まれる場合、allowlistを特別な値*とする。 -
それ以外の場合:
-
targetlistが空でtarget originが与えられていれば、allowlistのsrc-originをtarget originにする。
-
targetlistの各elementについて:
-
elementが
'self'(ASCII大文字小文字区別なし)で一致する場合:-
allowlistのself-originをcontainer originにする。
-
次のelementにcontinue。
-
-
target originが与えられ、elementが
'src'(ASCII大文字小文字区別なし)で一致する場合:-
allowlistのsrc-originをtarget originにする。
-
次のelementにcontinue。
-
-
resultを、URL parserをelementに適用した結果とする。
-
resultが失敗でなければ:
-
-
-
directive[feature]にallowlistをセット。
-
-
directiveを返す。
9.4. パーミッションポリシー属性の処理
9.5. ナビゲーション可能対象のPermissions Policy作成
-
アサート:nullでない場合、containerはナビゲーション可能コンテナである。
-
inherited policyを新しい順序付きマップにする。
-
-
isInheritedをコンテナで機能の継承ポリシーを定義をfeature、 container、originで実行した結果とする。
-
inherited policy[feature]にisInheritedをセット。
-
-
policyを新しいpermissions policy(継承ポリシーはinherited policy、 宣言ポリシーは «[], []»)とする。
-
policyを返す。
9.6. レスポンスからナビゲーション可能対象のPermissions Policy作成
-
policyをナビゲーション可能対象のPermissions Policy作成をcontainer、originで実行した結果とする。
-
dをレスポンスポリシーの処理をresponse、origin、report-onlyで実行した結果とする。
-
各feature → allowlistについて、dの宣言で:
-
policyを返す。
9.7. コンテナで機能の継承ポリシーを定義
Documentのオリジン(origin)、オプションのboolean(report-only、デフォルトFalse)を受け取り、このアルゴリズムはfeatureの継承ポリシー値を返します。
-
containerがnullなら、"
Enabled"を返す。 -
オリジンの機能値取得をfeature、containerのノードドキュメント、containerのノードドキュメントのオリジン、report-onlyで実行した結果が"
Disabled"なら、"Disabled"を返す。 -
オリジンの機能値取得をfeature、containerのノードドキュメント、origin、report-onlyで実行した結果が"
Disabled"なら、"Disabled"を返す。 -
container policyをパーミッションポリシー属性の処理をcontainerで実行した結果とする。
-
もしfeatureがcontainer policyに含まれる場合:
-
featureのデフォルト許可リストが
*なら、"Enabled"を返す。 -
featureのデフォルト許可リストが
'self'かつ、originがcontainerのノードドキュメントのオリジンと同一オリジンなら、"Enabled"を返す。 -
それ以外は"
Disabled"を返す。
9.8. オリジンの機能値取得
Documentオブジェクト(document)、オリジン(origin)、boolean(report-only)を受け取り、このアルゴリズムはfeatureが無効と見なす場合は"Disabled"、そうでなければ"Enabled"を返します。
-
policyをreport-onlyがTrueならdocumentのreport-only permissions policy、そうでなければdocumentのpermissions policyとする。
-
もしpolicyのfeatureの継承ポリシーが"
Disabled"なら、"Disabled"を返す。 -
もしfeatureがpolicyの宣言ポリシーに存在する場合:
-
"
Enabled"を返す。
9.9. Permissions Policyチェック
Disabled"、そうでなければ"Enabled"を返します。
-
もしpolicyのfeatureの継承ポリシーが"
Disabled"なら、"Disabled"を返す。 -
もしfeatureがpolicyの宣言ポリシーに存在する場合:
-
featureのデフォルト許可リストが
*なら、"Enabled"を返す。 -
featureのデフォルト許可リストが
'self'で、originがdocument originと同一オリジンなら、"Enabled"を返す。 -
"
Disabled"を返す。
9.10. ドキュメントで機能がオリジンに対して有効か?
Documentオブジェクト(document)、オリジン(origin)、オプションのboolean(report、デフォルトTrue)を受け取り、このアルゴリズムはfeatureが無効と見なす場合は"Disabled"、そうでなければ"Enabled"を返します。reportがTrueの場合、機能がdocumentのpermissions policyまたはreport-only permissions
policyのいずれにも有効でない場合、違反レポートも生成・キューします。
注: reportのデフォルト値Trueは、ほとんどのパーミッションポリシーチェックで、機能が有効でない場合に違反レポートが生成されることを意味します。これは、ほとんどのチェックが実際の機能利用試行のためであるため、期待される結果です。機能の状態をただ問い合わせるだけで実際の機能利用を表さない場合は、reportをFalseにするべきです。
-
policyをdocumentのpermissions policyとする。
-
report-only policyをdocumentのreport-only permissions policyとする。
-
resultをPermissions Policyチェックをpolicy、feature、origin、documentのオリジンで実行した結果とする。
-
report-only resultをPermissions Policyチェックをreport-only policy、feature、origin、documentのオリジンで実行した結果とする。
-
もしreportがTrueなら:
-
settingsをdocumentの環境設定オブジェクトとする。
-
もしresultが"
Disabled"なら:-
endpointを機能のレポートエンドポイント取得をfeature、policyで実行した結果とする。
-
設定のPermissions Policy違反レポート生成をfeature、settings、"
Enforce"、endpointで呼び出す。
-
-
それ以外でreport-only resultが"
Disabled"なら:-
report-only endpointを機能のレポートエンドポイント取得をfeature、report-only policyで実行した結果とする。
-
設定のPermissions Policy違反レポート生成をfeature、settings、"
Report"、report-only endpointで呼び出す。
-
-
-
resultを返す。
9.11. 機能のレポートエンドポイント取得
9.12. コンテナでPermissions Policyの潜在的違反チェック
-
documentをcontainerのノードドキュメントとする。
-
settingsをdocumentの環境設定オブジェクトとする。
-
-
コンテナで機能の継承ポリシーを定義をfeature、container、containerの宣言オリジンで実行した結果が"
Disabled"なら:-
endpointを機能のレポートエンドポイント取得をfeature、documentのpermissions policyで実行した結果とする。
-
設定のPermissions Policy潜在的違反レポート生成をfeature、settings、"
Enforce"、endpoint、allowAttribute、srcAttributeで呼び出す。
-
-
それ以外でコンテナで機能の継承ポリシーを定義をfeature、container、containerの宣言オリジン、Trueで実行した結果が"
Disabled"なら:-
report-only endpointを機能のレポートエンドポイント取得をfeature、documentのreport-only permissions policyで実行した結果とする。
-
設定のPermissions Policy潜在的違反レポート生成をfeature、settings、"
Report"、report-only endpoint、allowAttribute、srcAttributeで呼び出す。
-
-
9.13. 設定のPermissions Policy違反レポート生成
-
bodyを新しい
PermissionsPolicyViolationReportBodyとして、以下の値で初期化:- featureId
-
featureの文字列表現
- sourceFile
-
null
- lineNumber
-
null
- columnNumber
-
null
- disposition
-
disposition
-
ユーザーエージェントが現在スクリプトを実行中で、settingsからソースファイルURL、行番号、列番号を抽出できる場合、それらをbodyのsourceFile、lineNumber、columnNumberに設定する。
-
generate and queue a reportをbody、"permissions-policy-violation"、endpoint、settingsで実行する。
9.14. 設定のPermissions Policy潜在的違反レポート生成
-
bodyを新しい
PermissionsPolicyViolationReportBodyとして、以下の値で初期化:- featureId
-
featureの文字列表現
- sourceFile
-
null
- lineNumber
-
null
- columnNumber
-
null
- disposition
-
disposition
- allowAttribute
-
allowAttribute
- srcAttribute
-
srcAttribute
-
ユーザーエージェントが現在スクリプトを実行中で、settingsからソースファイルURL、行番号、列番号を抽出できる場合、それらをbodyのsourceFile、lineNumber、columnNumberに設定する。
-
generate and queue a reportをbody、"potential-permissions-policy-violation"、endpoint、settingsで実行する。
9.15. リクエストで機能利用を許可すべきか?
true、そうでなければfalseを返します。
-
clientをrequestのclientとする。
-
clientがnullなら、
falseを返す。 -
clientのglobal objectが
Windowでなければ、falseを返す。Window以外のコンテキスト(WorkerGlobalScopeやWorkletGlobalScope)でのPermissions Policyはissue #207で議論中。解決後、このアルゴリズムを更新し、こうしたコンテキストでのフェッチに機能制御ポリシーが使えるようにする。解決まではこれらのコンテキスト内ではすべてのポリシー制御機能(例:Client Hintsなど)を不許可とする。 -
documentをclientのglobal objectの関連Documentとする。
-
originをrequestのoriginとする。
-
resultをドキュメントで機能がオリジンに対して有効か?をfeature、document、originで実行した結果とする。
-
resultが"
Enabled"ならtrueを返す。 -
それ以外は
falseを返す。
10. 他仕様への変更点
10.1. HTML仕様への変更点
すべてのDocumentは、report-only permissions policy(報告のみのpermissions
policy)を持ち、これは初期状態で空のpermissions policyです。
7.5.1 共通ドキュメント生成インフラの手順3の後に、以下の手順を挿入:
-
reportOnlyPermissionsPolicyを、navigationParamsのnavigableのcontainer、navigationParamsのorigin、navigationParamsのresponse、Trueを引数にレスポンスからナビゲーション可能対象のPermissions Policy作成を呼び出した結果とする。
同セクションの手順10で、新しいDocumentのreport-only permissions
policyをreportOnlyPermissionsPolicyに設定する。
iframe load event stepsの手順6の後に、以下の手順を挿入:
-
コンテナでPermissions Policyの潜在的違反チェックをelement、elementのallow属性、elementのsrc属性で呼び出す。
11. IANA考慮事項
恒久的なメッセージヘッダーフィールドレジストリは、以下の登録内容で更新されるべきです [RFC3864]:
- ヘッダーフィールド名
- Permissions-Policy
- 適用プロトコル
- http
- ステータス
- standard
- 著者/変更管理者
- W3C
- 仕様書
- https://www.w3.org/TR/permissions-policy/
12. プライバシーとセキュリティ
この仕様は、埋め込みページに対してポリシーを設定し、それを強制する仕組みを標準化します。iframeのsandboxのように、埋め込まれるページの明示的な許可なしに行うことができ、公開済みウェブサイト上の既存機能の挙動を、適切なコンテナポリシーを持つ別のドキュメントに埋め込むことで変更することが可能です。
このため、最大のプライバシーとセキュリティ上の懸念は次の通りです:
- クロスオリジンのサブフレームの挙動が埋め込み元に露出すること
- 埋め込み元によって制御されるサブフレームで予期しない挙動変化が起こること
ある程度、これらの懸念は既にウェブプラットフォーム上に存在しており、本仕様は少なくとも不要にそれらを悪化させないようにしています。
セキュリティやプライバシーの課題は、個々の機能の設計によっても生じ得るため、本仕様と統合する際には注意が必要です。このセクションでは、どのような挙動が問題を引き起こす可能性があるかについて指針を示します。
12.1. クロスオリジン挙動の露出
機能は、フレーム内ドキュメントでポリシー違反(violation)が発生しても、他のフレームのドキュメントから観察できないように設計すべきです。例えば、仮想の機能で、ポリシーで無効化された際に埋め込みドキュメントでイベントが発生する場合、埋め込み元がそのイベントを監視することで、埋め込まれたドキュメントの状態情報を取得できてしまいます。例えば、その機能がログイン中にのみ利用されると分かっている場合、埋め込み元はその機能をフレームで無効化し、イベントを監視することでユーザーのログイン状態を推測できます。
内省APIは、サブフレームのポリシー情報を埋め込みドキュメントが既に推測できる範囲のみに限定して表示されるよう設計されています。この可観測ポリシーは、フレームドキュメントで配信されたHTTPヘッダーによって影響されず、フレームが自身でナビゲートしても(異なるオリジンでも異なるポリシーでも)変化しません。src属性の設定によるナビゲーションのみ、可観測ポリシーが更新されます。
12.2. 予期しない挙動の変化
Permissions Policyの仕組みにより、ドキュメントはサブフレームでどの機能を利用可能・不可にするかを制御できます。機能がウェブプラットフォームの既存・長期的な挙動を表している場合、公開済みウェブコンテンツが特定APIが失敗し得ることを想定していない場合があります。
実用的(少し極端ですが)な例として、同期XMLHttpRequestを使ってユーザーが十分な権限を持っているか判定するドキュメントを考えます:
<!DOCTYPE html>
<h1>Welcome to SecureCorp!</h1>
<script>
var req = new XMLHttpRequest();
req.open("GET", "/api/security_check.json", false);
req.send();
if (req.response == "untrusted user") {
// User is not logged in; redirect to a safe page
location.href = "/security_check_failed.html";
}
</script>
<!-- Page continues with assumption that user is logged in -->
このドキュメントが、"sync-xhr"機能を無効化したページに埋め込まれると、XMLHttpRequest.open()の呼び出しは失敗し、セキュリティチェックが回避されてしまいます。
このような挙動の強制は既にウェブ上で可能です:一部の機能はトップレベルドキュメントでのみ許可され、iframeでは許可されず、iframeのサンドボックス機能を使って同様に依存している機能へのアクセスを防ぐこともできます。
一般的に、この懸念は以下の2つの方法で軽減されます:
- 脆弱なページは
X-Frame-OptionsHTTPヘッダーを付与し、攻撃者によるフレーミングを防ぐことができます。 - サイトはAPIや挙動を利用する前に機能検出を行い、API呼び出しによって返されるエラーや例外を扱えるようにするべきです。
- 機能検出が不可能な場合、新しいウェブコンテンツは
policyオブジェクトを使って現在強制されているpermissions policyを調査し、挙動やUIを調整できます。
Permissions Policyに機能を統合する著者は、ドキュメントが無効状態で機能を利用しようとした際の失敗タイミングや方法を決めることができます。既存の失敗モードがあればそれを活用することで、既存コンテンツが既にその失敗を適切に処理している可能性を高めることができます。
12.3. 埋め込みポリシーの露出
埋め込みページがクロスオリジンページの挙動について推測できる情報は制限されています。しかし一部のケースでは、埋め込み先ページが自身に強制されたポリシーを調査することで、埋め込み元に関する情報を推測できる場合があります。
これは既存のdocument.fullscreenEnabledプロパティにも類似していて、埋め込まれたドキュメントは埋め込み元がFullscreen
APIの利用権限を与えているかを推測できます。これが特定条件(例えば、埋め込み元でユーザーがログインしている場合のみ許可)で付与されている場合、埋め込み先サイトは埋め込み元の状態情報を取得することができます。