説明
kSecAttrAccessibleAlways 系の設定は、端末がロックされていてもKeychain項目へのアクセスを許可します。そのため、端末の紛失、脱獄、バックアップからの復元などの際に、機密情報が露出するリスクが高まります。
想定される影響
- 端末のロック中のKeychain項目へのアクセス
- 端末の紛失やバックアップの復元に伴う機密情報の漏えい
- 盗まれたトークンやパスワードの再利用
対処方法
- 機密性のある項目には、
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnlyを優先します。 - バックグラウンドでのアクセスが必要な場合も、
AfterFirstUnlockThisDeviceOnlyなど、より制限の厳しい設定を選びます。 - 可能なら端末限定のアクセス条件を使い、バックアップによる別端末への移行を防ぎます。
例
変更前
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の処理結果を確認します。既存の項目も同じ強いアクセス条件で更新し、パスコード未設定などで失敗しても、弱い条件に切り替えません。