はじめに
Androidアプリケーションを作成する上でSQLiteを使用してデータベースを扱うことは多々あります。データの永続化や検索といった処理を容易に実装することができるので、多くのアプリケーションで使用されています。しかし、AndroidにはSQLiteを使用するための方法が以下のようにいくつか用意され、それぞれファイル作成時のパーミッションの挙動が異なっています。
- SQLiteDatabase#openOrCreateDatabaseを使用して作成する
- Context#openOrCreateDatabaseを使用して作成する
- SQLiteOpenHelperクラスを使用して作成する
もし、開発者がこれら方法の挙動を十分に理解していない場合、知らずのうちに脆弱性を作り込んでしまいます。
実際に、いくつかのアプリケーションではSQLiteDatabase#openOrCreateDatabaseを起因とするファイルパーミッションに関する脆弱性が発見されています。
Androidのアプリケーションは、基本的に他のアプリケーションで管理されているファイルにはアクセスできません。しかし、方法によっては他のアプリケーションからもアクセスできるファイルが作成されてしまいます。今回はこれらDBファイルを作成する方法について、それぞれDBファイルのパーミッションはどのようになっているのか、その挙動について解説します。
SQLiteDatabase#openOrCreateDatabase
まず、SQLiteDatabase#openOrCreateDatabaseですが、以下のサンプルコードではdatabasesディレクトリ以下にapp.dbというファイル名でDBファイルが作成されます。
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(new File( "/data/data/" + getContext().getPackageName() + "/databases/", "app.db"), null);
作成されたDBファイルのパーミッションを確認すると644となっており、他のアプリケーションからもアクセスできるようになっています。では、実際にこのメソッドの内部ではどのようなことが行われているのでしょうか。
openOrCreateDatabaseメソッドの内部ではopenDatabaseメソッドが呼ばれています。
/** * Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY). */ public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory) { return openDatabase(path, factory, CREATE_IF_NECESSARY); } 省略 ... public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags) { return openDatabase(path, factory, flags, new DefaultDatabaseErrorHandler()); } 省略 ... public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags, DatabaseErrorHandler errorHandler) { SQLiteDatabase sqliteDatabase = openDatabase(path, factory, flags, errorHandler, (short) 0 /* the main connection handle */); 省略 ... private static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags, DatabaseErrorHandler errorHandler, short connectionNum) { SQLiteDatabase db = new SQLiteDatabase(path, factory, flags, errorHandler, connectionNum); try { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.i(TAG, "opening the db : " + path); } // Open the database. db.dbopen(path, flags); db.setLocale(Locale.getDefault());
openDatabaseメソッドではネイティブメソッドのdbopenが呼ばれ、その中でSQLiteの関数の一つであるsqlite3_open_v2関数を使用してDBファイルが作成されるようになっています。
/* public native void dbopen(String path, int flags, String locale); */ static void dbopen(JNIEnv* env, jobject object, jstring pathString, jint flags) { 省略 ... err = sqlite3_open_v2(path8, &handle, sqliteFlags, NULL); if (err != SQLITE_OK) { LOGE("sqlite3_open_v2(\"%s\", &handle, %d, NULL) failed\n", path8, sqliteFlags);
この付近のコードを確認する限り、作成されるファイルのパーミッションを設定したり変更したりという処理は見当たりません。よってデフォルトのumaskで作成され、その結果、ファイルのパーミッションは644になっているのではないかと推測します。