エンジニア諸君、現場の最前線へようこそ。
今日は、モバイルアプリ開発で誰もが一度は誘惑に駆られる「データのローカル保存」という地雷原について話そう。なぜか「ローカルにあるデータなら安全だろう」と勘違いしているエンジニアが後を絶たないが、結論から言えば、平文でのローカル保存は「どうぞ解析してください」と泥棒に鍵を渡しているのと同じだ。
今回は、Androidの SharedPreferences や iOSの Plist に機密情報をぶち込むリスクと、それをプロフェッショナルなレベルで封じ込めるための「SQLCipher」を用いた実践的アプローチを解説する。
—
なぜ「ローカル保存」が脆弱性の温床になるのか
攻撃者の視点に立てば、モバイルデバイスの解析など朝飯前だ。
もし君が SharedPreferences や Plist にAPIトークンや個人情報を平文で書き込んだら、攻撃者はデバイスをPCに接続し、adb backup やジェイルブレイク(ルート化)済みの端末から、一瞬でそのファイルを抜き出す。アプリのサンドボックス境界など、root権限の前では無力だ。
PoC:攻撃者はどうやって君のデータを奪うのか
攻撃者は、adb shell を使って以下のようなコマンドで一瞬で君の「宝箱」をコピーする。
# アプリのデータディレクトリへ侵入
adb shell
run-as com.your.appname
# 内部のSharedPreferencesディレクトリを特定し、ファイルをPCへ転送
cp /data/data/com.your.appname/shared_prefs/user_settings.xml /sdcard/
この user_settings.xml を開けば、name="auth_token" value="eyJhbG..." のように、君が守るべき秘密が丸見えだ。ここから先は、セッションハイジャックやなりすまし攻撃の始まりである。
—
守るための鉄則:SQLCipherによる透過的暗号化
「暗号化すればいい」と安易に独自実装のAESを組もうとするな。鍵管理を誤れば、暗号化はただの無駄な処理になる。モバイルで機密性の高い構造化データを扱うなら、SQLCipher 一択だ。これはSQLiteに透過的なAES-256暗号化を追加したもので、データベースファイル全体を暗号化できる。
実装の勘所:KotlinでのSQLCipher統合例
AndroidでSQLCipherを使用する場合、従来の SQLiteOpenHelper を net.zetetic.database.sqlcipher.SQLiteOpenHelper に差し替えるだけでいい。
// SQLCipherを用いた暗号化DBの初期化サンプル
import net.zetetic.database.sqlcipher.SQLiteDatabase
import net.zetetic.database.sqlcipher.SQLiteOpenHelper
class SecureDatabaseHelper(context: Context) : SQLiteOpenHelper(context, "secure.db", null, 1) {
override fun onCreate(db: SQLiteDatabase) {
db.execSQL("CREATE TABLE users (id INTEGER PRIMARY KEY, token TEXT);")
}
// 接続時にパスフレーズを要求する
fun getEncryptedDatabase(passphrase: String): SQLiteDatabase {
return this.getWritableDatabase(passphrase)
}
}
ここが最大のポイント:
コード内にパスフレーズをハードコードしてはいけない。鍵は Android Keystore System を使用して生成し、生成した鍵でパスフレーズを暗号化して保存する。これが「多層防御」だ。
—
暗号理論の使い分け:AESとRSAの境界線
現場でよくあるミスが、暗号化アルゴリズムの選定ミスだ。
- AES (共通鍵暗号): 高速であり、大量のデータを暗号化するのに向いている。SQLCipherの内部はこれだ。鍵を失えばデータはゴミになる。
- RSA / ECC (公開鍵暗号): 通信の鍵交換や電子署名に向いている。非常に重いため、ローカルDBの全データをこれで暗号化するのはパフォーマンス的に自殺行為だ。
実務のルール:
- データそのものは AES-256 (GCMモード推奨) で暗号化する。
- そのAES鍵を保護するために RSA / ECC を使う、あるいは
KeyStoreの鍵管理機能を利用する。
—
セキュリティチーフからの「現場の教訓」
最後に、一つだけ覚えて帰ってほしい。「クライアント側に秘密は存在しない」という前提を忘れるな。
どれだけ堅牢な暗号化を施しても、OSレベルの脆弱性やOSのアップデートに伴う挙動の変化によって、予期せぬ場所へデータが漏洩する可能性はゼロではない。
1. 機密データは極力ローカルに持たない: 必要最低限のキャッシュ以外はクラウドへ。
2. キーチェーン/キーストアを絶対視する: 自前で鍵を管理しようとするな。OSが提供するハードウェア backed な鍵管理機能にすべてを委ねろ。
3. Proguard/R8で難読化を徹底する: 解析の難易度を上げることは、攻撃者のモチベーションを下げることに直結する。
技術は日々進化するが、攻撃者の動機は変わらない。「楽に、確実に奪える場所」が狙われる。君たちが書くその一行のコードが、ユーザーの人生を左右するかもしれない。常にその意識を持ち、実装には「疑い」を持って臨んでくれ。
何か不明点があれば、またいつでも相談に来い。コードのレビューはいつでも歓迎するぞ。
コメント