KeyChain & 암호화

2023. 10. 8. 20:08·면접준비

UserDefaults의 치명적 보안 약점

iOS개발자로서 데이터를 안전하게 관리하는 것은 필수적인 역량입니다. UserDefault는 설정같이 가벼운 데이터를 저장하는데 적합했지만 비밀번호같은 민감한 데이터를 저장하면 보안 위협이 큽니다.

왜UserDefault는 위험할까?

UserDefault는 key -value 형태로 데이터를 저장하여 plist 파일로 즉 샌드백스 내에 존재합니다.

즉 앱이 삭제되면 데이터도 사라지는데 .UserDefaults는 암호화되지 않아있음. 
예를들어 탈옥된 디바이스에서 중요 정보를 빼갈 수 있는거져 

// 이렇게 저장하면...
UserDefaults.standard.set("super_secret_token_12345", forKey: "auth_token")

실제로는 이런 곳에 평문으로 저장됩니다:

# iOS Simulator 기준
~/Library/Developer/CoreSimulator/Devices/[UUID]/data/Containers/Data/Application/[UUID]/Library/Preferences/com.yourapp.bundleid.plist

# 실제 디바이스 (탈옥 시)
/var/mobile/Containers/Data/Application/[UUID]/Library/Preferences/

그래서 UserDefault는 속도와 편의성 위한 세팅들만 저장해야합니다.

KeyChain이 필수인 이유

apple이 제공하는 보안프레임워크. 디바이스 안에 암호화된 데이터 저장공간.

로그인 및 암호(해시), 결제데이터, 등 비밀 유지하고 싶은것을 저장.

 

  1. 영속성: 사용자가 직접 제거하지 않는 이상, 앱을 제거하고 설치해도 데이터는 유지된다.
  2. device lock하면 keychain도 잠기고, 디바이스 unlock하면 풀림.
    • 잠긴상태에서는 item에 접근, 복호화 할수 없다.
    • 풀린상태에서도 해당 아이템을 생성하고 저장한 어플리케이션에서만 접근이 가능.

keychain은 하나의 암호화된 컨테이너로 디바이스 내에서 안전하게 데이터에 보호하며 샌드박스 밖에 위치해 앱 삭제에도 영향을 받지 않는다.

 

KeyChain 동작 원리와 암호화 알고리즘

KeyChain은 AES - 256를 사용해 데이터 암호화한다. 대칭키 암호화 알고리즘. 암호화 키는 Secure Enclave라는 하드웨어 칩에 저장.

KeyChain 데이터는 디바이스의 보안파티션에 저장된다. 이 파일은 당연히 암호화되어있음.

앱이 keyChain에 데이터를 저장하려면 KeyChain Item으로 패키징 -> Secure Enclave에서 AES-256키를 생성하고 데이터 암호화

-> 암호화된 데이터는 KeyChain 데이터베이스에 저장(/private/var/Keychains/keychain-2.db)

-> 디바이스 잠금 시 Secure Enclave가 키를 잠가 복호화 불가.

-> 잠금 해제시 앱이 인증을 거쳐 복호화된 데이터에 접근

Secure Enclave?
이건 좀 어렵고 생소한 개념이엿다. 애플 디바이스에 내장된 하드웨어 기반 보안 프로세서이다. SoC니까 칩에 포함되어있다. FaceID 등 알고리즘 계산법도 여기있다고 한다.
AES-256?
단방향 암호화 알고리즘: 평문을 암호화했을 때 다시 평문으로 되돌리는(복호화) 할수 없는 암호화 방식. SHA-256
양방향 암호화 알고리즘: 복호화가 가능한 암호화 방식. AES-256
AES: 암호화 및 복호화에 동일한 키를 사용하는 대칭키 알고리즘으로 높은 안전성과 빠른 속도로 인해 양방향 암호화에서 가장 많이 사용되는 방식이다. 256이 의미하는것은 키의 길이. 256 bit = 32byte
어 그러니까.. 같은 키라는 것은 클라와 백엔드가 합의한 같은 키를 사용한다는거!

그러면 왜 KeyChain은 SHA나 RSA기법이 아닌 AES기법을 사용했을까?

대칭키기법이 속도와 대량 데이터 효율이 좋다. 왜? 비대칭은 공개키 비밀키 계산을 해야하는데 그게 아니므로

또한 비대칭키 강점이 키교환과 서명으로 인한 보안인데 키체인은 내부에서만 동작하므로 필요가 없는 오버헤드다.

