패스키 Overview
최근에 패스키(PassKey)에 관심이 생겨서 알아보았는데 이번에 알다본 내용을 간단하게 정리해본다. 구현 예제는 이번 글이 아닌 다른 글에서 해볼 것이다.
간단한 소개
패스키는 비밀번호를 대체하는 차세대 인증 기술로 FIDO 표준 기반의 인증 방식이다. 서비스를 운영하는 서버에는 공개 키를 저장하고 사용자의 기기에는 비공개 키를 저장한다. 사용자가 서비스에 인증할 때에는 비공개 키를 기반으로 인증을 진행하게 된다.
이 예시는 리눅스 서버에서 볼 수 있다. 흔히들 클라우드 서버에 Linux Instance를 생성하면 SSH Key 쌍을 만드는 것을 경험했을 것이다. 리눅스 서버 내에 ~/.ssh/authorized_keys에 공개 키 정보를 저장하고 접속 할 때 올바른 유저 이름과 비밀 키를 제시하면 인증할 수 있는데, 이 원리와 비슷하다고 볼 수 있다. (똑같은 건 아니다.)
장점
그렇다면 장점은 무엇일까?
- 안전하다.
비밀번호의 경우 여러 사이트에 돌려쓰거나 Weak한 비밀번호일 수 있다. 그렇다면, 해킹을 쉽게 당할 수 있다. 또한 서버 Side에서 해킹을 당했을 시, 대부분의 경우 암호화된 값이겠지만 유출이 된다. 하지만, 패스키는 서버에서는 공개키만 저장하기에 공개키로는 특별하게 할 수 있는게 없다.
- 2차 인증을 대체할 수 있다.
최근에는 비밀번호와 더불어 본인임을 인증하기 위해서 '2차 인증'이 도입되는 경우가 많다. 옛날에는 2차 비밀번호가 그러한 것이고 이가 발전되어 OTP, 이메일 인증, 기기 푸시 인증 등이 그러하다. 하지만, 이는 사용자가 서비스에 접근하기 힘든 환경을 만들어 준다. 패스키를 사용하면, 클라이언트에서 제공하는 인증만으로 기존 복잡했던 과정을 대체할 수 있다.
생성 과정
그렇다면 특정 서비스의 계정에 패스키를 어떻게 등록하는걸까? 간단하게 과정을 서술한다.
- 사용자는 서비스에 기존 방식대로 로그인을 한다.
- 사용자가 패스키 등록 요청을 한다.
- FE는 Backend 서비스에 요청하여 옵션을 받아온다. Backend에서는 RP(Relying Party), 로그인한 사용자 정보 (ID, name, displayName), Challenge (랜덤 값), Key Parameters, 이미 계정에 등록된 키의 정보를 제공한다.
여기서, User Id의 경우 패스키 인증만을 위해서 랜덤한 Uint8Array 값이 생성된다. 즉, BE에서 자체 관리하는 User와 연결하기 위해서는 별도의 테이블을 통해 유저 정보를 연계하여야한다.
- FE에서는 위 옵션을 기반으로 navigator.credentials.create()를 메소드를 실행한다. 이 요청을 받은 브라우저는 OS의 API 등과 연동하여 패스키 등록 과정을 진행한다.
- 사용자가 해당 과정을 진행하면, 브라우저는 키 쌍을 생성해서 비공개 키는 지정한 서비스 (Google 비밀번호 관리자, iCloud KeyChain, Chrome, Bitwarden 등등)에 저장한다.
- 위 값을 기반으로 공개 키를 생성하고, Challenge 값과 함께 BE에 전송한다.
- BE에서는 키 값, Challenge 값을 검증하고, DB 등에 등록한다.
인증 과정
- 사용자는 패스키로 로그인을 시도한다.
- FE에서는 BE에 인증 옵션을 요청한다.
- BE에서는 Challenge 값과 지원하는 방식 등을 FE에 제공한다.
- FE에서는 위에서 받아온 값들을 기반으로 인증 화면을 브라우저에 요구한다.
- 브라우저는 OS API 등을 호출하여 패스 키 인증을 수행한다.
- FE는 Challenge 값 등을 종합하여 BE에 전송한다.
- BE는 만들어진 공개 키를 기반으로 사용자를 찾고, Challenge 값을 기준으로 요청의 유효성을 확인한다.
- 공개 키를 기반으로 사용자를 찾는데 성공하면 서비스에 인증이 된다.
다음 글에서는 Spring Security 3.4에서 WebAuthn4J와 연동하여 패스키로 인증하는 예제를 소개하겠다.