セキュリティモデル

Androidは各アプリケーションが(システム内の各部分も同様に)固有のプロセスを実行する、マルチプロセスシステムです。アプリケーションとシステムの間のセキュリティは、標準的なLinuxと同様にしてアプリケーションに割り当てられたユーザ・グループID制御により、プロセスレベルで確保されます。これ以外の、より洗練された[※1] セキュリティ機能はあるプロセスが行うことの出来る処理を制限するための『許可情報』メカニズムにより提供されます。

目次

ユーザIDとファイルアクセス

各Androidパッケージファイル(.apk)は、インストール時にユニークなLinuxのユーザIDを割り当てられます。これにより、他アプリケーションの情報を触ったり、逆に他アプリケーションから自身の情報にアクセスされることは無くなります。ユーザIDはアプリケーションがデバイスへインストールされる際に割り当てられ、デバイス上に存在し続ける限り変化しません。

セキュリティ制限はプロセスレベルで行われ、アプリケーションは個別のユーザIDにより実行する必要があるため、通常は2パッケージに属するコードを同一プロセス内で実行することは出来ません。AndroidManifest.xmlファイル中で各パッケージのmanifestタグにおいてsharedUserId属性を記述することで、同一のユーザIDを割り当てることが出来ます。2つのパッケージが同一のユーザIDとファイルアクセス許可を持つことでセキュリティ上の観点からこれらのパッケージのうち片方に対する脅威は同じくもう片方に対する脅威となります。

アプリケーションにより作成されたファイルには全てそのアプリケーションのユーザIDが割り当てられ、通常は他のパッケージから読み出すことは出来ません。getSharedPreferences(String, int)openFileOutput(String, int)createDatabase(String, int, int, SQLiteDatabase.CursorFactory)を用いてファイルを作成する際にMODE_WORLD_READABLEまたはMODE_WORLD_WRITEABLE、あるいはその両方を指定することにより他のパッケージからのファイル読み書きを可能とすることが出来ます。この場合ファイルの所有者は依然としてファイル作成元アプリケーションのままです。

許可情報の使い方

特別な権限情報を持たない通常のAndroidアプリケーションは、ユーザ体験やデバイスに対して致命的な影響を与えることはありません。デバイス上の保護された機能を利用するためには、AndroidManifest.xmlファイルに、必要な許可情報を宣言する1つ以上の<uses-permission>タグを記述する必要があります。

例えば、SMSのメッセージを受信する必要のあるアプリケーションにおいては以下のようになります。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.google.android.app.myapp" >
    <uses-permission id="android.permission.RECEIVE_SMS" />
</manifest>

アプリケーションのインストール時に、アプリケーションから求められた許可がパッケージインストーラによりユーザ承認の上で与えられます。[※2] アプリケーション実行時には一切のチェックが行われません。このためインストール時に許可された機能は利用出来ますが、許可されなかったものを実行しようとすると、ユーザへの通知無く失敗します。

許可不足によりアプリケーションへSecurityExceptionが投げられることがありますが、必ずしもいつも投げられるとは保証されていません。例えば、broadcastIntent(Intent) メソッドはメソッド呼び出し完了時点でIntent通知を受けようとしている全てのレシーバの許可情報をチェックしますが、許可が無い場合にも例外は発生しません。しかし多くの場合、許可不足情報はシステムログへ記録されます。

Androidシステムにより提供される許可情報一覧はManifest.permissionに記述されていますが、アプリケーションごとに独自の許可情報を作成することが出来るため、ここのリストは考えうる全ての許可情報を網羅したものではありません。

プログラムの処理時に必要となる許可情報の例を示します

許可情報の宣言と適用

許可情報を適用するためには、まず一つ以上の<permission>タグを使ったAndroidManifest.xml内での宣言が必要となります。

例えば、自身のアクティビティを実行できるものを制限したい場合、以下のような許可情報宣言を行います。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.google.android.app.myapp" >
    <permission id="com.google.android.app.myapp.permission.DEADLY_ACTIVITY"
        android:label="@string/permlab_deadlyActivity"
        android:description="@string/permdesc_deadlyActivity" />
</manifest>

許可情報には、labelとdescriptionの両方を指定する必要があります。これらはそれぞれ文字列リソースであり、ラベル(android:label)はユーザが許可情報一覧を見るためのもので、説明(android:description)はある許可情報の詳細を示すものです。ラベルは短めに数単語で、この許可情報が保護しているものについて記述すべきです。説明には許可が与えられることで何が出来るのかということを数文で記述すべきです。基本スタイルは、許可情報を説明する文と、許可を受けることで起こりうる弊害について警告する文、という2文で構成するものです。

