스트리밍은 대부분의 브라우저와
Developer 앱에서 사용할 수 있습니다.
-
로컬 승인 흐름 간소화
LocalAuthentication의 최신 승인 중심 API를 사용하여 사용자 데이터의 개인 정보 보호 및 보안을 유지하는 방법을 확인하세요. Local Authentication이 앱의 시크릿, 키 및 기타 민감한 리소스에 대한 액세스를 승인하는 동시에 복잡성을 줄이고 Touch ID 및 Face ID와 같은 일반적인 로컬 인증 방법의 보안 및 사용 용이성을 활용하는 방법을 보여드리겠습니다.
리소스
-
다운로드
♪♪ 승인 과정 간소화 안녕하세요, 소프트웨어 엔지니어 Felix Acero입니다 보안 엔지니어링과 아키텍처팀 소속이죠 이번 영상에서는 여러분께 LocalAuthentication 프레임워크를 사용해서 앱의 인증 및 승인 과정을 향상시키는 법을 보여 드릴게요 인증 및 승인에 대한 일반적인 개념부터 살펴보고 응용 프로그램에 어떻게 적용되는지 살펴볼 겁니다 그리고 기존에 있던 LocalAuthentication API와 그중에도 LAContext가 광범위한 승인 체제 구현에 어떻게 도움이 되는지 살펴보죠 그리고 마지막으로는 올해 LocalAuthentication에 추가할 새로운 API가 인증 코드를 간소화하는 데 어떻게 도움이 되는지 알아볼 겁니다
그럼 인증 및 승인에 대해 살펴보죠 인증과 승인은 다른 개념이지만 보안과 밀접하게 관련되어 있습니다 인증은 사용자의 신원을 확인하는 행위입니다 반면, 승인은 특정 사용자가 해당 사용자인지 확인하는 행위로 구체적인 리소스에 대해 특정 작업을 가능하게 하죠 종합해 보면 사용자가 어떤 리소스를 쓰고 어떤 활동을 할 수 있는지 평가하기 전에 사용자가 누구인지 확인해야 하기 때문에 인증해야 승인이 가능하다고 말할 수 있습니다 이 개념을 설명하기 위해서 Secure Enclave keys와 같은 응용 프로그램에서 관리하는 공통 보안 리소스와 관련된 구체적인 예를 살펴보죠
Secure Enclave keys는 특별한 유형의 비대칭 키로 하드웨어 기반 키 관리자에 의해 보호되고 특정 장치에 바인딩되어 메인 프로세서에서 분리돼 있죠 이 키가 특별한 이유는 Secure Enclave에 여러분이 키를 저장하면 그 키를 처리하는 게 아니라 Secure Enclave가 키를 가지고 작업을 수행하기 때문입니다 Secure Enclave keys는 접근 제어 목록에 연결될 수 있는데 줄여서 ACL이라고도 합니다 ACL은 블랍 서명 또는 암호 해독과 같은 특정 작업 수행을 위한 요구 사항을 지정합니다
지정한 항목을 사용하는 시기를 정할 수 있습니다 예를 들면, 기기 잠금 해제 후와 특정 작업의 실행을 허용하는 데 필요한 인증 요구 사항을 확인할 때가 될 수도 있죠 이 예시에서는 앱이 생체 인증을 통해 키의 서명과 암호 해독을 작업을 보호하는 동시에 기기의 잠금을 해제한 후에만 키를 사용할 수 있게 한다고 가정해 보겠습니다 이제 이 키를 포함한 서명 작업에 대해 승인하는 과정이 어떻게 되는지 보도록 하죠
응용 프로그램은 개인 키를 사용해 블랍에 서명하란 요청을 보냅니다
그다음 응용 프로그램이 키에 접근할 수 있는지 확인하고 시스템은 서명 작업을 위해 승인 요구 사항 식별을 계속 진행합니다 이 경우, 서명 작업을 수행하려면 현재 등록된 사용자 중 한 명이 생체 인식 인증에 성공해야 합니다 시스템이 사용자에게 표준 UI를 통해 셍체 인증 프로세스를 안내합니다 인증에 성공하면 시스템에서는 마지막으로 서명 작업을 수행하기 전에 모든 인증 요구 사항이 충족되었는지 확인하고 서명된 블랍을 앱에 반환합니다
이 과정과 관련된 주요 구성 요소를 분석해서 처음 정의에 어떻게 들어맞는지 확인해 보죠 첫째로는 Secure Enclave key라는 리소스가 있습니다 둘째는 키를 가지고 수행할 수 있는 작업이 있죠 셋째로는 요구 사항이 있습니다 작업을 수행할 사용자를 지정해야 하고 그 사용자의 신원을 확인하기 위한 인증 수단도 결정해야 합니다 이 예시의 매개 변수를 우리 정의에 연결해 보면 인증을 위해서는 이 사람이 맞는 사용자인지에 대한 질문에 생체 인증을 통해 답하고 승인을 위해서는 사용자가 개인 키를 사용해 서명 작업을 수행할 수 있는지에 대한 질문에는 ACL에 명시된 요구 사항을 확인함으로써 답을 합니다 지금까지 과정이 어떻게 진행되는지 확인했으니 이번엔 LocalAuthentication의 현재 API를 사용해 과정을 구현하는 방법을 알아보도록 하죠 LAContext가 제공하는 기능을 빠르게 훑어보고 시작하죠 LAContext는 프레임워크의 핵심 구성 요소 중 하나입니다 LAContext는 사용자의 신원을 평가하는 데 사용될 수 있습니다 생체 인식이나 암호 인증이 필요할 때 사용자와 상호 작용하면서 Secure Enclave와도 연결해 생체 인식 데이터를 안전하게 관리합니다 이런 면에서, LAContext는 사용자의 인증 과정을 지원하는 데 사용할 수 있습니다 LAContext는 인증 과정은 지원하는 다른 프레임워크와 함께 사용할 수도 있습니다 예를 들면, ACL 평가에도 사용할 수 있는데 이전 예시에서 봤던 것과 같죠 좀 더 자세히 알아봅시다 우리가 가장 먼저 해야 할 건 우리 개인 키와 연관된 ACL에 접속하는 겁니다 Security 프레임워크가 제공한 SecItemCopyMatching API를 사용해 이 작업을 수행할 수 있으며 쿼리에 ReturnAttributes 키를 제공해야 합니다
일단 ACL에 접근하면 evaluateAccessControl API와 LAContext를 이용해 직접 평가할 수 있습니다 이 접근 방식이 주는 가장 큰 장점은 응용 프로그램에서 사용자에게 승인을 요청하는 적절한 시간과 위치를 결정할 수 있다는 겁니다 이 경우에는 ACL이 서명 작업을 위해 생체 인증을 필요로 하기 때문에 LAContext가 Face ID나 Touch ID를 제시합니다
ACL이 LAContext 내에서 승인되면 ACL을 우리 키에 대한 참조를 얻기 위한 쿼리의 일부로 사용할 수 있습니다 UseAuthenticationContext의 SecItem 쿼리에 LAContext를 추가해 이 작업을 수행합니다
LAContext를 개인 키 참조에 바인딩하면 서명 작업을 실행할 때 다른 인증 과정이 일어나지 않기 때문에 불필요한 프롬프트 없이 작업을 계속할 수 있습니다 또한 이런 바인딩은 LAContext가 무효화될 때까지 이후 서명에 대한 사용자 상호 작용이 추가로 필요치 않음을 뜻합니다
LAContext는 아주 탄력적이며 승인 과정과 관련된 매개 변수와 각 단계를 여러분이 제어할 수 있도록 해 줍니다 Security 프레임워크 같은 다른 프레임워크와 사용할 수도 있는데 이로 인해 광범위한 사용도 가능합니다 하지만 이런 융통성으로 인해 코드는 더 복잡해지기 때문에 여러 프레임워크에서 제공하는 API를 신중하게 조정해야 합니다 사용 사례에 따라서 LAContext가 여러분에게 적합한 도구일 수도 있습니다 특히 여러분 앱의 주요 가치 제안에 키나 시크릿, 콘텍스트, ACL에 대한 낮은 수준의 액세스가 필요한 경우에 말이죠 하지만 앱에 필요한 게 콘텐츠나 민감한 리소스에 대한 접근 승인이라면 이런 융통성 중 일부를 단순한 API로 바꾸는 게 좋습니다 이제 마지막 주제인 앱 간소화입니다 iOS 16과 macOS 13에 새로 추가된 LocalAuthentication은 승인에 중점을 둔 높은 수준의 API입니다 새로운 API는 LocalAuthentication의 기존 개념을 기반으로 하며 인증 과정 실행을 단순화하는 방향으로 설계됐습니다 그래서 여러분이 앱의 가치 제안에만 전적으로 집중할 수 있도록 한 거죠 API가 도입한 가장 중요한 부분은 LARight입니다
여러분이 LARight에 제공하는 간단한 사용 사례는 응용 프로그램 리소스에 대한 승인 작업에 도움이 됩니다 예를 들면, 여러분이 사용자의 생체 인식을 요구해서 응용 프로그램의 '사용자 프로필' 섹션에 접근할 수 있는 권한을 가지는 거죠
기본적으로 권한은 일련의 인증 요구 사항으로 보호되며 사용자가 Touch ID나 Face ID, Apple Watch 다른 기기의 암호를 이용해 인증할 수 있는데 사용 중인 기기에 따라 다르죠 또한 여러분의 권한을 더 세분화된 요구 사항과 연결할 수도 있습니다 이렇게 하면 인증 수단을 더 제한할 수 있죠 코드에서 LARights를 어떻게 사용하는지 살펴볼게요
일단 권한을 인스턴스화해야 하는데 우리는 요구 사항을 명시하면 되죠 이 경우에는 사용자가 로그인 권한에 따라 생체 인식이나 암호로 인증해야 합니다 그다음 사용자가 로그인 권한을 얻을 수 있는지 확인합니다 우리는 이 정보를 이용해서 로그인 작업을 계속할 수 있는지 결정하거나 앱의 공개 섹션으로 사용자를 돌려보내야 합니다 마지막으로 사용자가 인증 UI에서 볼 수 있는 국부적인 이유를 제공하면서 실제 인증 작업을 진행할 수 있게 합니다
이렇게 권한을 받으면 새로운 시스템 기반 UI가 나타납니다 UI는 응용 프로그램 창 안에서 렌더링되며 사용자에게 관련 정보를 제공해 작업의 목적을 이해하는 데 도움을 줍니다 새로운 모습을 통해 응용 프로그램과 원활하게 통합되는 승인 과정을 구축할 수 있다고 생각합니다 그리고 사용자에게 더 많은 맥락과 정보를 제공할 겁니다
이제 권한을 생성하고 부여하는 방법을 살펴봤으니 그 주기에 대해 알아보겠습니다 권한의 주기는 알 수 없는 상태에서 시작됩니다 응용 프로그램에서 승인 요청을 보내자마자 권한의 상태가 인가 상태로 바뀝니다 이 시점에서 사용자에게 인증 UI가 표시됩니다 앞선 슬라이드에서 보셨죠
권한은 작업의 성패에 따라 authorized나 notAuthorized 상태로 전환될 수 있습니다 여러분의 응용 프로그램에 있어 가장 중요한 상태 변환이죠 마지막으로 권한은 authorized 상태에서 notAuthorized 상태로 변합니다 이건 여러분의 응용 프로그램에서 권한 해제 요청을 보내거나 인스턴스의 할당이 해제될 때 일어나죠
여러분의 권한 상태를 지키기 위해서는 본인의 권한을 잘 유지해야 합니다
권한이 해제된 후에도 응용 프로그램에서는 주기를 다시 시작하기 위해 승인 요청을 계속 보낼 수 있습니다 과거의 모든 상태 변환은 전부 관찰될 수 있습니다 여러분이 LARight 인스턴스에 접근할 수 있는 경우 상태 프로퍼티에 직접 의문을 제기할 수 있습니다 KVO나 Combine을 사용해 모든 상태 변환을 볼 수도 있죠 게다가 응용 프로그램이 여러 가지 권한을 처리하는 경우 한곳에서 모든 상태를 관찰할 수도 있습니다 didBecomeAuthorizedNotification과 didBecomeUnauthorizedNotification을 확인하는 거죠 이는 권한 상태의 변화가 감지된 후에 default NotificationCenter에서 공개하는 알림입니다
다음으로 넘어가기 전에 예시로 돌아가서 로그아웃 작업을 추가해 로그인 권한을 해제해 보겠습니다 이렇게 하면 사용자가 다음에 로그인할 때 새로운 승인 과정이 요구될 겁니다
지금까지 올바른 인스턴스를 사용해서 응용 프로그램 정의 리소스에 대한 작업을 승인하는 법을 배웠습니다 이런 권한의 주기와 상태가 궁극적으로는 런타임과 어떤 관련이 있는지도 확인했는데 이건 응용 프로그램의 모든 세션에서 권한을 정확하게 인스턴스화하고 설정해야 한단 걸 뜻합니다 이제 권한이 어떻게 지속되며 여러분의 앱이 어떤 걸 가능하게 하는지 확인해 보죠
LARight는 RIghtStore의 도움을 받아 지속될 수 있습니다
권한이 유지되면 권한의 승인 요구 사항과 맞는 접근 제어 목록 또는 ACL로 보호되는 고유한 Secure Enclave 키를 통해 백업됩니다 이러한 접근 방식은 권한이 지속된 후에도 승인 요구 사항이 변하지 않도록 합니다
또한 사용자의 권한을 지원하는 개인 키에도 접근할 수 있으며 보호 암호화 연산을 수행하는 데 사용할 수도 있습니다 암호 해독이나 서명 키 교환과 같은 작업이죠
해당되는 공용 키에도 접근할 수 있고 암호화 및 서명 확인과 같은 작업을 수행하는 데도 사용할 수 있습니다 공용 키기 때문에 관련 있는 정보도 내보낼 수 있죠
개인 키를 이용한 작업은 권한이 승인된 후에만 가능합니다 반대로 공개 키를 쓰는 작업은 항상 허용되죠
여러분의 권한이 유지되면 변하지 않는 시크릿 하나를 저장할 기회가 생깁니다 시크릿은 여러분의 권한에 대한 승인 요구 사항과 일치하는 ACL과도 관련이 있으며 권한이 승인된 후에만 접근할 수 있습니다
요약해 보죠 RightStore의 도움을 받아 LAPersistedRights를 생성합니다 설정은 한 번만 하면 되고 그 승인 요구 사항은 변하지 않습니다 권한은 저장되기 때문에 응용 프로그램의 다른 세션에서도 사용할 수 있죠 권한은 특정 기기 내부에 바인딩되어 있고 고유한 Secure Enclave key를 통해 백업되기 때문에 다른 암호화 작업을 수행하는 데 사용할 수 있는데 권한의 승인 상태에 따라 다릅니다 그 권한으로 하나의 시크릿을 보호할 수 있는데 그건 권한이 승인된 후에만 가능합니다 지금까지 PersistedRights가 제공하는 몇 가지 기능을 알아봤는데요 이제 프레젠테이션 초반에 말했던 것처럼 우리가 서명 작업을 승인하고자 하는 곳에 어떻게 구현하는지 알아보도록 하겠습니다 승인 요구 사항을 명시하는 권한을 인스턴스화하는 것부터 시작하죠 이 경우, 우리가 권한을 생성하는 순간에 기기에 생체 인증 정보가 등록되어 있는 사용자에게만 권한이 부여되도록 할 겁니다 그래서 biometryCurrentSet 요구 사항을 적용할 겁니다
그러면 RightStore의 도움으로 고유한 식별자를 제공해 권한을 유지할 수 있죠 이 식별자는 응용 프로그램에서 향후 진행되는 세션에서 권한을 불러와야 할 때 유용할 겁니다
권한이 유지되면 공개 키에 바로 접근할 수 있고 보호되지 않은 작업을 시작할 수 있습니다 명확한 승인을 받지 않고도 말이죠 이 예시에서는 공개 정보만 내보냅니다
나중에 서명 작업을 할 때가 되면 권한을 생성할 때 제공했던 고유 식별자를 사용해서 저장소에서 권한을 되찾을 수 있습니다 그다음 우리 권한에 대한 승인 작업을 통해 현재 사용자에게 권한을 부여할 수 있습니다 이 시점에서 시스템이 사용자에게 인증 과정을 안내하고 모든 인증 요구 사항이 충족되는지 확인합니다
권한이 승인되면 개인 키를 사용해서 보호된 암호화 작업을 수행할 수 있습니다 이 경우에는 응용 프로그램의 백엔드 서버에서 발급한 챌린지에 개인 키를 사용해 서명합니다 요약해 보자면 오늘은 인증과 승인이라는 일반적인 개념의 관계에 대해 얘기해 봤습니다 특히 인증이 승인을 가능하게 하는 방법에 대해 알아봤죠 LAContext가 제공하는 몇 가지 기능도 살펴보고 Security 같은 프레임워크와 결합해서 강력하고 확장 가능한 승인 과정을 만드는 법도 알아봤습니다 그리고 마지막으로 새로 추가된 LARight가 특정 권한을 부여한 사용 사례를 구현하기 위해 어떻게 코드를 간소화하는 데 도움이 되는지 알아봤습니다 여러분의 앱에서 기존에 있던 LocalAuthentication의 방식을 살펴보고 오늘 우리가 알아본 특징 중 일부가 사용자의 개인 정보를 보호하는 동시에 여러분의 코드를 간소화하는 데도 도움이 되는지 생각해 보세요
-
-
4:58 - LAContext (authorize a signature operation 1)
let query: [String: Any] = [ kSecClass as String: kSecClassKey, kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave, kSecAttrApplicationTag as String: "com.example.app.key", kSecReturnAttributes as String: true, ] var item: CFTypeRef? = nil guard SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess, let attrs = item as? NSDictionary, let accessControl = attrs[kSecAttrAccessControl] else { throw .aclNotFound }
-
5:15 - LAContext (authorize a signature operation 2)
let context = LAContext() try await context.evaluateAccessControl(accessControl as! SecAccessControl, operation: .useKeySign, localizedReason: "Authentication is required to proceed")
-
5:44 - LAContext (authorize a signature operation 3)
let query: [String: Any] = [ kSecClass as String: kSecClassKey, kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave, kSecAttrApplicationTag as String: "com.example.app.key", kSecReturnRef as String: true, kSecUseAuthenticationContext as String: context ] var item: CFTypeRef? = nil guard SecItemCopyMatching(query as CFDictionary, &item) == errSecSuccess, item != nil else { throw .keyNotFound }
-
6:00 - LAContext (authorize a signature operation 4)
let privateKey = item as! SecKey var error: Unmanaged<CFError>? guard let sgt = SecKeyCreateSignature(privateKey, self.algorithm, blob, &error) as Data? else { throw .signatureFailure }
-
8:28 - LA Right (basic usage)
// LARight: Basic usage func login() async { self.loginRight = LARight(requirement: .biometry(fallback: .devicePasscode)) do { try await loginRight.checkCanAuthorize() } catch { navigateTo(section: .public) return } do { try await self.loginRight.authorize(localizedReason: self.localizedReason) navigateTo(section: .protected) } catch { showError(.authenticationRequired) } }
-
11:01 - LARight (logout and deauthorization)
// LARight: Basic usage func login() async { self.loginRight = LARight(requirement: .biometry(fallback: .devicePasscode)) // ... do { try await self.loginRight.authorize(localizedReason: self.localizedReason) navigateTo(section: .protected) } catch { showError(.authenticationRequired) } } func logout() async { await self.loginRight.deauthorize() }
-
13:44 - LAPersistedRight
// LAPersistedRight: Retrieval and private key usage func generateClientKeys() async throws -> Data { let login2FA = LARight(requirement: .biometryCurrentSet) let persisted2FA = try await LARightStore.shared.saveRight(login2FA, identifier: "2fa") return try await persisted2FA.key.publicKey.bytes } func signChallenge(_ challenge: Data, algorithm: SecKeyAlgorithm) async throws -> Data { let persisted2FA = try await LARightStore.shared.right(forIdentifier: "2fa") let localizedReason = "Biometric authentication is required to proceed" try await persisted2FA.authorize(localizedReason: localizedReason) return try await persisted2FA.key.sign(challenge, algorithm: algorithm) }
-
-
찾고 계신 콘텐츠가 있나요? 위에 주제를 입력하고 원하는 내용을 바로 검색해 보세요.
쿼리를 제출하는 중에 오류가 발생했습니다. 인터넷 연결을 확인하고 다시 시도해 주세요.