Context#openOrCreateDatabase
次に、Context#openOrCreateDatabaseですが、先のSQLiteDatabaseクラスのメソッドとは異なり、第一引数にはDBファイル名、第二引数は作成するファイルのパーミッション(モード)を指定します。
以下のサンプルコードでは、databasesディレクトリ以下にapp.dbというファイル名でMODE_PRIVATEで作成され、DBファイルのパーミッションは660となります。
SQLiteDatabase db = this.getContext().openOrCreateDatabase("app.db", Context.MODE_PRIVATE, null);
第二引数で指定できるモードは三種類あり以下のようになっています。
値 | 説明 |
Context.MODE_PRIVATE | 呼び出し元のアプリケーションのみ読み書き可 |
Context.MODE_WORLD_READABLE | 他のアプリケーションも読み込み可 |
Context.MODE_WORLD_WRITEABLE | 他のアプリケーションも書き込み可 |
Context.MODE_PRIVATEを指定してファイルを作成するとパーミッションは660となり、他のアプリケーションからはアクセスできないDBファイルが作成されます。
しかし、もしContext.MODE_WORLD_READABLEを指定するとパーミッションは664となり、他のアプリケーションからもアクセスできるようになります。Context.MODE_WORLD_WRITEABLEを指定した場合は662となり、他のアプリケーションから書き込みができるようになります。また、Context.MODE_WORLD_READABLEとContext.MODE_WORLD_WRITEABLEの両方を指定するとパーミッションは666となり、すべてのアプリケーションから読み書きできるファイルが作成されます。
では、実際にこのContext#openOrCreateDatabaseではどのようなことが行われているのでしょうか。
@Override public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) { File f = validateFilePath(name, true); SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f, factory); setFilePermissionsFromMode(f.getPath(), mode, 0); return db; }
内部ではSQLiteDatabase#openOrCreateDatabaseを呼び出しています。しかし、その後にsetFilePermissionsFromModeと呼ばれる関数を使用し何かしら行っているわけですが、この関数内で、非公開APIのFileUtils#setPermissionsを使用して作成されたDBファイルのパーミッションを変更しています。
SQLiteOpenHelper
先の2つのメソッドとは異なり、このSQLiteOpenHelperクラスはSQLiteを使用する際のヘルパークラスで、実際に使用する場合はこれを継承したクラスを作成し実装を進めていきます。このクラスを使用した場合、作成されるDBファイルのパーミッションは660となります。
実際に内部の動作を確認してみると、
public synchronized SQLiteDatabase getWritableDatabase() { 省略 ... boolean success = false; SQLiteDatabase db = null; if (mDatabase != null) mDatabase.lock(); try { mIsInitializing = true; if (mName == null) { db = SQLiteDatabase.create(null); } else { db = mContext.openOrCreateDatabase(mName, 0, mFactory, mErrorHandler); }
Context#openOrCreateDatabaseを使用し、作成するファイルのモードは0(MODE_PRIVATE)を指定してDBファイルを作成しています。このことから、SQLiteOpenHelperクラスでは作成されるDBファイルのパーミッションはMODE_PRIVATE固定ということが分かります。
まとめ
このように、データベースを扱うそれぞれの方法にはDBファイルのパーミッションに関して挙動の違いがあります。もし、データベースにセンシティブなデータが保存されている場合、この挙動の違いを把握しないままアプリケーションを開発してしまうと、情報漏えいのおそれがあります。
特別な理由がない限り、ファイルを作成する場合は以下の2点に留意してください。
- Context#openOrCreateDatabaseを使用し、MODE_PRIVATEを指定してDBファイルを作成する
- SQLiteOpenHelperクラスを使用してデータベースを扱う
これらいずれかの方法でDBファイルへのアクセスを制限する必要があります。なお、Javaセキュアコーディングスタンダードにはファイルのパーミッションについて対応するルールがあります。
こちらも併せて参照してください。
おまけ:ファイルのパーミッションを変更するには
AndroidのAPIレベル9以降では、File#setReadableやFile#setWritableなどのファイルのパーミッションを変更するメソッドが用意されています。しかし、それ以前のバージョンではそのようなファイルのパーミッションを変更するメソッドは用意されていません。もし全てのバージョンに対応する場合は、ネイティブコードを書くか、またはリフレクションを使用して先のContext#openOrCreateDatabase内で使用しているような非公開APIのFileUtils#setPermissionsにアクセスし使用することになります。
Class<?> fileUtils = Class.forName("android.os.FileUtils"); Method setPermissions = fileUtils.getMethod("setPermissions", String.class, int.class, int.class, int.class); int flag = (Integer) setPermissions.invoke(null, filename, permissions, -1, -1);
リフレクションを使用するとクラスやメソッドの情報を取得したり実行したりすることができ、結果として非公開APIにアクセスすることも可能になります。Javaセキュアコーディングスタンダードには、リフレクションの使用に対応するルールがあるので参照してください。