iOS 앱 보안 강화를 위한 Keychain 개요
iOS 개발자들은 다양한 정보를 관리하는 앱을 만들 때, 사용자 데이터 보호를 위한 보안 강화가 필수적이라고 인식하고 있습니다. 사용자 비밀번호, 로그인 정보, 인증서, 토큰 등 중요한 정보들은 앱 내부에서 안전하게 보호되어야 합니다. 이를 위해 iOS에서는 Keychain이라는 보안 메커니즘이 제공됩니다.
Keychain은 iOS에서 기본적으로 제공하는 고급 보안 프레임워크입니다. 앱에서 사용자 데이터를 안전하게 저장하고 관리하는 방법을 제공합니다. Keychain은 보안을 위해 암호화를 사용하며, iOS에서는 앱마다 별도로 분리되어 관리됩니다. 이는 앱 내부에서 안전하게 데이터를 공유할 수 있도록 합니다.
이번 글에서는 Keychain을 이용한 iOS 앱 보안 강화 방법에 대해 알아보겠습니다.
Keychain API를 사용한 iOS 보안 기능 구현
iOS에서 Keychain API는 C 언어로 작성되어 있습니다. 이를 이용해 Objective-C나 Swift에서 Keychain을 사용할 수 있습니다. Keychain API는 다음과 같은 기능을 제공합니다.
- 데이터 저장 및 검색
- 데이터 수정 및 삭제
- 키 생성 및 삭제
- 인증서 및 비밀번호 관리
- 보호 클래스 및 엑세스 제한 설정
이 중에서 데이터 저장 및 검색, 데이터 수정 및 삭제, 그리고 키 생성 및 삭제에 대해 살펴보겠습니다.
데이터 저장 및 검색
Keychain에서 데이터를 저장하고 검색하는 방법은 두 가지가 있습니다. 첫 번째는 Generic Password를 이용하는 것이고, 두 번째는 Internet Password를 이용하는 것입니다. Generic Password는 사용자 이름과 비밀번호와 같은 일반적인 비밀번호를 저장하는데 사용됩니다. Internet Password는 웹사이트나 앱에서 사용하는 비밀번호를 저장하는데 사용됩니다.
먼저 Generic Password를 이용해 데이터를 저장하는 방법을 살펴보겠습니다. 다음은 Keychain에 데이터를 저장하는 예제 코드입니다.
func save(key: String, data: Data) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecValueData as String: data
]
let status = SecItemAdd(query as CFDictionary, nil)
return status == errSecSuccess
}
위 코드에서는 kSecClassGenericPassword를 이용해 Generic Password를 설정합니다. kSecAttrAccount는 데이터를 저장할 때 사용할 키를 지정합니다. kSecValueData는 실제 데이터를 저장합니다.
다음은 Generic Password를 이용해 데이터를 검색하는 예제 코드입니다.
func load(key: String) -> Data? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnData as String: true
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
if status == errSecSuccess {
return result as? Data
}
return nil
}
이 코드에서는 kSecClassGenericPassword를 이용해 Generic Password를 설정합니다. kSecAttrAccount는 검색할 데이터의 키를 지정합니다. kSecMatchLimit는 검색 결과의 개수를 지정합니다. kSecReturnData는 검색 결과로 데이터를 반환합니다.
다음은 Internet Password를 이용해 데이터를 저장하는 예제 코드입니다.
func save(server: String, account: String, password: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassInternetPassword,
kSecAttrServer as String: server,
kSecAttrAccount as String: account,
kSecValueData as String: password.data(using: .utf8)!
]
let status = SecItemAdd(query as CFDictionary, nil)
return status == errSecSuccess
}
위 코드에서는 kSecClassInternetPassword를 이용해 Internet Password를 설정합니다. kSecAttrServer와 kSecAttrAccount는 데이터를 저장할 때 사용할 서버 이름과 계정 이름을 지정합니다. kSecValueData는 실제 데이터를 저장합니다.
다음은 Internet Password를 이용해 데이터를 검색하는 예제 코드입니다.
func load(server: String, account: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassInternetPassword,
kSecAttrServer as String: server,
kSecAttrAccount as String: account,
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnData as String: true
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
if status == errSecSuccess {
if let data = result as? Data {
return String(data: data, encoding: .utf8)
}
}
return nil
}
이 코드에서는 kSecClassInternetPassword를 이용해 Internet Password를 설정합니다. kSecAttrServer와 kSecAttrAccount는 검색할 데이터의 서버 이름과 계정 이름을 지정합니다. kSecMatchLimit는 검색 결과의 개수를 지정합니다. kSecReturnData는 검색 결과로 데이터를 반환합니다.
데이터 수정 및 삭제
Keychain에서 데이터를 수정하거나 삭제하는 방법은 데이터를 검색한 후, 해당 데이터를 수정하거나 삭제하는 것입니다. 다음은 Generic Password를 이용해 데이터를 수정하는 예제 코드입니다.
func update(key: String, data: Data) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key
]
let attributes: [String: Any] = [
kSecValueData as String: data
]
let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
return status == errSecSuccess
}
이 코드에서는 먼저 데이터를 검색한 후, kSecValueData를 이용해 데이터를 수정합니다.
다음은 Generic Password를 이용해 데이터를 삭제하는 예제 코드입니다.
func delete(key: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key
]
let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess
}
이 코드에서는 먼저 데이터를 검색한 후, SecItemDelete를 이용해 데이터를 삭제합니다.
키 생성 및 삭제
Keychain에서는 키를 생성하고 삭제하는 방법도 제공합니다. 다음은 키를 생성하는 예제 코드입니다.
func generateKey() -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
kSecAttrLabel as String: "My Key"
]
var result: AnyObject?
let status = SecItemAdd(query as CFDictionary, &result)
if status == errSecSuccess, let keyData = result as? Data {
return String(data: keyData, encoding: .utf8)
}
return nil
}
위 코드에서는 kSecClassGenericPassword를 이용해 키를 생성합니다. kSecAttrAccessible는 키를 저장할 수 있는 보호 클래스를 지정합니다. kSecAttrLabel은 키 이름을 지정합니다.
다음은 키를 삭제하는 예제 코드입니다.
func deleteKey(key: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key
]
let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess
}
위 코드에서는 kSecClassGenericPassword를 이용해 키를 삭제합니다. kSecAttrAccount는 삭제할 키를 지정합니다.
iOS Keychain 서비스와 엑세스 그룹 기능 활용
iOS에서는 Keychain 서비스와 엑세스 그룹 기능을 이용해 앱 간 데이터 공유를 할 수 있습니다. 이를 이용해 여러 앱에서 공통으로 사용하는 데이터를 관리할 수 있습니다.
먼저 Keychain 서비스를 이용하는 방법에 대해 알아보겠습니다. Keychain 서비스는 동일한 앱 내에서 공유되는 데이터를 관리하는 데 사용됩니다. 다음은 Keychain 서비스를 이용해 데이터를 저장하는 예제 코드입니다.
let serviceName = "com.example.myapp"
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: "myaccount",
kSecValueData as String: "mypassword".data(using: .utf8)!
]
let status = SecItemAdd(query as CFDictionary, nil)
위 코드에서는 kSecAttrService를 이용해 서비스 이름을 지정합니다. 이렇게 지정된 서비스는 동일한 앱에서 공유됩니다.
다음은 Keychain 서비스를 이용해 데이터를 검색하는 예제 코드입니다.
let serviceName = "com.example.myapp"
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: serviceName,
kSecAttrAccount as String: "myaccount",
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnData as String: true
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
if status == errSecSuccess, let data = result as? Data {
let password = String(data: data, encoding: .utf8)
}
이 코드에서는 위와 마찬가지로 kSecAttrService를 이용해 서비스 이름을 지정합니다.
다음은 엑세스 그룹 기능을 이용하는 방법에 대해 알아보겠습니다. 앱에서 엑세스 그룹을 지정하면 동일한 그룹에 속한 앱끼리 데이터 공유가 가능합니다. 다음은 엑세스 그룹을 지정해 데이터를 저장하는 예제 코드입니다.
let accessGroup = "ABCDEFG123.com.example.myapp"
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "myaccount",
kSecValueData as String: "mypassword".data(using: .utf8)!,
kSecAttrAccessGroup as String: accessGroup
]
let status = SecItemAdd(query as CFDictionary, nil)
위 코드에서는 kSecAttrAccessGroup을 이용해 엑세스 그룹을 지정합니다.
다음은 엑세스 그룹을 이용해 데이터를 검색하는 예제 코드입니다.
let accessGroup = "ABCDEFG123.com.example.myapp"
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "myaccount",
kSecMatchLimit as String: kSecMatchLimitOne,
kSecReturnData as String: true,
kSecAttrAccessGroup as String: accessGroup
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
if status == errSecSuccess, let data = result as? Data {
let password = String(data: data, encoding: .utf8)
}
이 코드에서도 위와 마찬가지로 kSecAttrAccessGroup을 이용해 엑세스 그룹을 지정합니다.
iOS Keychain 보안 모범 사례 및 주의점
Keychain은 iOS에서 제공하는 고급 보안 기능 중 하나입니다. 앱에서 중요한 정보를 안전하게 저장하고 관리하기 위해서는 Keychain을 이용해야 합니다. 이번에는 Keychain을 이용할 때 주의해야 할 사항들에 대해 알아보겠습니다.
보호 클래스 설정
Keychain에서 데이터를 저장할 때는 보호 클래스를 지정해야 합니다. 보호 클래스는 데이터를 어떻게 보호할 것인지를 결정하는데, 보호 클래스가 높을수록 보안이 강화됩니다. iOS에서는 다음과 같은 보호 클래스를 제공합니다.
- kSecAttrAccessibleWhenUnlocked: 잠금 해제된 상태에서만 접근 가능
- kSecAttrAccessibleAfterFirstUnlock: 처음 잠금 해제 이후에만 접근 가능
- kSecAttrAccessibleWhenUnlockedThisDeviceOnly: 같은 기기에서만 접근 가능
- kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly: 같은 기기에서만 접근 가능
- kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly: 암호 설정이 되어 있고, 같은 기기에서만 접근 가능
보호 클래스는 앱의 보안 요구 사항에 맞게 설정해야 합니다. 예를 들어, 사용자가 로그인한 후에만 데이터에 접근해야 한다면 kSecAttrAccessibleWhenUnlocked을 사용하면 됩니다.
엑세스 제한 설정
Keychain에서 데이터를 저장할 때는 엑세스 제한도 설정할 수 있습니다. 엑세스 제한을 설정하면 특정한 조건에서만 데이터에 접근이 가능해집니다. iOS에서는 다음과 같은 엑세스 제한을 제공합니다.
- kSecAttrAccessControl: 추가적인 엑세스 제한을 설정할 수 있는 객체
- kSecAttrAccessGroup: 같은 엑세스 그룹에 속한 앱끼리 데이터 공유 가능
엑세스 제한은 보호 클래스보다 더 강력한 보안 기능을 제공합니다. 암호를 입력해야만 데이터에 접근할 수 있도록 설정할 수도 있습니다.
키 체인 접근 권한 설정
Keychain에서 데이터를 저장할 때는 해당 데이터에 접근할 수 있는 앱을 설정할 수 있습니다. iOS에서는 다음과 같은 키 체인 접근 권한을 제공합니다.
- kSecAttrAccessGroup: 같은 엑세스 그룹에 속한 앱