설명
kSecAttrAccessibleAlways 계열 접근성은 기기가 잠겨 있어도 Keychain 항목 접근을 허용해 분실, 탈옥, 백업 복원 상황에서 민감 정보 노출 위험을 높입니다.
잠재적 영향
- 잠금 상태에서 Keychain 항목 접근
- 백업 복원 또는 기기 분실 시 민감 정보 노출
- 토큰과 비밀번호 재사용 공격
해결 방법
- 민감 항목에는
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly를 우선 사용합니다. - 백그라운드 접근이 필요한 경우에도
AfterFirstUnlockThisDeviceOnly처럼 더 제한적인 접근성을 선택합니다. - Keychain 항목은 가능한 한 이 기기 전용 접근성으로 설정해 백업 이동을 제한합니다.
예시
변경 전
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 결과를 확인합니다. 기존 항목은 같은 강한 접근성을 유지하며 갱신하고, 패스코드 미설정 등으로 작업이 실패해도 더 약한 접근성으로 폴백하지 않습니다.