View on GitHub
mixi-inc/AndroidTraining
セキュリティ
この章では、Android におけるセキュリティについて解説します。

参考:Permissions | Android Developers
参考:Android Security
参考:Android アプリのセキュア設計・セキュアコーディングガイド

目次

パーミッション

Android では、通常特に宣言のない限りは、他のアプリや OS、ユーザに悪い影響を与える、いかなる操作もできないようにしています。
他のアプリのデータを読み取ったり、カメラや位置情報を利用したりするものがその例です。
これを特別に許可する仕組みとして、パーミッションが存在します。

パーミッションは、常に AndroidManifest で静的に宣言されるので、動的(実行時)にパーミッションを設定することはできません。
またパーミッションは、そのレベルによって、インストール時にユーザへ同意を求めるものや、同じパッケージ署名でなければならないもの、システムにしか権限を与えないものなどに分類されます。

通常、パーミッションは、外部に公開するコンポーネントに対して宣言を行い、その使用を制限します。
例えば、以下のような操作をしようとする場合に、パーミッションが要求されます。

  • アプリケーションをシステムにインストールする際、特定の機能をアプリケーションが呼び出すことを防ぐため
  • Activity を呼び出す際、他のアプリの Activity を起動するのを防ぐため
  • ブロードキャストの送受信の際、ブロードキャストを受け取れる人や、送信できる人を制限するため
  • ContentProvider へアクセスしたり操作したりする際
  • サービスをバインドしたり開始したりする際

パーミッションの宣言

この項では、AndroidManifest における各種パーミッションの宣言について解説します。

<uses-permission>

他のアプリや、システムが宣言しているパーミッションを使用する宣言を行います。

パーミッションを宣言すると、インストール時にユーザに対して同意を求めるダイアログが表示されます。
パーミッションのレベルがdangerousと宣言されているものを取得すると、同意画面上で優先的に表示されるようになります。<br /. 同じ署名でなければ利用できないパーミッションを、異なる署名のアプリから利用することはできません。

ここでパーミッションの取得宣言をしなかったにもかかわらず、パーミッションの必要な呼び出しを行った場合、SecurityExceptionが実行時にスローされます。

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.mixi.sample"
    android:versionCode="1"
    android:versionName="1.0">
    <!-- ネットワークの状態を取得するためのパーミッションを取得する宣言 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <!-- 位置情報を取得するためのパーミッションを取得する宣言 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
</manifest>

<permission>

自分でパーミッションを宣言することもできます。
パーミッション名やアイコン、説明、レベルを設定します。
パーミッション名とアイコン、説明は、同意画面に表示されるので、分かりやすいものを設定します。

<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.mixi.sample"
    android:versionCode="1"
    android:versionName="1.0">
    <!-- パーミッションの定義 -->
    <!-- android:name には、ユニークなパーミッション名を定義する -->
    <permission
        android:name="jp.mixi.sample.permission.READ_CONTENT_PROVIDER"
        android:label="@string/permission_label_contentprovider"
        android:icon="@drawable/ic_launcher"
        android:description="@string/permission_description_contentprovider"
        android:protectionLevel="signature">
    </permission>

    <application>
        <!-- コンポーネントに対するパーミッション要求の設定 -->
        <!-- android:permission 属性を利用して、このコンポーネントを呼び出す際にパーミッションを要求する -->
        <!-- Activity の場合、Context#startActivity() や Context#startActivityForResult() の呼び出しでチェックされる -->
        <activity
            android:name="jp.mixi.sample.SamplePermissionActivity"
            ...
            android:permission="jp.mixi.sample.permission.ACCESS_ACTIVITY">
        </activity>
        <!-- Service の場合、Context#startService() や Context#stopService()、Context#bindService() の呼び出しでチェックされる -->
        <service
            android:name="jp.mixi.sample.SamplePermissionService"
            ...
            android:permission="jp.mixi.sample.permission.CALL_SERVICE">
        </service>
        <!-- BroadcastReceiver の場合、この BroadcastReceiver への Intent の送信可否を宣言することになる。 -->
        <!-- もし許可のないまま BroadcastReceiver に Intent を送信しても、SecurityException とはならない。 -->
        <!-- ただし、Intent はこの BroadcastReceiver に送信されない。 -->
        <!-- SecurityException とはならないのは、Context#sendBroadcast() から戻った後にパーミッションがチェックされるからである。 -->
        <!-- 逆に、Context#sendBroadcast() の時にパーミッションを宣言することもできる。 -->
        <!-- この場合、宣言したパーミッションを取得している BroadcastReceiver のみがその Intent を受け取ることになる。 -->
        <receiver 
            android:name="jp.mixi.sample.SamplePermissionReceiver"
            ...
            android:permission="jp.mixi.sample.permission.CALL_BROADCAST_RECEIVER">
        </receiver>
        <!-- ContentProvider の場合、他のコンポーネントと異なり、READ と WRITE の 2 つの操作に対して、別々のパーミッションを設定できる -->
        <!-- 両方のパーミッションを宣言した場合、WRITE のパーミッションを持っていても、READ も許可されるとは限らない点に注意する -->
        <provider
            android:name="jp.mixi.sample.SamplePermissionContentProvider"
            ...
            android:readPermission="jp.mixi.sample.permission.READ_CONTENT_PROVIDER"
            android:writePermission="jp.mixi.sample.permission.WRITE_CONTENT_PROVIDER">
        </provider>
    </application>