KeyChain의 구성요소 3가지
1. KeyChain: 키체인에 저장되는 데이터, 여러개의 키체인 item을 가질 수 있다.

2. ItemClass: 키체인 item에 저장되는 데이터 종류를 지정할수 있다.

  • kSecClassGenericPassword : 일반 암호를 저장할 때 사용.
    • kSecAttrService: 키체인 아이템과 연관되어 있는 서비스의 이름
    • kSecAttrAccount: 저장할 아이템의 계정 이름 (아이디)
    • kSecAttrGeneric: 저장할 아이템의 데이터 (비밀번호)
  •  kSecClassInternetPassword : 인터넷에서 불러온 암호를 저장할 때 사용
    • kSecAttrAccount: 저장할 아이템의 계정 이름 (아이디)
  • kSecClassCertificate: 인증서를 저장할때 사용
  • kSecClassKey: 암호화 키항목을 저장할 때 사용
  • kSecClassIdentity: 아이디 항목을 저장할 때 사용

3. Attribute: item class에 대한 속성 저장.

what is attribute? 데이터에 접근하거나 검색하는 것을 가능하게 한다. 직접적으로 접근하게 하는 것이 아닌 접근가능성을 보여주는 것,

item Class에 따라 설정할수 있는 attributes가 달라진다.


실습을 해보자!!!~~~~!!!

1. Create

   private func create(key: String, token: String) -> Bool{
        let query: NSDictionary = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrAccount: key, //저장할 아이템의 계정이름
            kSecValueData: token.data(using: .utf8, allowLossyConversion: false) as Any // 저장할 토큰
        ]
        SecItemDelete(query) // keychain key값이 겹치면 저장할수 없기에 삭제.
        return SecItemAdd(query as CFDictionary, nil) == errSecSuccess
    }

2. read

private func read(key:String) -> String? {
        let query: NSDictionary = [
            kSecClass: kSecClassGenericPassword,
            kSecAttrAccount: key,
            kSecReturnData: kCFBooleanTrue as Any, //CFType으로 불러와라
            kSecMatchLimit: kSecMatchLimitOne // 중복되는 경우 하나만 가져온다.
        ]
        var dataTypeRef: AnyObject?
        let status = SecItemCopyMatching(query, &dataTypeRef) //키체인 아이템을 검색.
        if status == errSecSuccess {
            let data = dataTypeRef as! Data
            let value = String(data: data, encoding: String.Encoding.utf8)
            return value
        } else{
            return nil
        }
    }

3. delete

    private func delete(key: String) -> Bool{
        let query: NSDictionary = [
            kSecClass: kSecClassInternetPassword,
            kSecAttrAccount: key
        ]
        return SecItemDelete(query) == errSecSuccess
    }

4. update

   private func update(key: String, data: Any) -> Bool {
        let previousQuery: NSDictionary = [
            kSecClass: kSecClassInternetPassword,
            kSecAttrAccount: key
        ]
        let updateQuery: NSDictionary = [kSecValueData: data]
        return SecItemUpdate(previousQuery, updateQuery) == errSecSuccess
    }

'면접준비' 카테고리의 다른 글

Assertions && Preconditions  (2) 2024.03.02
LLVM? Swift가 컴파일되는 과정 Swift 기초  (2) 2024.03.01
TaskGroup  (0) 2023.08.13
Swift Concurrency - Async/Await⭐️  (0) 2023.08.08
디스패치 그룹  (0) 2023.07.02
'면접준비' 카테고리의 다른 글
  • Assertions && Preconditions
  • LLVM? Swift가 컴파일되는 과정 Swift 기초
  • TaskGroup
  • Swift Concurrency - Async/Await⭐️
2료일
2료일
좌충우돌 모든것을 다 정리하려고 노력하는 J가 되려고 하는 세미개발자의 블로그입니다. 편하게 보고 가세요
  • 2료일
    GPT에게서 살아남기
    2료일
  • 전체
    오늘
    어제
    • 분류 전체보기 (120)
      • SWIFT개발 (29)
      • 알고리즘 (25)
      • Design (6)
      • ARkit (1)
      • 면접준비 (30)
      • UIkit (2)
      • Vapor-Server with swift (3)
      • 디자인패턴 (5)
      • 반응형프로그래밍 (12)
      • CS (3)
      • 도서관 (1)
  • 인기 글

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
2료일
KeyChain & 암호화
상단으로

티스토리툴바