セキュアストレージは、デバイス上の機密データ(トークン、暗号化キー、決済情報、個人データ)を保護するための方法や技術の組み合わせです。OWASP Mobile Top 10 (2024)によると、不安全なデータストレージは最も重大なリスクのトップ3に入っています。セキュアストレージを適切に実装することで、デバイスへの物理的アクセスがあってもデータ漏えいを防ぐことができます。
まとめ
セキュアストレージとは、モバイルアプリの機密データを、他のアプリケーション、マルウェア、およびデバイスに物理アクセスできる攻撃者からアクセス不可能なように保管する実践です。一般的なストレージとは異なり、セキュアストレージは暗号化、分離、およびハードウェア保護を使用します。
すべてのデータにセキュアストレージが必要なわけではありません: プロフィール画像やニュースキャッシュは一般的なファイルシステムに保管できます。しかし、暗号化キー、認証トークン、決済データ、秘密キー、生体認証テンプレートは必ず保護する必要があります。Google Security Blog (2025)によると、モバイルアプリの67%の脆弱性は、機密情報を明文で保管することに関連しています。
各モバイルプラットフォームは独自のセキュアストレージメカニズムを提供しています: Android — KeystoreとEncryptedSharedPreferences、iOS — KeychainとData Protection API。これらのメカニズムはハードウェアセキュリティモジュール (TEE、Secure Enclave) と統合されており、ジェイルブレイクやルーティング後でもデータを読むことができないことを保証します。
セキュアストレージ方法の正しい選択は、データの種類、使用シナリオ、パフォーマンス要件によって異なります。各メカニズムのアーキテクチャを理解することで、開発者は正しいアーキテクチャ的決定を下すことができます。
Androidプラットフォームは、ハードウェアキーストレージから暗号化SharedPreferencesまで、複数のデータ保護レベルを提供しています。選択はデータの機密度とパフォーマンス要件によって異なります。
Android Keystoreは、ハードウェア保護をサポートするデバイス上で、収容実行環境 (TEE — Trusted Execution Environment) にキーを生成および保管する暗号化プロバイダです。キーがTEEから外に出ることはありません: 暗号化操作は、オペレーティングシステムからもアクセス不可能な保護された領域内で実行されます。
Android 9 (API 28) より、KeystoreはStrongBox Keymasterをサポートしています — 独自のCPU、真の乱数生成器 (TRNG)、保護されたメモリを備えた専用セキュリティチップです。StrongBoxはCommon Criteria EAL 4+に準拠した認証を得ており、Androidにおける最高レベルのキーストレージセキュリティです。StrongBoxを使用するには、キー生成時にinStrongBox()フラグを明示的に指定する必要があります。
Keystoreがサポートするアルゴリズム: AES/GCM/NoPadding (256ビット)、EC (secp256r1, secp384r1)、RSA (2048–4096ビット)、HMAC-SHA256。すべてのキーはsetUserAuthenticationRequired(true)を通じて生体認証に結びつけることができます。
EncryptedSharedPreferencesはAndroidX Securityパッケージのライブラリで、SharedPreferences APIを通じて保存されたすべてのデータを自動的に暗号化します。値はAES-256 GCMキーで暗号化され、キーはAES-256 SIV (合成IV) で暗号化され、キー名に対する辞書攻撃を防ぎます。
主な暗号化キーはAndroid Keystoreに保管され、二階層保護を提供します: Keystoreがマスターキーを保護し、EncryptedSharedPreferencesがデータを保護します。一般的なデータ (トークン、設定) に対する読み書き操作ごとの暗号化パフォーマンスは5 ms未満であり、このライブラリはユーザーシナリオに適しています。
EncryptedSharedPreferencesは大容量のデータ (5 MB以上) には対応していません — その場合、SQLCipherやRoomを暗号化と組み合わせて使用してください。
SQLCipherはSQLiteの拡張で、AES-256-CBCを使用してデータベース全体をページ単位で暗号化します。データベースの各ページは、PBKDF2を通じてマスターパスワードから導出された独自のキーで暗号化されます。SQLCipherはデータサイズに応じて約5–15%のパフォーマンスオーバーヘッドを追加します。
Androidとの統合はnet.zetetic:android-database-sqlcipherライブラリを通じて行われ、標準のSQLiteOpenHelperと互換性のあるAPIを提供します。SQLCipherのパスワードは、コードやSharedPreferencesではなく、Keystoreに保管することを推奨します。
iOSプラットフォームは、主なセキュアストレージとしてKeychain Servicesを、またOSレベルでのファイル暗号化のためにData Protection APIを提供しています。
Keychainは、iOSがパスワード、暗号化キー、証明書、ノートを保管する暗号化されたSQLiteデータベースです。各Keychainアイテム (SecItem) は、デバイス固有のハードウェアキーを使用して暗号化されて保管されます。アイテムへのアクセスはACL (アクセス制御リスト) によって制御され、生体認証 (Face ID、Touch ID) やパスコードが必要になる場合があります。
Keychainは、データがアクセス可能かを決定する保護クラスをサポートしています: kSecAttrAccessibleWhenUnlockedThisDeviceOnly — デバイスがアンロックされている場合のみデータにアクセス可能で、バックアップ中に転送されません。このクラスは、多くの認証トークン保管シナリオに推奨されます。
iOS 15+では、Security frameworkがSecure Enclaveを通じてハードウェアキーサポートを提供しています — 暗号化操作を処理し、秘密キーをアイソレートされたメモリに保管する専用Appleプロセッサです。Secure Enclaveは、チップから抽出不可能なキーを生成するためにECDSA (secp256r1) およびECDHアルゴリズムをサポートしています。
Data Protectionは、デバイスのパスコードに結びついたキーを使用してファイルシステムレベル (APFS) で各ファイルを暗号化するiOSの機構です。開発者は、ファイル作成時にNSFileProtectionType属性を通じて保護レベルを指定します: NSFileProtectionComplete — デバイスがアンロックされている場合のみファイルにアクセス可能。
Data Protectionは、パスコードが設定されていれば、iOS 5+を実行しているすべてのデバイスで自動的に動作します。暗号化はAppleのDedicated AES Engineを通じてハードウェアレベルで実行され、高いパフォーマンスを確保します — 暗号化の遅延はユーザーにはほとんど感知されません。アプリケーションで保護を有効にするには、FileManagerを通じてファイル作成時に保護属性を設定すればそれで十分です。
Data Protectionは、キーを保管するKeychainを置き換えるわけではありません — ファイル、Core Dataデータベース、その他の大容量データを暗号化するために使用されます。Keychain (キー用) とData Protection (ファイル用) の組み合わせにより、iOS上で完全なセキュアストレージサイクルが実現されます。
AndroidおよびiOSの組み込みAPIを使用したセキュアストレージの実践的な例を見てみましょう。
この例は、Android Keystoreからのマスターキーを使用したEncryptedSharedPreferencesの初期化を示しています。その後のすべての読み書き操作は自動的に暗号化および復号化されます。
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val prefs = EncryptedSharedPreferences.create(
context,
"secure_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
prefs.edit()
.putString("auth_token", "eyJhbGciOiJIUzI1NiJ9...")
.apply()
この例は、Security frameworkを使用してiOS Keychainへのデータ保存と読み込みを示しています。コードは最大限の保護のためにkSecAttrAccessibleWhenUnlockedThisDeviceOnlyを使用しています。
import Security
func saveToKeychain(key: String, data: Data) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecValueData as String: data,
kSecAttrAccessible as String:
kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemDelete(query as CFDictionary)
SecItemAdd(query as CFDictionary, nil)
}
func readFromKeychain(key: String) -> Data? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(
query as CFDictionary, &result
)
return status == errSecSuccess ? result as? Data : nil
}
Android Keystoreに保管されたパスワードを使用してSQLCipherを通じて暗号化SQLiteデータベースに接続する例。
import net.sqlcipher.database.SQLiteDatabase
import net.sqlcipher.database.SQLiteOpenHelper
class SecureDBHelper(context: Context) :
SQLiteOpenHelper(context, "secure.db", null, 1) {
private val password = getKeyFromKeystore()
override fun onCreate(db: SQLiteDatabase) {
db.execSQL("CREATE TABLE tokens (id INTEGER PRIMARY KEY, value TEXT)")
}
override fun onUpgrade(
db: SQLiteDatabase, oldVersion: Int, newVersion: Int
) {
onCreate(db)
}
}
// 使い方: 開くときにパスワードを渡す
val helper = SecureDBHelper(context)
val db = helper.getWritableDatabase(password)
セキュアストレージを適切に使用するには、開発者の一般的な誤りを防ぐいくつかの基本原則を守る必要があります。
データの分類を定義します: どのデータにハードウェア保護が必要か (Keystore / Secure Enclave)、どのデータにOSレベルの暗号化が必要か (EncryptedSharedPreferences / Data Protection)、どのデータを一般的なファイルシステムに保管できるか。認証トークン、秘密キー、決済データ — ハードウェアレベルのみ。ユーザー設定 (テーマ、言語) — EncryptedSharedPreferencesで十分。セッションデータ (一時キャッシュ) はメモリや一時ディレクトリに保管できます。
機密情報をコード内に保管しないでください: ソースコード内のAPIキー、パスワード、シードフレーズは、重大なセキュリティ誤りです。いかなる逆エンジニアリングによっても、これらのデータは即刻に暴露します。キーにはKeystoreを使用し、設定にはアプリ起動時のサーバーサイド読み込み (リモートコンフィグ) を使用してください。
重要な操作には生体認証バインディングを使用します: Android KeystoreとiOS Keychainは、キーを生体認証に結びつけることができます。キーにアクセスするたびに、システムはFace ID、Touch ID、またはAndroidの生体認証 (BiometricPrompt) を要求します。これにより、デバイスを完全に制御されていても、攻撃者がオーナーなしに保管データを使用できないことが保証されます。
セキュリティをテストします: セキュリティ解析ツールを使用します — 静的解析にはMobSF (Mobile Security Framework)、ランタイムテストにはobjection、保護バイパスにはFridaを使用します。ルーティングやジェイルブレイク後にデータがアクセス不可能であることを確認してください。AndroidはSafetyNet AttestationやPlay Integrity APIを通じてルートアクセスを検査でき、iOSはSecure Enclaveの完全性検証を通じて検査できます。
暗号化ライブラリを定期的に更新します: 暗号化ライブラリの脆弱性は定期的に発見されます。AndroidX Security、SQLCipher、KeychainラッパーのCVEを監視してください。DependabotやRenovateを通じて、新しいバージョンの自動通知システムを導入します。
Apple Security Research (2025)によると、セキュアストレージの適切な実装により、デバイスからのデータ盗みを目的とした攻撃の96%を防げます。残り4%は、物理アクセスやゼロデイ攻撃であり、生体認証バインディングが効果的です。
よくある質問
iOS Keychainは、ACLによるアクセス制御を伴う、パスワード、キー、証明書を保管する暗号化データベースです。Android Keystoreは、アイソレート環境 (TEE/StrongBox) でキーを生成および保管し、秘密キーの抽出を許さない暗号化プロバイダです。
EncryptedSharedPreferencesは、値の暗号化にAES-256 GCM、キーの暗号化にAES-256 SIVを使用しています。マスターキーはAndroid Keystoreに保管され、二階層保護を提供します。さらに、完全性検証にHMAC-SHA256が使用されます。
はい、HTTPSは送信チャンネルのみでデータを保護します。デバイス上では、復号後にデータは明文で保管されます。攻撃者がデバイスに物理アクセスしたり、マルウェアをインストールしたりすると、HTTPSは保管データを保護できません。常にストレージレベルでデータを暗号化してください。
setUnlockedDeviceRequired(true)フラグを使用したAndroid Keystoreを使用します。これにより、ルートされたデバイスでのキーアクセスがブロックされます。さらに、Play Integrity APIを通じて完全性を検証し、参照値から過色があった場合は、ストレージからすべての機密情報を削除します。
いいえ、UserDefaultsはサンドボックス内のplistファイルにデータを明文で保管します。逆エンジニアリングツールを持ついかなるアプリケーションでも (バックアップやジェイルブレイクを通じて) トークンを読むことができます。iOSで機密情報を保管するだけのセキュアな場所はKeychainだけです。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。