CALL_PHONE許可情報におけるラベルと説明の例を以下に示します。

    <string name="permlab_callPhone">Call Phone Numbers</string>
    <string name="permdesc_callPhone">Allows application to call
        phone numbers without your intervention.  Bad applications may
        cause unexpected calls on your phone bill.</string>

AndroidManifest.xml内で許可情報を適用する

AndroidManifest.xmlファイル内で、システムやアプリケーションの全コンポーネントに対する高レベルな許可制限をかけることが出来ます。この場合、指定をかけたいコンポーネントの定義に許可対象名付きの[※4] android:permission属性を追加する[※5] と、アクセス制御が行われます。

<activity>タグに適用するActivity許可情報は、当該アクティビティを開始出来るものを制限します。許可情報はContext.startActivity()Activity.startSubActivity()の中でチェックされ、呼び出し側が許可を持っていない場合はSecurityExceptionが発生します。

<service>タグに適用するService許可属性は、当該サービスを開始出来るものを制限します。許可情報はContext.startService()Context.stopService()Context.bindService()の中でチェックされ、呼び出し側が許可を持っていない場合はSecurityExceptionが発生します。

<receiver>タグに適用するIntentReceiver許可情報は当該レシーバへIntentオブジェクトを送信出来るものを制限します。許可情報はContext.broadcastIntent()が完了した時点でチェックされ、システムは送信されたIntentをレシーバへ送信しようとします。結果的に、許可不足により例外が呼び出し側へ投げられることは無く、単純にIntentが届かないだけです。同様にしてプログラム的に登録されたレシーバへIntent送信を出来るものを制限するために、Context.registerReceiver()へ許可情報を渡すことが出来ます。[※6] また後述するように、Context.broadcastIntent()の呼び出し時に、どのIntentReceiverオブジェクトがIntent受信を行えるかという許可情報を付加することも出来ます。

<provider>タグに適用するContentProvider許可情報は、ContentProvider内のデータへアクセス出来るものを制限します。これは他のコンポーネントとは異なり2つの異なる許可属性を設定することが出来ます。android:readPermissionではContent Providerからの読み取りを、android:writePermissionではContent Providerへの書き込みを制限します。プロバイダへの読み込み・書き込み共が共に制限されている場合、書き込み権限のみを持っていても読み込みは出来ないということになります。許可情報は最初にContent Providerを取得する際にチェックされて許可を両方とも持っていない場合にはSecurityExceptionが発生します。Content Providerに対してContentResolver.query()等を用いた操作を行うには読み込み権限が必要で、ContentResolver.insert()ContentResolver.update()ContentResolver.delete()Cursor.commitUpdates()を実行するには書き込み権限が必要となります。いずれにしても必要な権限を持っていない場合にはこれらのメソッド呼び出し時にSecurityExceptionが発生します。

Intent送信への許可情報適用

前述の、登録されたIntentReceiverへIntent送信を行うことの出来るものを制限するものに加え、Intent送信時に許可情報を設定することが出来ます。Context.broadcastIntent()の呼び出し時に許可情報文字列を付加することで、Intent受信を出来るアプリケーションを、当該許可を持ったものに制限することが出来ます。

つまりIntentに関しては、送信側も受信側も許可情報を設定することが出来、両方のチェックを通った場合のみに配信が成功するということになります。

その他の許可情報適用について[※7]

全てのサービス呼び出しにおいて、Context.checkCallingPermission()メソッドを呼び出すことで許可情報適用を徹底することが出来ます。[※8] このメソッドにチェック対象の許可情報文字列を渡すと、当該許可が呼び出し元に与えられているか否かを示す整数値を返します。この方法はサービスが公開しているIDLインターフェイスからの呼び出しなど、他プロセスからの呼び出し時のみ有効であることに注意してください。

権限チェックに便利な方法をいくつか紹介します。もし他プロセスのpidが分かる場合は、コンテキストメソッドのContext.checkPermission(String, int, int)を呼び出すことで当該pidに対する許可情報をチェックすることが出来ます。他アプリケーションのパッケージ名が分かる場合にはパッケージマネージャのメソッドであるContext.checkPermission(String, String)を呼び出すことで、そのパッケージが特定の権限を許可しているかどうか調べることが出来ます。