Swift: Data Protection Class

틀틀보·2025년 9월 1일

Swift

목록 보기
13/19
post-thumbnail

앱이 생성하는 파일에 적용되는 암호화 수준을 정의하여 사용자 데이터를 안전하게 보호하는 iOS의 핵심 보안 기능

애플의 기기들은 새로운 파일이 생성되면 그 파일을 생성한 앱이 각 파일에 클래스를 할당한다.

각 클래스는 데이터 접근 시기를 결정하는 정책, 기능이 다르다.

✨ 여기서 설명하는 클래스는 프로그래밍 관점에서의 클래스가 아닌 등급, 범주의 의미!

Data Protection Class

NSFileProtectionComplete

가장 강력한 보호 수준

  • 기기가 잠금 해제된 상태에서만 파일에 접근 가능

  • 기기가 잠기면 즉시 접근이 불가능해짐.

예시: 메일 앱의 첨부파일 다운로드
  • 사용자가 메일 앱에서 대용량 첨부파일 다운로드를 시작

  • 다운로드가 진행되는 동안 사용자가 기기를 잠굼.

  • 메일 앱은 백그라운드에서 계속 실행되다가, 잠기는 순간, 파일에 대한 쓰기 권한이 막혀 중단

NSFileProtectionCompleteUnlessOpen

앱이 파일 작업을 진행하고 있는 동안만 보호

  • 기기가 잠겨 있어도 앱이 이미 파일을 열고 있었다면 계속 접근 가능

  • 새 파일을 생성하고 쓰는 것도 가능

예시: 메일 앱의 첨부파일 다운로드
  • 사용자가 메일 앱에서 대용량 첨부파일 다운로드를 시작

  • 다운로드가 진행되는 동안 사용자가 기기를 잠굼.

  • 메일 앱은 백그라운드에서 계속 실행되면서 다운로드된 데이터를 임시 파일에 계속 씀. 이때 새로운 데이터 조각을 파일에 계속 쓸 수 있음.

NSFileProtectionCompleteUntilFirstUserAuthentication

재부팅 후 첫 잠금 해제 전까지 보호

  • 사용자가 기기를 재부팅한 후 처음으로 잠금을 해제할 때까지 파일이 보호

  • 그 이후에는 기기가 잠겨도 접근 가능

  • ✨ 파일 저장 시 기본으로 적용되는 설정

NSFilePositioningNone

보호 없음 (None)

  • 파일이 암호화는 되지만, 기기의 잠금 상태와 관계없이 항상 접근 가능

적용법

FileManager

FileManager를 사용하여 파일을 생성하거나 수정할 때 보호 클래스 설정

import Foundation

let fileManager = FileManager.default
guard let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
    return
}

let sensitiveFileURL = documentsURL.appendingPathComponent("SensitiveData.txt")
let content = "This is highly sensitive information."

do {
    // NSFileProtectionComplete 수준으로 파일 생성
    try content.write(to: sensitiveFileURL, atomically: true, encoding: .utf8)
    var attributes = [FileAttributeKey: Any]()
    attributes[.protectionKey] = FileProtectionType.complete
    try fileManager.setAttributes(attributes, ofItemAtPath: sensitiveFileURL.path)
    print("민감한 파일이 성공적으로 저장되었습니다.")
} catch {
    print("파일 저장 중 오류 발생: \(error)")
}

attributes[.protectionKey] = FileProtectionType.complete
해당 부분으로 파일의 보호 클래스를 설정

파일을 생성, 덮어쓰기, 속성 변경 등의 변경에 클래스 설정 및 변경 가능

Core Data

Core Data 스택을 설정할 때 NSPersistentStoreDescription의 옵션으로 보호 수준을 지정

import CoreData

struct PersistenceController {
    static let shared = PersistenceController()

    let container: NSPersistentContainer

    init(inMemory: Bool = false) {
        container = NSPersistentContainer(name: "YourAppName")

        // ---------------------> 여기가 핵심! <---------------------
        // 1. 컨테이너의 기본 설정(Description)을 가져옴
        guard let description = container.persistentStoreDescriptions.first else {
            fatalError("Failed to retrieve a persistent store description.")
        }

        // 2. 원하는 데이터 보호 클래스를 옵션으로 설정
        // FileProtectionType.complete = NSFileProtectionComplete
        description.setOption(FileProtectionType.complete as NSObject,
                              forKey: NSPersistentStoreFileProtectionKey)
        // -----------------------------------------------------------

        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
    }
}
  • container에 저장되는 데이터베이스 파일들은 container에 설정된 보호 클래스로 관리됨.

  • 다만 한 번 설정된 container는 보호 클래스 변경이 어려우므로, 새로운 container에 마이그레이션이 필요함.

비슷한 보안 관리

Keychain을 사용한 데이터 보호

Keychain에 데이터를 저장할 때는 kSecAttrAccessible 속성을 사용하여 접근성을 제어

import Security

let account = "com.example.myApp.user"
let password = "mySecretPassword".data(using: .utf8)!

// 저장할 쿼리를 생성
let query: [String: Any] = [
    kSecClass as String: kSecClassGenericPassword,
    kSecAttrAccount as String: account,
    kSecValueData as String: password,
    // 기기가 잠금 해제되었을 때만 접근 가능하도록 설정
    kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked
]

// 키체인에 아이템 추가
let status = SecItemAdd(query as CFDictionary, nil)

if status == errSecSuccess {
    print("키체인에 안전하게 저장되었습니다.")
} else if status == errSecDuplicateItem {
    print("이미 해당 아이템이 존재합니다.")
    // 업데이트 로직을 추가할 수 있습니다.
} else {
    print("키체인 저장 오류: \(status)")
}

kSecAttrAccessible으로 보안 수준 설정 가능

  • kSecAttrAccessibleWhenUnlocked: 기기가 잠금 해제된 동안에만 접근 가능 (NSFileProtectionComplete와 유사)

  • kSecAttrAccessibleAfterFirstUnlock: 재부팅 후 첫 잠금 해제 이후부터 접근 가능 (NSFileProtectionCompleteUntilFirstUserAuthentication과 유사)

  • kSecAttrAccessibleAlways: 항상 접근 가능 (NSFileProtectionNone과 유사)

  • kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly: 암호가 설정된 이 기기에서만, 그리고 잠금 해제 시에만 접근 가능

참고
https://support.apple.com/ko-kr/guide/security/secb010e978a/web

https://developer.apple.com/documentation/uikit/encrypting-your-app-s-files

profile
안녕하세요! iOS 개발자입니다!

0개의 댓글