Permissions Policyによる機能ゲートの委譲モデル
カメラや位置情報などの強力機能を、オリジン単位で許可し iframe へ委譲する仕組みを原理から理解。allowlist の継承規則と allow 属性の連携、レポーティングまで押さえ、埋め込みコンテンツの権限事故を防げるようになります。
- 1.Permissions Policy は Permissions-Policy ヘッダで機能ごとに「どのオリジンが使ってよいか」を allowlist として宣言する。既定値は self が多く、未許可オリジンでは API 呼び出しが拒否や no-op になる。
- 2.親が許可した機能だけを、iframe の allow 属性で子フレームへ委譲できる。委譲は親の allowlist との積(intersection)で決まり、親が持たない権限を子に与えることはできない。
- 3.違反は Reporting API(Report-To/permissions-policy-violation)で収集でき、本番前に影響範囲を把握できる。Feature-Policy ヘッダは旧名で、構文も値も異なる後方互換の別物。
Permissions Policy(旧 Feature Policy)は「このドキュメントとその埋め込みフレームが、どの強力機能を使ってよいか」をオリジン単位で宣言・強制する仕組みです。ユーザー許可ダイアログ(Permissions API)が「使ってよいか人に尋ねる」のに対し、こちらは「そもそも使う資格があるか」を開発者とブラウザが事前に区切ります。両者は直交し、Permissions Policy で塞がれた機能はダイアログすら出ません。ここでは allowlist の評価と委譲の代数を原理から見ていきます。
機能とポリシーの単位
Permissions Policy が制御する対象は 機能(policy-controlled feature) と呼ばれる名前付きの能力です。camera、microphone、geolocation、fullscreen、payment、autoplay、display-capture などが代表で、各機能ごとに独立した allowlist を持ちます。allowlist は「その機能を有効にしてよいオリジンの集合」です。
Permissions-Policy: geolocation=(self "https://maps.example.com"), camera=(), fullscreen=*
allowlist には4つの基本表現があります。
| 表現 | 意味 | 典型用途 |
|---|---|---|
| * | すべてのオリジン(埋め込み先含む)で有効 | fullscreen など害の少ない機能 |
| self | ドキュメント自身のオリジンのみ | 多くの機能の既定値 |
| "https://a.example" | 列挙したオリジンを明示許可 | 特定の信頼パートナーへ委譲 |
| () (空リスト) | どのオリジンでも無効 | 機能を完全に封じる |
ここで重要なのは、機能ごとに 既定 allowlist が定義されている点です。既定値は仕様上 *・self・none のいずれかで、機能ごとに決まっています。camera や geolocation、autoplay など多くの強力機能は既定が self(自オリジンのみ、クロスオリジン埋め込みでは無効)です。Permissions-Policy ヘッダを一切送らなくても、各機能はこの既定値で動いています。ヘッダはその既定を上書きする手段にすぎません。
ポリシーで機能が無効なオリジンから API を呼ぶと、geolocation や camera は Promise が NotAllowedError で reject されます。一方 autoplay のような機能は例外を投げず、単に再生が始まらない no-op になります。「拒否=必ず throw」ではないため、機能ごとの仕様を確認する必要があります。
委譲モデル — 親から子への権限の流れ
Permissions Policy の核心は、メインフレームが持つ機能を iframe へ委譲(delegation) できることです。委譲は2つの設定の積で決まります。
- 親ドキュメントの
Permissions-Policyヘッダの allowlist(その機能が親で有効か、どのオリジンに開かれているか) - その iframe を作る
<iframe allow="...">属性
<!-- 親が geolocation を持つ前提で、この子フレームに委譲 -->
<iframe src="https://maps.example.com/embed"
allow="geolocation 'src'; camera 'none'"></iframe>
allow 属性の各エントリは「機能名+許可するオリジン」の並びです。'src' は iframe の src オリジン、'self' は親オリジン、'none' は無効、明示 URL も書けます。allow="geolocation" のように省略すると 'src' を許可した扱いになります。
決定的なのは、子フレームの実効 allowlist は「親の allowlist」と「allow 属性」の積集合(intersection) だという点です。親が geolocation=(self) で自分にしか許していなければ、いくら子の allow="geolocation *" と書いても、子フレームは geolocation を使えません。委譲は「親が持っている権限の一部を渡す」操作であり、無から権限を生み出すことはできません。
クロスオリジン iframe は、allow 属性で明示的に渡さない限り、camera や geolocation などの委譲可能機能を 既定で無効 として開始します。親が * で全許可していても、子の allow に書かなければ子では使えません。逆に言えば、埋め込み広告や外部ウィジェットに余計な権限が漏れないよう、ブラウザ側が安全側に倒しています。
継承とサンドボックスの相互作用
委譲は階層的です。フレームが入れ子になると、各段で intersection が累積されます。孫フレームの実効許可は「ルート → 親 → 子 → 孫」の各 allowlist をすべて掛け合わせた結果で、途中のどこか一段でも閉じれば、その下流すべてで無効になります。一度狭めた権限を下層で広げ直すことは原理的にできません。
<iframe sandbox> との関係も整理が要ります。sandbox 属性は origin・スクリプト・フォーム送信などをまとめて剥奪する別系統の隔離機構ですが、Permissions Policy の委譲とは独立に評価されます。サンドボックス化された iframe はオリジンが不透明(opaque)になりやすく、その場合 'src' 指定の委譲先が一致せず機能が通らないことがあります。隔離の設計思想は 同一オリジンポリシーとサイト分離の信頼境界 の信頼境界と地続きで、両者を重ねて考えると挙動の食い違いに迷いません。
ヘッダ・allow・既定値の優先関係
実際にある機能が「このフレームで使えるか」は、複数の入力が合流して決まります。評価の流れは次のように整理できます。
| 段階 | 効果 | 備考 |
|---|---|---|
| 機能の既定 allowlist | ヘッダ無指定時のベースライン | self か * が大半 |
| 親の Permissions-Policy ヘッダ | 親ドキュメントでの有効範囲を確定 | 既定を上書き |
| iframe の allow 属性 | 親の範囲内で子へ委譲 | 親 allowlist との intersection |
| ヘッダの report-to 連携 | 違反時にレポート送信 | ブロックの可否は変えない |
Permissions-Policy ヘッダの値は、HTTP の Structured Field(構造化フィールド)構文に従います。値の文字列オリジンは二重引用符で囲み、self や * は引用符なしのトークンです。ここを混同すると静かにパース失敗して機能が既定値のまま、という事故が起きやすいので注意します。
Permissions Policy は基本的に Permissions-Policy レスポンスヘッダで配信します。CSP のような <meta http-equiv> での指定は、Permissions Policy では限定的にしかサポートされません。委譲やレポーティングを含む本格運用ではレスポンスヘッダで一元管理するのが定石です。配信制御の発想は Content Security Policyの内部動作と回避耐性 と同じく「サーバーが宣言しブラウザが強制する」モデルです。
レポーティング — 違反を観測する
ポリシーをいきなり厳格化すると、正規の機能まで止まってページが壊れます。これを避けるため、Permissions Policy は Reporting API と連携し、違反を観測できます。機能がポリシーで拒否されると、permissions-policy-violation タイプのレポートが生成され、Reporting-Endpoints(または旧 Report-To)で指定したエンドポイントへ送られます。
Reporting-Endpoints: pp-endpoint="https://report.example.com/pp"
Permissions-Policy: camera=(self), geolocation=(self)
ページ内では ReportingObserver で同じ違反を JavaScript から拾えます。
const observer = new ReportingObserver((reports) => {
for (const r of reports) {
if (r.type === "permissions-policy-violation") {
// r.body.featureId に拒否された機能名が入る
console.warn("blocked:", r.body.featureId);
}
}
}, { types: ["permissions-policy-violation"] });
observer.observe();
これにより、どの埋め込みフレームがどの機能を要求して弾かれたかを本番投入前に可視化できます。CSP の Report-Only 段階と同じく、まず観測して影響範囲を測り、それから allowlist を締めるのが安全な導入手順です。なお収集データの計測手法は Performance Measurement API の内部設計 と同じ Reporting/Observer 系の発想で動きます。
旧 Feature-Policy との違い
歴史的経緯として、この仕組みは当初 Feature Policy と呼ばれ、Feature-Policy ヘッダと異なる構文を持っていました。
| 項目 | Feature-Policy(旧) | Permissions-Policy(現) |
|---|---|---|
| 区切り | セミコロンでディレクティブ区切り | カンマで機能区切り |
| オリジン表記 | 引用符なしの URL を空白区切り | 二重引用符付き+Structured Fields |
| 全許可/全禁止 | * / 'none' | * / () (空リスト) |
| 位置づけ | 非推奨・後方互換 | 標準(推奨) |
両ヘッダが同時に送られた場合、対応ブラウザは新しい Permissions-Policy を優先します。古い記事の Feature-Policy: geolocation 'self' をそのまま Permissions-Policy に貼ると構文エラーになるため、移行時は機械的なコピーではなく構文変換が必要です。
Permissions Policy で機能が無効だと、ユーザー許可ダイアログすら表示されず、開発者から見れば API が静かに失敗します。地図ウィジェットの位置情報が「ユーザーが拒否したのか、親フレームが委譲し忘れたのか」を切り分けられず、原因究明が長引きがちです。だからこそ、委譲を設計する側はレポーティングを最初から仕込み、allow 属性と親ヘッダの intersection を意識的に管理することが、埋め込みコンテンツの権限事故を防ぐ最短路になります。
Web/フロントエンド Article
Permissions Policyによる機能ゲートの委譲モデルを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
セキュリティ
比較で見る軸
難易度: advanced / カテゴリ: Web/フロントエンド / タグ数: 5
導入後に効く点
親が許可した機能だけを、iframe の allow 属性で子フレームへ委譲できる。委譲は親の allowlist との積(intersection)で決まり、親が持たない権限を子に与えることはできない。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- Web/フロントエンド
- タグ数
- 5
判断チェックリスト
- 自社の用途が「セキュリティ / Web」に近いか確認する。
- 強みである「Permissions Policy は Permissions-Policy ヘッダで機能ごとに「どのオリジンが使ってよいか」を allowlist として宣言する。既定値は self が多く、未許可オリジンでは API 呼び出しが拒否や no-op になる。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。