Swiftのローカルデータベースへの機密情報の平文保存

Swiftのローカルデータベースへの機密情報の平文保存

説明

パスワード、トークン、APIキーなどの機密情報をCore DataやRealmのローカルデータベースに平文で保存すると、バックアップ、デバッグによる抽出、脱獄された端末などを通じて漏えいするおそれがあります。

想定される影響

  • 認証トークンやセッションの窃取。
  • ローカルバックアップやデバッグによる機密情報の漏えい。
  • 窃取した値の再利用によるアカウントへのアクセス。

対処方法

  1. 可能な限り、機密情報をローカルデータベースに保存しないでください。
  2. 保存が必要な場合は、Keychain、SQLCipher、暗号化キーを設定したRealmなど、実績のある安全な保存方法を使用してください。
  3. データベースへの保存前に値を暗号化し、鍵を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などの独立した安全な保存先で管理してください。

参考資料