【実務・中級編】 モバイルアプリにおける暗号化データの不適切な保存(SharedPreferences/Plist) – 暗号理論・認証基盤 & エンドポイントセキュリティ防御ガイド

エンジニア諸君、現場の最前線へようこそ。

今日は、モバイルアプリ開発で誰もが一度は誘惑に駆られる「データのローカル保存」という地雷原について話そう。なぜか「ローカルにあるデータなら安全だろう」と勘違いしているエンジニアが後を絶たないが、結論から言えば、平文でのローカル保存は「どうぞ解析してください」と泥棒に鍵を渡しているのと同じだ。

今回は、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で難読化を徹底する: 解析の難易度を上げることは、攻撃者のモチベーションを下げることに直結する。

技術は日々進化するが、攻撃者の動機は変わらない。「楽に、確実に奪える場所」が狙われる。君たちが書くその一行のコードが、ユーザーの人生を左右するかもしれない。常にその意識を持ち、実装には「疑い」を持って臨んでくれ。

何か不明点があれば、またいつでも相談に来い。コードのレビューはいつでも歓迎するぞ。

コメント

タイトルとURLをコピーしました