Swiftの弱いKeychainアクセス条件

Swiftの弱いKeychainアクセス条件

説明

kSecAttrAccessibleAlways 系の設定は、端末がロックされていてもKeychain項目へのアクセスを許可します。そのため、端末の紛失、脱獄、バックアップからの復元などの際に、機密情報が露出するリスクが高まります。

想定される影響

  • 端末のロック中のKeychain項目へのアクセス
  • 端末の紛失やバックアップの復元に伴う機密情報の漏えい
  • 盗まれたトークンやパスワードの再利用

対処方法

  1. 機密性のある項目には、kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly を優先します。
  2. バックグラウンドでのアクセスが必要な場合も、AfterFirstUnlockThisDeviceOnly など、より制限の厳しい設定を選びます。
  3. 可能なら端末限定のアクセス条件を使い、バックアップによる別端末への移行を防ぎます。

例

変更前

swift
query[kSecAttrAccessible as String] = kSecAttrAccessibleAlways

変更後

swift
import Foundation
import Security

enum KeychainStorageError: Error {
    case unexpectedStatus(OSStatus)
}

func saveToken(_ token: Data) throws {
    let item: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrService as String: "com.example.app.auth",
        kSecAttrAccount as String: "access_token",
        kSecAttrAccessible as String:
            kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
        kSecValueData as String: token
    ]

    let addStatus = SecItemAdd(item as CFDictionary, nil)
    if addStatus == errSecSuccess { return }
    guard addStatus == errSecDuplicateItem else {
        throw KeychainStorageError.unexpectedStatus(addStatus)
    }

    let match: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrService as String: "com.example.app.auth",
        kSecAttrAccount as String: "access_token"
    ]
    let updates: [String: Any] = [
        kSecAttrAccessible as String:
            kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly,
        kSecValueData as String: token
    ]
    let updateStatus = SecItemUpdate(
        match as CFDictionary, updates as CFDictionary)
    guard updateStatus == errSecSuccess else {
        throw KeychainStorageError.unexpectedStatus(updateStatus)
    }
}

説明:

  • 変更前: 端末のロック状態に関係なく、項目へのアクセスを許可します。
  • 変更後: パスコードと端末限定の保護を要求し、Keychainの処理結果を確認します。既存の項目も同じ強いアクセス条件で更新し、パスコード未設定などで失敗しても、弱い条件に切り替えません。

参考資料