들어가며
안녕하세요. Security R&D 팀에서 FIDO2 클 라이언트 개발을 담당하고 있는 김도연, 김영현입니다.
공개 키 암호화를 기반으로 한 FIDO는 패스워드나 SMS OTP 인증과 비교했을 때 사용자 관점에서는 사용하기 쉽고 안전하며 서비스 관점에서는 배포 및 유지 보수하기 쉬운 인증 표준입니다. FIDO 관련 자격 증명은 사용자의 기기를 벗어나지 않고 서버에도 저장되지 않기 때문에 피싱(phishing)이나 모든 형태의 패스워드 도용 및 재전송 공격의 위험에서 벗어날 수 있습니다.
저희 회사는 LY Corporation으로 합병 전 Yahoo Japan Corporation이 2014년에 FIDO 얼라이언스에 가입해 2019년에 이사진으로 선임되고, LINE Corporation이 2017년에 FIDO 얼라이언스 이사진으로 가입한 것을 시작으로 FIDO 서버의 상호 운용성 테스트 행사에 참여하는 등 FIDO와 관련한 여러 가지 이벤트에 참여했습니다. 그리고 2021년에는 서비스 제공 업체로서 전 세계 최초로 FIDO Universal Server 인증을 획득한 FIDO2 서버를 오픈소스로 공개했습니다. 이전에 패스워드 없는 세상으로의 첫 발걸음과 FIDO2 서버를 오픈 소스로 공개했습니다라는 글로 이와 관련된 내용을 소개했는데요. 이번 글에서는 새롭게 오픈소스로 공개하는, WebAuthn 표준을 따르는 FIDO2 클라이언트 SDK를 소개하려고 합니다. 클라이언트 측에서 FIDO2 인증을 구현할 때 이 SDK를 사용하면 더욱 쉽게 FIDO2 인증을 구현할 수 있습니다.
FIDO 소개
패스워드 등의 지식 기반 인증(knowledge-based authentication)을 사용하는 기존 인증 기술을 대체하기 위한 새로운 인증 표준인 FIDO(Fast Identity Online)는 소유 기반 인증(possession-based authentication)을 활용한 인증 방식을 제안합니다.
전통적인 인증 방식인 지식 기반 인증은 사용자와 서버가 사전에 설정해 공유한 지식을 기반으로 하는 인증 방식입니다. 사용자 입장에서는 패스워드와 같은 지식을 외우고 있어야 한다는 불편함이 있으며, 공격자가 사용자의 패스워드를 유추하거나 서버에 저장된 패스워드가 유출될 가능성이 있어 보안 사고에 매우 취약합니다.
소유 기반 인증은 사용자가 소유하고 있는 기기를 기반으로 인증을 수행하는 방식입니다. FIDO의 인증 기술은 사용자의 기기를 인증기(authenticator)로 활용합니다. 사용자의 인증 정보(예: 생체 정보)는 기기에만 저장되고 서버에는 전달되지 않기 때문에 서버 입장에서는 보안을 한층 더 강화할 수 있습니다. 사용자는 패스워드를 기억할 필요가 없으며, 사용자만이 가지고 있는 정보를 통해 안전하고 쉽게 인증할 수 있습니다.
FIDO1과 FIDO2 차이
FIDO1과 FIDO2의 차이는 다음과 같습니다.
- FIDO1: FIDO1은 UAF(Universal Authentication Framework)와 U2F(Universal 2nd Factor)라는 두 가지 프로토콜을 포함합니다. UAF는 패스워드 없는 인증을 제공하는 프로토콜이고, U2F는 기존 패스워드 기반 인증에 보안 요소를 추가한 프로토콜입니다.
- FIDO2: FIDO2는 FIDO1이 확장된 버전으로, WebAuthn(Web Authentication)과 CTAP(Client to Authenticator Protocol)를 포함합니다. FIDO2는 웹 애플리케이션과의 통합을 강화하고 보다 다양한 인증 방법을 지원합니다.
FIDO2에 새로 포함된 WebAuthn과 CTAP이 무엇인지 하나씩 살펴보겠습니다.
WebAuthn
WebAuthn은 W3C(World Wide Web Consortium)에서 창설한 Web Authentication Working Group에서 개발한 웹 인증 표준으로 웹 애플리케이션에서 사용자의 인증 정보를 안전하게 관리하고 패스워드 없는 인증을 구현할 수 있도록 지원합니다. WebAuthn에서 제공하는 기능은 다음과 같습니다.
- 등록(registration): 사용자는 자신의 기기를 웹 애플리케이션에 등록합니다. 이 과정에서 공개 키와 개인 키가 생성되며, 공개 키는 서버에 저장되고 개인 키는 기기에만 저장됩니다.
- 인증(authentication): 사용자가 웹 애플리케이션에 로그인할 때 기기는 개인 키를 사용해 서명을 생성하고 이를 서버로 전송합니다. 서버는 공개 키를 사용해 서명을 검증하고 사용자를 인증합니다.
WebAuthn은 웹뿐만 아니라 네이티브 애플리케이션에서도 동일하게 사용할 수 있습니다. iOS와 Android와 같은 플랫폼에서 제공하는 생체 인증 API를 사용해 WebAuthn을 따르는 인증을 구현할 수 있습니다.
CTAP
CTAP는 FIDO 얼라이언스가 개발한 프로토콜로 클라이언트와 인증기 간 통신에 사용하는 프로토콜이며, 두 가지 버전이 있습니다.
- CTAP1: U2F 프로토콜을 기반으로 하며 2단계 인증을 제공합니다. 기존의 FIDO U2F 기기와 호환됩니다.
- CTAP2: FIDO2의 일부로 패스워드 없는 인증을 지원하며, WebAuthn과 함께 작동해 다양한 인증 방법을 제공합니다.
FIDO2 클라이언트 SDK 소개
Security R&D 팀에서는 패스워드 없는 인증 기능을 보다 쉽게 서비스에 적용할 수 있도록 FIDO2 클라이언트 SDK를 구현했습니다. 저희가 개발한 SDK는 최신 보안 기준을 준수하고 FIDO2 인증을 받은 서버와 호환되도록 WebAuthn Level 2 스펙 문서를 따르고 있습니다.
저희가 현재 W3C에서 작업 중인 Level 3가 아닌 Level 2를 채택한 이유는 두 가지가 있습니다. 먼저 Level 3 스펙은 아직 공식적으로 확정되지 않은 초안(draft) 상태입니다. 반면, Level 2는 이미 W3C의 공식 권고(recommendation)로 채택되어 있어 더 안정적입니다. 또한 Level 3에서 소개된 Passkey는 동일한 계정 내에서 동기화가 가능한 키인데요. 비즈니스 로직에 따라 동기화를 막고자 하는 경우가 발생할 수 있는데 현재 각 플랫폼에서 제공하는 Passkey 관련 API로는 이 동기화를 제어할 수 있는 방법이 없습니다.
이와 같은 이유로 저희는 현재 Level 2 스펙을 따르고 있으며, 앞으로 Level 3 스펙이 공식적으로 확정되고 Passkey 동기화 문제에 대한 해결책이 마련되면 이를 고려하여 스펙을 업데이트할 예정입니다.
그럼 이제 본격적으로 FIDO2 클라이언트 SDK를 살펴보겠습니다. FIDO2 클라이언트 SDK는 모바일에서 작동하는 네이티브 애플리케이션에서 사용하기에 최적화된 형태로 개발했습니다. Android와 iOS 플랫폼에서 쉽게 사용할 수 있도록 플랫폼별로 각각 Kotlin과 Swift로 구현했으며, 그 결과 다음과 같은 장점을 확보했습니다.
- 플랫폼 최적화: 클라이언트를 네이티브 코드로 개발했기 때문에 네이티브 애플리케이션에서 직관적으로 클라이언트를 호출할 수 있습니다. 또한 모바일 기기의 생체 인식 기능(Face ID, Touch ID 등)을 직접 활용해 각 플랫폼에 최적화된 인증 경험을 제공합니다.
- 개발 용 이성: SDK를 이용하면 클라이언트와 인증기 간 전송 프로토콜이나 CTAP 인터페이스를 별도로 구현하지 않아도 되기 때문에 상대적으로 간단하고 빠르게 개발할 수 있습니다. 이를 통해 개발자는 더 적은 노력으로 패스워드 없는 인증 기능을 서비스에 통합할 수 있습니다.
- 유연성: 각 서비스별로 특화된 인증 로직이 필요한 경우 커스텀 클라이언트와 인증기를 쉽게 추가할 수 있습니다. 이를 통해 다양한 비즈니스 요구 사항을 충족할 수 있으며, 사용자 경험을 개선할 수 있습니다.
- 보안성: 기기 내 안전한 보안 영역(예: iOS Secure Enclave, ARM TrustZone, Android StrongBox)을 직접 활용해 생체 인식 데이터를 안전하게 처리합니다.
아래는 FIDO2 클라이언트 SDK의 전체 구조를 간략하게 나타낸 그림입니다.
PublicKeyCredential
: 사용자 등록 및 인증 전반을 위한 인터페이스로 새로운 공개 키 크리덴셜(public key credential, 비대칭 키 쌍 및 관련 정보)을 생성하거나, 인증을 위해 공개 키 크리덴셜을 사용합니다. 개발자는 이 인터페이스를 활용해 WebAuthn 표준을 따르는 사용자 등록 및 인증 기능을 서비스에 구현할 수 있습니다.Authenticator
: 사용자의 신원을 확인한 후, 확인된 사용자에 대해 새로운 공개 키 크리덴셜을 생성하거나, 생성된 공개 키 크리덴셜을 사용하는 인터페이스입니다.- 신뢰 실행 환경(trusted execution environment, TEE): 기기 내에 하드웨어적으로 격리되어 있는 보안 관점에서 안전한 실행 환경을 통칭합니다. 공개 키 크리덴 셜 중 개인 키를 저장합니다.
- FIDO2 서버(FIDO2 Server): 사용자 등록 및 인증 과정에서 필요한 여러 가지 검증을 수행하는 서버입니다. 공개 키 크리덴셜 중 공개 키를 저장합니다.
- 사용자(User): 서비스를 이용하는 사용자로, 생체 인증 또는 패스코드(기기에서 사용자를 인증하기 위한 패스워드로 서버에 저장되는 정보가 아님)를 입력하는 주체입니다.
작동 방식을 간략히 소개하면 다음과 같습니다.
PublicKeyCredential
은 재전송 공격을 막기 위해 FIDO2 서버로부터 챌린지를 받습니다.PublicKeyCredential
은 내부적으로Authenticator
를 호출합니다.Authenticator
는 생체 인식 또는 패스코드 등의 기기 인증 정보(Device Credential)를 통해 사용자의 신원을 확인합니다.- 신원 확인이 완료되면 TEE에서 공개 키 크리덴셜을 생성 또는 사용합니다. 이때 개인 키를 사용해서 챌린지를 포함한 사용자 등록 및 인증 과정에 필요한 데이터를 서명합니다.
PublicKeyCredential
은Authenticator
를 통해 서명된 데이터를 받아 FIDO2 서버에 검증을 요청합니다.- FIDO2 서버는 갖고 있는 공개 키를 사용해서 서명된 데이터를 검증합니다.
다음으로 FIDO2 클라이언트 SDK의 주요 인터페이스인 PublicKeyCredential
과 Authenticator
를 조금 더 자세히 살펴보겠습니다.
PublicKeyCredential
PublicKeyCredential
은 WebAuthn Level 2 문서의 PublicKeyCredential 인터페이스를 구현한 것으로, 패스워드가 아닌 비대칭 키 쌍(asymmetric key pair)을 크리덴셜로 사용합니다. 이 인터페이스 를 통해 크리덴셜을 생성해 사용자를 등록할 수 있고, 생성된 크리덴셜을 사용해 사용자 인증을 진행할 수 있습니다.
PublicKeyCredential
인터페이스는 사용자 등록 및 인증을 위해 WebAuthn Level 2 문서에 정의된 두 개의 API인 create
와 get
메서드를 지원합니다. create
메서드는 사용자 등록을 수행하며, 인증기를 통해 새로운 크리덴셜을 생성합니다. get
메서드는 사용자 인증을 수행하며, create
메서드로 생성된 크리덴셜을 사용합니다. 이 두 메서드는 WebAuthn Level 2 문서에 명시된 기본 로직을 구현할 뿐만 아니라 FIDO2 서버와 통신해 크리덴셜을 검증하는 역할도 수행합니다.
FIDO2 클라이언트 SDK에서는 서비스 개발자가 FIDO2 서버의 API를 호출할 때 네트워킹 방법을 선택하고 커스텀 로직을 적용할 수 있도록 RelyingParty
인터페이스를 제공합니다. 서비스 개발자는 FIDO2 서버에서 제공하는 API 호출이 원활하게 수행되도록 RelyingParty
인터페이스를 직접 구현해야 합니다.
FIDO2 클라이언트 SDK는 다양한 사용자 인증 방식을 지원하기 위해 각 인증기별로 별도의 클라이언트를 구현했습니다. 현재 PublicKeyCredential
인터페이스의 구현체로는 Biometric
과 DeviceCredential
이 있습니다. Biometric
은 인증기 중 BiometricAuthenticator
를 사용하는 공 개 키 크리덴셜 방식이며, 사용자 신원 확인 시 생체 정보만을 활용합니다. DeviceCredential
은 인증기 중 DeviceCredentialAuthenticator
를 사용하는 공개 키 크리덴셜 방식이며, 사용자 신원 확인 시 생체 정보 또는 패스코드 등의 기기 인증 정보를 활용합니다. 각 인증기에 대해서는 밑에서 자세히 설명하겠습니다.
Authenticator
Authenticator
는 WebAuthn Level 2 문서의 인증기 모델(authenticator model)을 구현한 인증기로, OS에 구현된 인증 모듈을 호출해 현재 사용자 등록 또는 인증을 시도하는 대상이 올바른 사용자인지 신원을 확인하는 작업을 수행하는 핵심 컴포넌트입니다.
Authenticator
는 사용자 등록 및 인증 과정에서 다음과 같은 여러 중요한 역할을 수행합니다.
- 크리덴셜 생성 및 저장: 사용자가 새로운 서비스에 등록할 때 고유한 크리덴셜을 생성합니다. 이 크리덴셜은 기기 내 안전한 보안 영역에서 생성된 공개 키와 개인 키 쌍으로 구성되어 있습니다. 공개 키는 서버 검증이 완료된 후 서버에 저장되며, 개인 키는 기기 내 안전한 보안 영역에만 저장됩니다.
- 크리덴셜 관련 정보 저장: 크리덴셜을 관리하기 위해 관련 정보인 공개 키 크리덴셜 소스(public key credential source)와 서명 횟수(signature counter)를 저장 합니다. FIDO2 클라이언트 SDK에서는 서비스 개발자가 상황에 맞는 적절한 저장소를 선택할 수 있도록
CredentialSourceStorage
인터페이스를 제공하고 있습니다. 서비스 개발자는 공개 키 크리덴셜의 소스와 서명 횟수가 올바르게 저장되고 관리될 수 있도록CredentialSourceStorage
인터페이스를 따르는 저장소를 직접 구현해야 합니다. - 사용자 신원 확인: 사용자에게 생체 인식 또는 패스코드 입력을 요구해 사용자의 신원을 확인합니다.
- 인증 정보 생성: 사용자 등록 과정에서 개인 키로 서명된 증명 객체(attestation object)를 생성하고 사용자 인증 과정에서 서명된 어설션 객체(assertion object)를 생성합니다. 두 객체 모두 FIDO2 서버에 전달돼 공개 키로 검증됩니다.
이와 같은 역할을 수행하기 위해 Authenticator
는 WebAuthn Level 2 문서의 Authenticator Operations에 해당하는 두 개의 메서드인 makeCredential
과 getAssertion
을 지원합니다. makeCredential
은 PublicKeyCredential
의 create
메서드 내부에서 호출되는 메서드이며, getAssertion
은 PublicKeyCredential
의 get
메서드 내부에서 호출되는 메서드입니다. 각 메서드의 구체적인 작동 방식은 각 메서드의 링크에서 확인하실 수 있습니다.
FIDO2 클라이언트 SDK는 현재 생체 인식 인증을 지원하는 BiometricAuthenticator
와 기기 인증 정보를 사용한 인증(기기 크리덴셜 인증)을 지원하는 DeviceCredentialAuthenticator
를 지 원하고 있습니다. 각 방식의 특징을 살펴보겠습니다.
생체 인식 인증
모바일 기기의 생체 인식 기능(Face ID, Touch ID 등)을 이용한 인증 방식입니다. 이 인증 방식에서는 생체 인증 템플릿을 추가하면 기존 인증 정보가 무효화됩니다. 예를 들어 기기에 지문 1이 추가된 상태에서 크리덴셜을 등록한 뒤 기기에 새로운 지문 2를 추가하면 기존 인증 정보가 무효화돼 기존에 등록한 크리덴셜을 사용해 인증할 수 없으며 크리덴셜 재등록이 필요합니다.
기기 크리덴셜 인증
생체 인식 또는 패스코드 등의 기기 인증 정보를 이용한 인증 방식입니다. 이 방식은 생체 인식 인증에 비해 유연한 인증 방식을 제공합니다. 생체 인식 인증이 지원되지 않는 기기의 경우 패스코드를 입력할 수 있으며, 생체 인식 인증이 지원되는 기기에서도 생체 인식 인증에 실패하면 패스코드를 입력할 수 있는 2차 기회를 제공합니다. 생체 인식과 기기 인증 정보를 서로 교차해서 사용하는 것도 가능합니다. 즉, 지문을 이용해 크리덴셜을 등록한 경우에도 인증 시 지문 및 기기 인증 정보를 모두 사용할 수 있습니다. 또한 생체 인증 템플릿 추가에 영향을 받지 않으며, 추가된 템플릿도 기존의 인증 정보와 호환됩니다. 예를 들어 기기에 지문 1이 추가된 상태에서 크리덴셜을 등록한 뒤에 기기에 새로운 지문 2를 추가하는 경우 지문 1과 지문 2 모두로 기존의 크리덴셜을 사용해 인증할 수 있습니다.
FIDO2 클라이언트 오픈소스 소개
Security R&D 팀은 이전에 LINE FIDO2 서버를 오픈소스로 공개한데 이어서 새로 개발한 FIDO2 클라이언트 SDK 및 테스트를 위한 데모 앱 코드도 오픈소스로 공개하기로 결정했습니다. 이런 활동이 궁극적으로 FIDO 생태계의 발전에 기여할 것이라고 기대하며, 오픈소스로 공개한 SDK와 데모 앱을 간략히 소개하겠습니다.
FIDO2 클라이언트 SDK
Security R&D 팀의 FIDO2 클라이언트 SDK 오픈소스의 GitHub 주소는 다음과 같습니다.
두 버전 모두 라이선스는 Apache-2.0이며, 각각 다음과 같은 환경에서 작동합니다.
- Android
- Gradle 7.6.3
- Android API level 28 이상
- iOS
- Swift 5.9 이상
FIDO2 클라이언트 SDK를 사용하기 위해서는 서비스 개발자가 SDK에서 제공하는 CredentialSourceStorage
와 RelyingParty
인터페이스를 각각 구현해야 합니다. 자세한 정보는 오픈소스의 README.md를 참고하시기 바랍니다.
데모 앱
Security R&D 팀은 서비스 개발자가 FIDO2 클라이언트 SDK의 사용 방법을 이해하고 보다 쉽게 서비스에 통합할 수 있도록 사용자 등록 및 인증 기능을 포함한 데모 앱을 각 플랫폼별 SDK와 함께 오픈소스로 공개했습니다. 아래는 각 플랫폼별 데모 앱의 GitHub 주소입니다.
두 버전 모두 라이선스는 Apache-2.0이며, 각각 다음과 같은 환경에서 작동합니다.
- Android
- Gradle 7.6.3
- Android API level 28 이상
- iOS
- Swift 5.9 이상
- Xcode 15.4 이상
데모 앱에서는 Security R&D 팀의 FIDO2 클라이언트 SDK를 사용할 때 서비스 개발자가 직접 구현해야 하는 CredentialSourceStorage
와 RelyingParty
인터페이스 구현 예제를 확인할 수 있습니다.
데모 앱을 실행하기 위해서는 앱과 상호작용하는 FIDO2 서버를 구동시켜야 합니다. 저희 팀에서 제공하는 FIDO2 오픈소스 서버를 사용하려는 경우 README.md를 참고하시기 바랍니다.
마치며
저희가 공개한 FIDO2 클라이언트 SDK와 데모 앱은 FIDO 생태계를 발전시키기 위한 노력의 일환입니다. 오픈소스 코드와 관련해 이슈가 있거나 개선 사항을 제안하고 싶으시다면 언제든지 GitHub 이슈 트래커에 남겨주세요. 여러분의 피드백은 저희에게 큰 도움이 됩니다.
저희 팀은 오픈소스 커뮤니티와 함께 협력하며 지속적으로 프로젝트를 개선해 나갈 계획입니다. 새로운 기능 추가 및 성능 개선, 버그 수정 등을 통해 더 나은 솔루션을 제공할 수 있도록 노력하겠습니다. 여러분의 많은 관심과 참여와 기여를 기대하겠습니다.