iframeサンドボックスと権限分離の境界
埋め込んだ第三者コンテンツを安全に閉じ込められる。sandbox属性が無効化する能力、allow-same-originが境界を壊す理由、Permissions Policyとの併用で隔離を設計する勘所を原理から押さえます。
- 1.sandbox属性を付けた瞬間、iframeはスクリプト実行・フォーム送信・ポップアップ・トップレベル遷移・同一オリジン扱いを一括で失う。allow-トークンで能力を1つずつ戻していく減算モデル。
- 2.allow-same-origin と allow-scripts を同時に許すと、内側のスクリプトが自分のsandbox属性を消して再読み込みでき、サンドボックスを自力で破れる。同一オリジン埋め込みでは特に危険。
- 3.sandbox は能力(API面)の無効化、Permissions Policy はカメラ等の強力機能の許可委譲、CSPのframe系は誰を埋め込む/誰に埋め込まれるかを制御。3層を重ねて初めて隔離が成立する。
iframe は別ドキュメントを現在のページに埋め込む仕組みですが、埋め込む相手が広告・ウィジェット・ユーザー投稿の HTML など信頼できないコードであることは珍しくありません。sandbox 属性は、その埋め込みフレームから危険な能力を奪い、限定的な実行環境に閉じ込めるための機能です。ここでは各トークンが何を無効化するのか、なぜ allow-same-origin が境界を壊すのか、そして Permissions Policy や CSP との役割分担を原理から見ていきます。
sandbox は「全部禁止」から始まる減算モデル
最大のポイントは、sandbox 属性を付けた時点でフレームの能力がほぼすべて無効化されることです。何も値を書かない <iframe sandbox src="..."> は最も制限が強く、そこから sandbox="allow-scripts allow-forms" のように allow- トークンを並べて、必要な能力を1つずつ戻していきます。CSP のような「許可リストを足していく」発想に似た、加算ではなく減算のモデルです。
<!-- 最も厳しい: スクリプトもフォームも遷移もすべて不可 -->
<iframe sandbox src="https://untrusted.example/widget"></iframe>
<!-- スクリプトとフォーム送信だけ戻す -->
<iframe sandbox="allow-scripts allow-forms"
src="https://untrusted.example/widget"></iframe>
空の sandbox を付けたフレームは、内部的に**不透明オリジン(opaque origin)**を割り当てられます。これは「どのオリジンとも一致しない、生成のたびに異なる一意のオリジン」で、自分自身とも同一オリジンになりません。結果として、そのフレーム内のスクリプトは自身の localStorage/sessionStorage、document.cookie、IndexedDB などオリジン紐付けのストレージに一切アクセスできなくなります。オリジンが何を意味するかは 同一オリジンポリシーとサイト分離の信頼境界 の通りで、sandbox はその境界を「最も孤立した側」に倒す操作だと捉えると理解しやすいです。
各トークンが無効化/復帰させる能力
主要なトークンと、それが制御する能力を整理します。
| トークン | 戻る能力 | 省略時に起きること |
|---|---|---|
| allow-scripts | JavaScript の実行 | script が一切動かない(自動再生・計測も止まる) |
| allow-forms | フォームの送信 | submit がブロックされる |
| allow-same-origin | 本来のオリジンとして扱う | 不透明オリジン化しストレージ/Cookie 不可 |
| allow-popups | window.open 等での新規ウィンドウ | ポップアップが開けない |
| allow-top-navigation | トップレベル(親)の遷移 | フレームが親ページを別URLへ飛ばせない |
| allow-modals | alert/confirm/print 等のモーダル | これらの呼び出しが無視される |
| allow-downloads | ファイルのダウンロード開始 | ダウンロードが抑止される |
特に注意したいのが トップレベル遷移 です。allow-top-navigation を与えないと、サンドボックス内のスクリプトは top.location = '...' や target="_top" のリンクで親ページごと別サイトへ飛ばすことができません。広告フレームがユーザーを勝手にフィッシングサイトへリダイレクトする典型的な攻撃は、これで遮断されます。より安全な中間として allow-top-navigation-by-user-activation があり、これは実際のクリックなどユーザー操作を伴うときだけトップ遷移を許します。自動リダイレクトは止めつつ、正規のリンククリックは通したい広告枠などで使われます。
allow-popups で開いた新しいウィンドウは、既定では親フレームと同じ sandbox 制約を継承します。allow-popups-to-escape-sandbox を併記すると、その継承を断ち切り、ポップアップ先を制約なしのウィンドウとして開けます。逆に言えば、これを安易に付けると「サンドボックスの外」へ抜ける口を自ら開けることになるため、信頼できない埋め込みでは避けます。
allow-same-origin が境界を壊す理由
allow-same-origin は他のトークンと性格が違います。これは能力を1つ足すというより、フレームを不透明オリジンから本来のオリジンへ戻すスイッチです。これを付けると、フレームは本来のオリジンとして扱われ、同一オリジンであれば Cookie・ストレージ・postMessage を超えた DOM 直アクセスまで可能になります。
危険なのは allow-same-origin と allow-scripts を同時に許す場合です。両方が揃うと、フレーム内のスクリプトは自分が埋め込まれている <iframe> 要素の DOM へ(同一オリジンなら)アクセスでき、自分自身の sandbox 属性を書き換えてから再読み込みを誘発できます。属性の評価は次回ロード時に効くため、sandbox を緩めた状態で読み直すことで、自力でサンドボックスを脱出できてしまいます。さらに同一オリジンを名乗れる以上、親ページのストレージや Cookie とオリジンを共有し、サンドボックスの「隔離」という前提自体が崩れます。
信頼できないコンテンツを自サイトと同一オリジンで sandbox="allow-scripts allow-same-origin" 埋め込みするのは、ほぼサンドボックスを無効化したのと同じです。脱出に加え、同一オリジンの DOM 直アクセスで親の機密も読まれ得ます。信頼境界が要るコンテンツは、専用の別オリジン(例: usercontent.example のような分離ドメイン)に置き、そのうえで sandbox を掛けるのが定石です。こうすれば allow-same-origin を付けても、共有されるのはその分離オリジン内に限られます。
sandbox・Permissions Policy・CSP の役割分担
iframe の隔離は1つの属性では完結せず、性質の異なる3つの仕組みを重ねて設計します。
| 仕組み | 制御する対象 | 向き |
|---|---|---|
| sandbox 属性 | スクリプト/フォーム/遷移などフレームの基本能力 | 親 → 埋め込むフレーム |
| allow 属性 (Permissions Policy) | カメラ/マイク/位置情報など強力機能の委譲 | 親 → 埋め込むフレーム |
| CSP frame-src | どの URL を iframe で読み込んでよいか | 親が自分の埋め込み先を制限 |
| CSP frame-ancestors | どのページが自分を iframe 化してよいか | 子が自分の埋め込まれ方を制限 |
sandbox が「フレーム内でできる操作」を能力レベルで削るのに対し、<iframe> の allow 属性が指定する Permissions Policy は、カメラ・マイク・位置情報・全画面・支払いといった強力機能の利用許可をフレームへ委譲するかどうかを制御します。両者は補完関係です。たとえばカメラを使うウィジェットなら sandbox="allow-scripts" で能力を絞りつつ、allow="camera" で必要な機能だけを明示的に渡します。allow で渡さない限り、たとえ allow-scripts でスクリプトが動いても getUserMedia() は権限拒否になります。
<iframe
sandbox="allow-scripts allow-forms"
allow="camera; microphone 'none'; geolocation 'none'"
src="https://untrusted.example/recorder">
</iframe>
CSP 側は埋め込み関係そのものを制御します。親が frame-src で「自分が読み込んでよい iframe の URL」を制限し(Content Security Policy の内部動作と回避耐性 で扱う default-src フォールバックも効きます)、逆に埋め込まれる側は frame-ancestors で「自分を iframe に入れてよい親」を制限してクリックジャッキングを防ぎます。frame-ancestors 'none' は旧来の X-Frame-Options: DENY の後継で、CSP として一元管理できる点が利点です。
サンドボックス化したフレームと親が連携する正規ルートは postMessage です。送信側は第2引数で受信オリジンを厳密に指定し、受信側は event.origin を必ず検証します。なお不透明オリジンや別オリジンのフレームは Web ストレージ(localStorage / sessionStorage) を親と共有しないため、状態はメッセージで受け渡すか、ユーザー操作を起点とする Storage Access API 経由に限定する設計が安全です。
設計の指針 — 最小権限で組み立てる
実務での組み立て方は明快です。まず値なしの sandbox から始め、そのコンテンツが本当に必要とする能力だけを allow- で戻します。スクリプトが要るなら allow-scripts、フォームが要るなら allow-forms、というように加算は最小限にとどめます。allow-same-origin は、信頼できないコンテンツに対しては原則付けない。どうしてもストレージ等が要るなら、必ず専用の分離オリジンに隔離したうえで付けます。
そのうえで Permissions Policy の allow で強力機能を絞り、CSP の frame-src/frame-ancestors で埋め込みの両方向を固めます。信頼できない HTML を直接描画する場面では、サンドボックス化フレームへの注入経路そのものも DOM-based XSS の sink/source 分類と緩和原理 の観点で塞いでおくべきです。sandbox 単体に頼らず、能力の無効化・機能委譲・埋め込み制御の3層を重ねることが、第三者コンテンツを安全に同居させる唯一の堅い設計です。
Web/フロントエンド Article
iframeサンドボックスと権限分離の境界を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
セキュリティ
比較で見る軸
難易度: advanced / カテゴリ: Web/フロントエンド / タグ数: 5
導入後に効く点
allow-same-origin と allow-scripts を同時に許すと、内側のスクリプトが自分のsandbox属性を消して再読み込みでき、サンドボックスを自力で破れる。同一オリジン埋め込みでは特に危険。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- Web/フロントエンド
- タグ数
- 5
判断チェックリスト
- 自社の用途が「セキュリティ / Web」に近いか確認する。
- 強みである「sandbox属性を付けた瞬間、iframeはスクリプト実行・フォーム送信・ポップアップ・トップレベル遷移・同一オリジン扱いを一括で失う。allow-トークンで能力を1つずつ戻していく減算モデル。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。