Androidアプリ実装におけるセキュリティ(2) – ファイル編 -
はじめに
Androidアプリの安全な実装について、
前回は「(1) 他アプリからのAndroidコンポーネントの不正利用の脆弱性」について解説しました。引き続き、今回は「(2) ファイルからの情報漏洩」について解説していきます。
Androidアプリの構成要素とセキュリティリスクの発生箇所の対応関係
脆弱性と対策の概要
Androidアプリのデータ保存形式としてはファイル、プリファレンス、データベースがあり、それぞれの形式を扱う上でもさまざまな実現手段(API)が用意されています。
これらの中には使用方法に癖のあるものも多く、誤った使い方をすると保存データが意図せずに他のアプリに公開される恐れがあります。
脆弱性
データ保存に利用するAPIの使用方法に不備がある場合、他のアプリから自由にデータが読み書きできる。
脅威
>悪意のあるアプリから重要情報を取得されたり、データを改竄・消去される。
想定される悪影響
情報漏洩、漏洩した認証情報を利用した不正アクセス、改竄されたデータを利用した不正使用など。
対策
データ保存に利用するAPIを適切に使い分ける。
解説
Androidアプリのファイルへのアクセス制御の仕組みと、使用可能なAPIおよび使用方法の注意点について解説します。
Android OSのファイルパーミッション
Android OSでは、Androidアプリのインストール時にアプリ固有のユーザIDが生成されます。
各アプリはそれぞれの固有のユーザによって実行される仕組みとなっており、ファイルやディレクトリへのアクセスはこのユーザのアクセス許可情報に基づいて制限されます。
ファイル、プリファレンス、データベースのいずれのデータ保存形式においても、データはAndroid OSのファイルシステム上の1ファイルとして保存され、このファイルのパーミッション設定がアクセス制限に利用されます。
Android OSのシェルを起動し、ファイル詳細情報を表示するコマンドを実行した例を以下に示します。
ls -l ls -l -rw-rw-r-- app_37 app_37 4096 2012-07-09 07:40 mydatabase.db
この表示結果は、標準のLinuxにおけるファイルパーミッションと同じ意味を示しています。
すなわち、「mydatabase.dbファイルは、app_37ユーザおよびapp_37グループからは読み込み・書き込みが可能であり、その他のユーザからは読み込みが可能」であることを示しています。
この仕組みを踏まえ、データを保存する際は適切なファイルパーミッションを設定可能なAPIを選択して使用する必要があります。
データ保存時に使用するAPIと設定可能なパーミッション
ファイル、プリファレンス、データベースへデータを保存するAPIについて、ファイルの出力先やファイル名、指定可能な公開モードとファイルパーミッションの対応関係について解説します。
共通する注意点として、使用するAPIによってファイルの出力先が固定のものと指定可能なものがあることがあげられます。
出力先が固定のものについては、他のアプリから読み取り・書き込みできない自アプリ専用のディレクトリ内に保存されるため、安全性の確保が容易であり、またアプリのアンインストール時に自動的に削除されるなどファイル管理の観点でもシンプルな作りにできます。
一方、出力先を独自に指定すると、安全性が損なわれやすかったり、ファイル管理が煩雑になったりするため、特別な理由がない限りは避けたほうがよいでしょう。
このようなファイルはアプリのアンインストールによっても自動的に削除されず、その他のユーザにアクセス許可を与えていない場合は削除することすらできなくなります。
ファイル作成API
ファイルにデータを保存する場合は以下が利用可能です。
特別な事情がない限り、ファイルの出力先を独自に指定しなくてはならない場面はほとんどないと思われます。
ファイルのパーミッションと管理をシンプルにするためにも、Androidアプリ用のAPIであるContext#openFileOutputメソッドを使用すべきでしょう。
API | 出力先 | ファイル名 | 指定可能な公開モード | ファイルパーミッション |
---|---|---|---|---|
android.content.Context #openFileOutput(String, int) |
固定 (/data/data /<パッケージ名> /files/) |
指定可能 | MODE_PRIVATE | -rw-rw—- |
MODE_WORLD_READABLE | -rw-rw-r– | |||
MODE_WORLD_WRITEABLE | -rw-rw–w- | |||
MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE | -rw-rw-rw- | |||
java.io .File(String) |
指定可能 | 指定可能 | Fileクラスの各APIを用いて自由に設定可能 | 同左 |
プリファレンス作成API
プリファレンスにデータを保存する場合は以下が利用可能です。
どのAPIを使用しても、意図せずに安全性が損なわれたり、ファイル管理が煩雑になるといったことは起きにくいです。
主な違いは、ファイル名に制約を与えるかどうかであるため、ファイル管理のポリシーにあうAPIを使用するのがよいでしょう。
API | 出力先 | ファイル名 | 指定可能な公開モード | ファイルパーミッション |
---|---|---|---|---|
android.content.Context #getSharedPreferences(String, int) |
固定 (/data/data /<パッケージ名> /shared_prefs/) |
指定可能 | MODE_PRIVATE | -rw-rw—- |
MODE_WORLD_READABLE | -rw-rw-r– | |||
MODE_WORLD_WRITEABLE | -rw-rw–w- | |||
MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE | -rw-rw-rw-` | |||
android.preference.PreferenceManager .getDefaultSharedPreferences(Context) |
固定 (/data/data /<パッケージ名> /shared_prefs/) |
<パッケージ名> _preferences.xml | 指定できない | -rw-rw—- |
android.preference.PreferenceActivity #addPreferencesFromResource(int) |
固定 (/data/data /<パッケージ名> /shared_prefs/) |
<パッケージ名> _preferences.xml | 指定できない | -rw-rw—- |
android.app.Activity #getPreferences(int) |
固定 (/data/data /<パッケージ名> /shared_prefs/) |
<アクティビティ名> .xml | MODE_PRIVATE | -rw-rw—- |
MODE_WORLD_READABLE | -rw-rw-r– | |||
MODE_WORLD_WRITEABLE | -rw-rw–w- | |||
MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE | -rw-rw-rw- |
データベース作成API
データベースにデータを保存する場合は以下のAPIが利用可能です。
指定可能なファイルパーミッションやデフォルト値がAPI每に異なっているため、用途に応じて適切な使い分けが必要です。特にSQLiteDatabase#openOrCreateDatabase
メソッドで作成されたデータベースファイルは、他の全てのアプリから読み取り可能となる点に注意してください。
API | 出力先 | ファイル名 | 指定可能な公開モード | ファイルパーミッション |
---|---|---|---|---|
android.content.Context #openOrCreateDatabase(String, int, CursorFactory) |
指定可能 ※省略した場合、/data/data /<パッケージ名> /databases/ |
指定可能 | MODE_PRIVATE | -rw-rw—- |
MODE_WORLD_READABLE | -rw-rw-r– | |||
MODE_WORLD_WRITEABLE | -rw-rw–w- | |||
MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE | -rw-rw-rw- | |||
android.database.sqlite .SQLiteOpenHelper(Context, String, CursorFactory, int) |
指定可能 ※省略した場合、/data/data /<パッケージ名> /databases/ |
指定可能 | 指定できない | -rw-rw—- |
android.database.sqlite.SQLiteDatabase .openOrCreateDatabase( String, CursorFacotry) |
指定可能 ※省略した場合、/data/data /<パッケージ名> /databases/ |
指定可能 | 指定できない | -rw-r–r– |
特定のアプリとデータを共有する場合
ファイルパーミッションを利用したアプリ間でのアクセス制御は、「その他のユーザ」のアクセス許可情報のみを用いて実現されています。
つまり、他の全てのアプリに対する公開範囲しか設定できず、特定のアプリとデータを共有するといったことは実現できません。
なお、「グループユーザ」のアクセス許可情報は、Androidアプリのパーミッション情報(android:permission属性)の管理に利用されており、公開範囲の設定に利用することはできません。
そのため、特定のアプリとデータを共有する際はandroid:SharedUserId属性か、またはデータ共有の仕組みを提供するコンテンツプロバイダ(ContentProviderクラス)を使用してください。
外部ストレージを利用する際の注意点
外部ストレージへ保存したファイルは、アプリ固有のユーザの所有物ではなくなり、他のユーザに等しく公開されます。
そのため、特定のアプリと重要情報を共有したい場合などには、外部ストレージへデータを保存しないよう注意する必要があります。
執筆者プロフィール 加藤 慧(NTTコムウェア株式会社)
品質生産性技術本部 技術SE部に所属。社内のJavaおよびJavaScript案件を中心とした開発支援に携わっています。Emacs派。