[iOS] API 키 보안 적용하기

iosSwift
avatar
2025.02.13
·
3 min read

현재 진행 중인 프로젝트에 Google Books API를 사용하고 있다. 커밋을 하려다 API 키가 하드코딩 되었다는 사실에 부랴부랴 찾아본 API 키에 보안을 적용하는 방법을 정리한다.

우선, 내가 알아본 방법은 아래 세 가지다.

  1. Info.plist에 API 키 저장 후 가져오기

  2. .xcconfig 파일을 사용해 환경 변수로 관리

  3. KeyChain 또는 서버에서 API 키를 받아오기

나는 Info.plist를 사용했지만, 혹시 몰라 위 세 가지 방법에 대해 정리한다.

.xcconfig 파일을 사용하는 방법이 더 안전하다고 판단되어 변경했다.

Info.plist

  1. Xcode - 프로젝트 폴더 - Info.plist 클릭

  2. 아래 내용 추가

    • Key: 식별자(ex. GOOGLE_BOOKS_API_KEY)

    • Type: String

    • Value: API Key

  3. API 키를 불러오는 함수 추가

        private var apiKey: String {
            guard let key = Bundle.main.object(forInfoDictionaryKey: "식별자") as? String else {
                fatalError("Not found API key.")
            }
            return key
        }

.xcconfig

  1. Config.xcconfig 파일 생성 및 설정

    • Config.xcconfig에 API_KEY = your_debug_api_key 입력

  2. 프로젝트에 .xcconfig 연결

    • Project - Info - Configurations

    • DebugRelease 중 원하는 곳에 Config.xcconfig 파일 지정

  3. Info.plist에 환경 변수 추가

    1. Key: API_KEY

    2. Type: String

    3. Value: $(API_KEY)

  4. 테스트

    print(Bundle.main.object(forInfoDictionaryKey: "API_KEY") as? String ?? "No Key")

Keychain

import Security

func saveAPIKeyToKeychain(apiKey: String) {
    let keychainQuery: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrService as String: "Bundle Identifier",
        kSecAttrAccount as String: "식별자",
        kSecValueData as String: apiKey.data(using: .utf8)!
    ]
    SecItemAdd(keychainQuery as CFDictionary, nil)
}

func getAPIKeyFromKeychain() -> String? {
    let keychainQuery: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrService as String: "Bundle Identifier",
        kSecAttrAccount as String: "식별자",
        kSecReturnData as String: kCFBooleanTrue!,
        kSecMatchLimit as String: kSecMatchLimitOne
    ]
    
    var dataTypeRef: AnyObject?
    let status = SecItemCopyMatching(keychainQuery as CFDictionary, &dataTypeRef)
    
    if status == errSecSuccess, let retrievedData = dataTypeRef as? Data {
        return String(data: retrievedData, encoding: .utf8)
    }
    return nil
}






- 컬렉션 아티클