説明
パスワード、トークン、APIキーなどの機密情報をCore DataやRealmのローカルデータベースに平文で保存すると、バックアップ、デバッグによる抽出、脱獄された端末などを通じて漏えいするおそれがあります。
想定される影響
- 認証トークンやセッションの窃取。
- ローカルバックアップやデバッグによる機密情報の漏えい。
- 窃取した値の再利用によるアカウントへのアクセス。
対処方法
- 可能な限り、機密情報をローカルデータベースに保存しないでください。
- 保存が必要な場合は、Keychain、SQLCipher、暗号化キーを設定したRealmなど、実績のある安全な保存方法を使用してください。
- データベースへの保存前に値を暗号化し、鍵をKeychainで管理してください。
例
変更後の例は、Core Dataの access_token 属性がBinary Data型であることを前提としています。鍵の取得とコンテキストの保存処理は省略しています。
変更前
swift
let user = NSEntityDescription.insertNewObject(forEntityName: "User", into: context)
user.setValue(accessToken, forKey: "access_token")
変更後
swift
import CoreData
import CryptoKit
enum StorageEncryptionError: Error {
case missingCombinedRepresentation
}
func storeToken(
context: NSManagedObjectContext,
accessToken: Data,
databaseKey: SymmetricKey
) throws {
let sealedBox = try AES.GCM.seal(accessToken, using: databaseKey)
guard let encryptedToken = sealedBox.combined else {
throw StorageEncryptionError.missingCombinedRepresentation
}
let user = NSEntityDescription.insertNewObject(
forEntityName: "User", into: context)
user.setValue(encryptedToken, forKey: "access_token")
}
解説:
- 変更前: 認証トークンをローカルデータベースに平文で保存しています。
- 変更後: CryptoKitのAES-GCMによる認証付き暗号化の結合表現だけを保存します。
databaseKeyはデータベースやソースコードではなく、Keychainなどの独立した安全な保存先で管理してください。