Swift 취약한 Keychain 접근성 설정

Swift 취약한 Keychain 접근성 설정

설명

kSecAttrAccessibleAlways 계열 접근성은 기기가 잠겨 있어도 Keychain 항목 접근을 허용해 분실, 탈옥, 백업 복원 상황에서 민감 정보 노출 위험을 높입니다.

잠재적 영향

  • 잠금 상태에서 Keychain 항목 접근
  • 백업 복원 또는 기기 분실 시 민감 정보 노출
  • 토큰과 비밀번호 재사용 공격

해결 방법

  1. 민감 항목에는 kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly를 우선 사용합니다.
  2. 백그라운드 접근이 필요한 경우에도 AfterFirstUnlockThisDeviceOnly처럼 더 제한적인 접근성을 선택합니다.
  3. 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 결과를 확인합니다. 기존 항목은 같은 강한 접근성을 유지하며 갱신하고, 패스코드 미설정 등으로 작업이 실패해도 더 약한 접근성으로 폴백하지 않습니다.

참조