</manifest>

ProtectionLevel

Android のパーミッションで設定可能な保護レベルは下記のとおりです。複数設定することができます。

レベル 意味
normal リスクの低いパーミッションで、システムや他のアプリケーション、ユーザへの影響が少ないものを意味します。システムは、インストール時に自動でこのパーミッションの許可を付与するので、ユーザに対する同意画面は表示されません(ただし、設定画面などからパーミッションの確認はできます)。
dangerous リスクの高いパーミッションで、システムや他のアプリケーション、ユーザへの影響が大きいもの(プライベートなデータの参照など)を意味します。このため、インストール時には必ずユーザへの同意画面が表示されます。
signature このパーミッションを宣言しているアプリケーションと同じ鍵で署名されたアプリケーションでなければ、このパーミッションに対する許可が付与されないことを意味します。署名が合えば、ユーザに同意画面を表示することなく、自動で許可を付与します。
signatureOrSystem 同じ署名、または Android のシステムにのみ許可を付与します。通常、このオプションは使用しないことをおすすめします。
system Android のシステムに許可を付与します。通常、このオプションは使用しないことをおすすめします。
development 開発中のアプリケーションに許可を付与します。

アプリが依存する端末機能の使用宣言

カメラや位置情報など、端末のハードウェアないしソフトウェアの資源に依存する機能を有する場合、特別に、その宣言をしておく必要があります。
これは、その機能を有するかどうかが、デバイスごとに異なるため、予め依存する資源を宣言しておくことで、インストール可能かどうかを判断できるようにする為です。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.mixi.sample.di"
    android:versionCode="1"
    android:versionName="1.0">
    <!-- カメラ機能の宣言。必ずしも必要としない場合は、android:required 属性を false とする。 -->
    <uses-feature
        android:name="android.hardware.camera"
        android:required="false"/>
</manifest>

WebView

WebView は、指定された URL のページを読み込んだり、HTML を直接読み込んだりして、Web ページを表示するための View です。
WebView は、その特性から、いくつかの重要なセキュリティ対策項目があります。

参考:Androidセキュリティ勉強会 ~WebViewの脆弱性編~

JavaScript Interface

WebView には、JavaScript Interface という特別なインタフェースがあります。
これは、JavaScript からネイティブに宣言されたインタフェースを介することで、Java のコードを実行するものです。

公式リファレンスにも記述がありますが、信頼出来る Web ページ以外で、この JavaScript Interface を有効にしないことを強く推奨します。
なぜならば、JavaScript からJava のコードを呼び出すことが出来るため、不用意な設計をしていると、アプリのユーザ権限で様々な攻撃が可能となってしまうためです。

キャッシュ

WebView は、SQLiteDatabase を利用したキャッシュの仕組みを持っています。
この、SQLiteDatabase のキャッシュファイルは、/data/data/<app_package_name>/databases/webview.dbに保存されています。
キャッシュファイルには、ID やパスワードの情報も含まれますが、暗号化などは特にかけられていません。

他のアプリからのアクセスは拒否されますが、WebView 自身はすべてのパーミッションが付与されている状態であることから、WebView に何らかの攻撃手段を持たせてしまうと、ID やパスワードが抜き取られる危険性があります。

たとえば、標準ブラウザのキャッシュへは、標準ブラウザのアドレスバーに下記のように入力することでアクセスすることができます。

# 以下のどちらかの URL でアクセスします。
file:///data/data/com.android.browser/databases/webview.db
file:///data/data/com.google.android.browser/databases/webview.db