モバイルのアプリサンドボックスと権限モデル
個人情報漏洩や乗っ取りを防ぐ最終防衛線がここにある。iOSとAndroidのサンドボックス・権限モデルの内部動作を原理から押さえられる。
- 1.iOSはentitlementsで署名済みの権限を宣言し、サンドボックスプロファイルがコンテナ外へのファイルアクセスをカーネル層で強制的に遮断する。
- 2.Androidはインストール時(normal)と実行時(dangerous)でパーミッション付与のタイミングが異なり、最終的にSELinuxのMAC(強制アクセス制御)で二重に守られる。
- 3.Scoped Storageは共有ストレージへの生パス依存を断ち切り、MediaStore経由のアクセスに限定することでアプリ間の無制限な読み書きを防ぐ。
なぜアプリ同士を隔離しなければならないのか
スマートフォンには連絡先・位置情報・カメラ・決済情報など、機密度の高いデータが一台に集約されています。もし任意のアプリが他アプリのデータやOS本体に自由にアクセスできれば、1本の悪性アプリの侵入がデバイス全体の陥落に直結します。この前提に立ち、iOSとAndroidはいずれも「アプリは既定で何もできない」を出発点に設計されています。プロセスごとに隔離された実行環境(サンドボックス)を割り当て、そこからはみ出す操作をOSが個別に許可制へ戻す——この最小権限の原則をどう実装しているかを、両OSの内部動作から見ていきます。
iOS:コンテナとentitlementsによる二重の壁
iOSの各アプリは、インストール時に専用のコンテナディレクトリを割り当てられます。アプリの実行ファイル・Documents・Library・tmpはすべてこのコンテナ配下に閉じ込められ、他アプリのコンテナへは通常の手段でパスをたどっても到達できません。この隔離を強制しているのは単なるUNIXパーミッションではなく、カーネル拡張のSandbox(Seatbelt)が適用するサンドボックスプロファイルです。プロファイルはプロセスごとに「どのパス・どのMachサービス・どのソケットにアクセスできるか」をルールとして持ち、システムコール発行時にカーネルが照合して拒否します。アプリ側のバグや脆弱性でロジックが乗っ取られても、この強制アクセス制御はユーザー空間のコードを経由しないため回避が極めて困難です。
コンテナの壁だけでは「カメラを使う」「位置情報を取る」といった、コンテナの外側にあるリソースへのアクセスは説明できません。ここを担うのがentitlementsです。entitlementsはアプリバイナリの署名に埋め込まれた鍵と値のペアで、「このアプリはHealthKitを使ってよい」「プッシュ通知を受け取ってよい」といった権限を宣言します。重要なのは、entitlementsは実行時に書き換えられない点です。コード署名の一部として検証されるため、改ざんすればコード署名自体が無効になりアプリは起動できません。
entitlementsは「そもそもこの機能を要求してよいか」という開発者向けの静的な許可であり、Apple Developer Programの審査・プロビジョニングプロファイルと結び付いています。一方でカメラや位置情報のような利用者のプライバシーに関わる項目は、entitlementsで要求権を持っていても、実行時にユーザーへダイアログで同意を求める仕組み(TCC、Transparency Consent and Control)が別途介在します。entitlementsだけでは実データへは到達できず、TCCの同意データベースと両方を通過して初めてアクセスが成立します。
TCCはユーザーごと・アプリごとの同意状態をシステムのデータベースで管理し、写真・カメラ・マイク・連絡先などのリソースへのアクセスをプロセス起動時に仲介します。entitlements(署名時に固定される静的な資格)とTCC(実行時にユーザーが与える同意)という二段構えにより、「開発者が要求できる範囲」と「ユーザーが実際に許可した範囲」が分離され、片方が漏れても他方が防波堤になります。
Androidのパーミッションモデル:インストール時と実行時
Androidも各アプリに固有のLinuxユーザーID(UID)を割り当て、プロセスとファイルシステムの隔離をUNIXのDAC(任意アクセス制御)で行う点はiOSと発想が近いものの、権限の付与フローはAPIレベルの変遷で大きく変わってきました。
| 区分 | タイミング | 対象の例 | ユーザーの関与 |
|---|---|---|---|
| normal(通常) | インストール時に自動付与 | インターネット通信、Wi-Fi状態の取得 | 同意ダイアログなし。マニフェスト宣言のみ |
| signature | 同一署名鍵のアプリ間のみ付与 | 同一開発者アプリ間の連携API | ユーザー操作は不要 |
| dangerous(実行時) | 該当機能を使う瞬間に要求 | 位置情報、カメラ、連絡先、マイク | 都度ダイアログで許可/拒否を選択 |
Android 5以前はインストール時に危険な権限も含め一括承認する方式でしたが、Android 6(API 23)以降はdangerous権限を実行時パーミッションへ移行しました。アプリはマニフェストに権限を宣言するだけでは実際の許可を得られず、対象のAPIを呼ぶ直前にランタイムAPIでダイアログを表示し、ユーザーの明示的な選択を待つ必要があります。ユーザーは設定画面からいつでも個別に権限を取り消せるため、インストール時に許可した内容が固定されるiOSの古いモデルより、事後の制御可能性が高い設計です。
権限が付与されると、それはプロセス単位ではなくUID単位でシステムにひも付きます。実行時にAPIを呼ぶと、フレームワーク層がそのUIDに紐づく権限テーブルを参照して可否を判定します。ここまではJavaフレームワーク層の話ですが、Androidの防御はこれで終わりません。
SELinuxによる強制アクセス制御という下支え
Androidのプロセス隔離を最終的に支えているのがSELinux(Security-Enhanced Linux)です。Android 4.3から段階的に導入され、Android 5以降は全面的なenforcingモード(違反を実際にブロックするモード)が標準です。DAC(UIDやパーミッションビットに基づく任意アクセス制御)はプロセスの所有者自身が設定を変えられてしまう弱点がありますが、SELinuxのMAC(強制アクセス制御)はシステム全体で固定されたポリシーに基づき、たとえroot権限を奪われたプロセスであってもポリシーの許可がなければ操作できません。
SELinuxはドメイン(プロセスの実行文脈)とタイプ(ファイルやリソースの分類)にラベルを貼り、「どのドメインがどのタイプに対してどの操作を行えるか」をポリシーとして事前定義します。アプリプロセスはuntrusted_appのようなドメインに分類され、そこから許可されていないファイルタイプやシステムサービスへは、たとえUIDレベルのDACをすり抜けても到達できません。
AndroidのアクセスはまずDAC(UID/GIDに基づくUNIXパーミッション)を通過し、次にSELinuxのMACを通過して初めて成立します。権限昇格の脆弱性はしばしば「DACの判定は通過できたがMACで拒否される」形で被害範囲が食い止められており、片方の層が破られても他方が独立して防御する多層防御(defense in depth)の典型例です。SELinuxポリシー違反はauditログに記録されるため、攻撃の痕跡調査にも使われます。
Scoped Storageによる共有ストレージの再設計
権限モデルを整備しても、共有ストレージへの生パスアクセスが野放しなら実効性は薄れます。Android 10以降のScoped Storageは、この抜け穴を塞ぐ設計変更です。従来のWRITE_EXTERNAL_STORAGE権限は外部ストレージ全域への読み書きを許してしまい、あるアプリが他アプリの作成したファイルを無関係に読める状態でした。
Scoped Storageでは、アプリは自分の専用領域(アプリ固有ディレクトリ)と、MediaStore APIやStorage Access Framework経由でアクセスする共有メディア(写真・動画・音声・ダウンロード)とに分離されます。共有領域の生のファイルパスは原則として見えなくなり、アプリは自分が作成したファイルのメタデータのみを暗黙に把握でき、他アプリが作ったファイルへはユーザーの明示的な選択(ファイルピッカーでの選択操作)を介してのみ触れられます。これは「パスを知っていれば読める」というファイルシステムの暗黙の信頼を崩し、アクセスの都度ユーザーの意図を介在させる設計です。
権限昇格を防ぐ設計の共通原理
ここまでの各層は個別の技術に見えますが、根底にある設計原理は共通しています。
| 原理 | iOSでの実装 | Androidでの実装 |
|---|---|---|
| 最小権限 | entitlements未宣言の機能は要求すら不可 | 未宣言の権限はAPI呼び出し自体が失敗 |
| 多層防御 | コンテナ隔離+entitlements+TCC | DAC(UID)+実行時権限+SELinux |
| 改ざん耐性 | entitlementsはコード署名に封入 | SELinuxポリシーはシステムイメージに固定 |
| ユーザーの実効的な同意 | TCCが機能単位でダイアログ表示 | dangerous権限が機能単位でダイアログ表示 |
いずれのOSも、単一のチェックポイントに頼らず、静的な宣言(署名やマニフェスト)・動的な同意(ユーザーダイアログ)・カーネルレベルの強制(Sandbox/SELinux)という異なる性質の関門を直列に並べています。これにより、あるレイヤーの実装ミスや脆弱性が直ちに全権限奪取へつながることを防ぎ、仮に一つの層を突破されても被害をそのプロセスの許可範囲内に封じ込められます。権限昇格攻撃の多くは、実際にはこの直列の関門のうち一つだけを破って「もう安全なはずの領域」へ抜けようとするため、開発者側も「この操作は本当にどの層でチェックされているか」を常に意識する必要があります。
まとめ
モバイルOSのセキュリティは、プロセスをコンテナで隔離した上に、静的な権限宣言(entitlements/マニフェスト)、動的なユーザー同意(TCC/実行時パーミッション)、そしてカーネルレベルの強制アクセス制御(Sandboxプロファイル/SELinux)を積み重ねる多層構造で成り立っています。Scoped Storageのように、共有リソースへの暗黙の信頼を後から絶つ設計変更が続くのも、この「最小権限を各層で独立に検証する」原則を貫くためです。OSカーネルが担うアクセス制御の一般論は/os/、ハードウェア資源の隔離という観点は/embedded/、暗号や認証を含むより広いセキュリティ設計は/security/と合わせて理解すると見通しが良くなります。
モバイル開発 Article
モバイルのアプリサンドボックスと権限モデルを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
モバイル
比較で見る軸
難易度: advanced / カテゴリ: モバイル開発 / タグ数: 6
導入後に効く点
Androidはインストール時(normal)と実行時(dangerous)でパーミッション付与のタイミングが異なり、最終的にSELinuxのMAC(強制アクセス制御)で二重に守られる。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- モバイル開発
- タグ数
- 6
判断チェックリスト
- 自社の用途が「モバイル / セキュリティ」に近いか確認する。
- 強みである「iOSはentitlementsで署名済みの権限を宣言し、サンドボックスプロファイルがコンテナ外へのファイルアクセスをカーネル層で強制的に遮断する。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。