패스키(WebAuthn) 입문: 비밀번호와 뭐가 다르고 무엇을 설계해야 하나

발행 읽는 시간 약 5 분

  • 패스키
  • WebAuthn
  • 보안
  • 인증
  • Next.js

글 소개

패스키 도입을 코드 스니펫이 아니라 검증 모델·복구·점진 도입 관점에서 정리했습니다. 사용자가 체감하는 차이, WebAuthn 의 세 역할, Next.js 백엔드에서의 흐름, 실제 도입 검토 때 막혔던 복구 설계와 결정 항목 체크리스트까지 다룹니다.

"패스키를 달면 끝" 이 아닙니다. 패스키는 로그인 화면 하나를 바꾸는 일이 아니라, 사용자가 잃어버릴 수 있는 것(기기, 계정, 생체 정보)과 서버가 믿는 것(공개키, challenge, origin)을 다시 정리하는 일입니다. 이 글은 구현 라이브러리 나열 대신 비밀번호와의 차이, WebAuthn 에 등장하는 역할, 그리고 실제로 도입을 검토하면서 가장 오래 걸렸던 복구 설계를 정리합니다. 작은 B2B 서비스에 패스키를 붙이는 검토를 하면서 적은 기준입니다.


사용자가 체감하는 차이#

비밀번호는 결국 기억하거나 적어 둔 공유 비밀 입니다. 서버도 알고(해시 형태로) 사용자도 알기 때문에 유출, 재사용, 피싱에 노출됩니다. 패스키는 기기나 OS 가 보관하는 비대칭 키 입니다. 서버에는 공개키만 남고, 개인키는 기기 밖으로 나가지 않습니다. 키가 도메인에 묶이기 때문에 가짜 사이트에서는 같은 키가 작동하지 않습니다. "패스키는 피싱에 강하다" 는 말이 나오는 이유입니다.

비밀번호 로그인과 패스키 로그인 비교

사용자 입장에서는 "비밀번호 입력" 이 "지문·얼굴 확인" 으로 바뀌는 것이 전부처럼 보입니다. 그런데 복구 는 오히려 어려워집니다. 비밀번호는 "이메일로 재설정" 한 번이면 끝나던 흐름이, 패스키만으로 가면 기기를 바꾸거나 잃어버렸을 때 다른 기기 등록, 백업 키, 사람이 확인하는 절차 같은 설계가 필요해집니다. 도입 검토에서 시간이 가장 많이 든 지점이 여기였습니다.


WebAuthn 의 세 역할#

스펙 문서에 나오는 세 이름만 정리돼 있어도 문서가 절반은 읽힙니다.

WebAuthn 등장인물: RP, 브라우저, Authenticator

  • Relying Party(RP). 우리 서비스의 백엔드와 도메인입니다. challenge 를 만들고, 서명을 검증하고, 공개키를 저장합니다.
  • 클라이언트(브라우저). navigator.credentials 로 OS 의 인증 UI 를 띄우고, 어느 출처(origin)에서 요청했는지를 Authenticator 에 전달합니다.
  • Authenticator. 키를 실제로 보관하고 서명하는 쪽입니다. 플랫폼 내장(폰의 보안 칩, 노트북의 TPM)일 수도 있고, 외장 보안 키일 수도 있습니다.

패스키가 피싱에 강한 이유는 브라우저가 origin 을 Authenticator 에 넘기고, 키가 그 origin 에 묶여 있어서입니다. 가짜 도메인에서는 맞는 키를 찾지 못합니다.


Next.js 백엔드에서의 흐름#

App Router 기준으로 보통 아래처럼 갑니다.

  1. 등록. Route Handler 가 challenge 를 만들어 세션에 저장하고 클라이언트에 보냅니다. 브라우저가 Authenticator 로 키를 만들고 공개키·credentialId 를 돌려주면, 서버가 challenge 와 origin 을 확인한 뒤 사용자에게 연결해 저장합니다.
  2. 로그인. 서버가 새 challenge 를 발급하고, 브라우저가 Authenticator 서명을 받아 보내면, 서버가 저장된 공개키로 서명을 검증하고 세션 쿠키를 발급합니다.

서명 검증과 credentialId 매핑은 검증된 라이브러리를 씁니다. ASN.1 파싱이나 COSE 키 처리를 직접 짜는 것은 피하는 것이 맞습니다. 세션은 기존 방식 그대로 두고, "어떤 인증 수단으로 세션이 열렸는지" 만 기록해 두면 나중에 수단별 정책을 둘 수 있습니다.

검토하면서 막혔던 것. challenge 를 어디에 두느냐입니다. 서버 메모리에 두면 인스턴스가 여러 개일 때 깨지고, 쿠키에 두면 크기와 만료를 따로 관리해야 합니다. 작은 서비스에서는 서버 세션 저장소(Redis 등)에 짧은 만료로 두는 것이 가장 단순했습니다.


복구 설계: 결정해야 할 것#

패스키 도입에서 실제로 결정해야 하는 것은 로그인이 아니라 복구입니다. 아래 질문에 답이 없으면 출시 후 고객 문의가 몰립니다.

상황결정할 것
기기를 바꿨다OS 계정 동기화(iCloud 키체인 등)로 패스키가 따라오는가, 안 따라오는 경우 어떻게 하는가
기기를 잃어버렸다두 번째 기기 등록을 처음부터 권장할 것인가, 이메일·SMS 같은 보조 수단을 둘 것인가
패스키를 지워 버렸다비밀번호를 남겨 둘 것인가, 남긴다면 패스키의 보안 이점이 얼마나 줄어드는가
회사 기기만 쓰는 사용자기업 정책으로 생체 인증이 막힌 경우 외장 키를 허용할 것인가
아무것도 안 된다사람이 확인하는 복구 절차(본인 확인 자료, 대기 시간)를 둘 것인가

저희 검토에서는 "비밀번호를 당분간 남기고, 패스키를 두 개 이상 등록하도록 유도하며, 이메일 복구는 유지" 로 결론이 났습니다. 보안 이점은 줄지만 고객 지원 비용이 현실적이었습니다. 이 결정은 서비스마다 다르고, 정답이 있다기보다 어느 비용을 감수할지의 문제입니다.


점진 도입을 권하는 이유#

이미 비밀번호와 MFA 를 쓰는 서비스라면, 비밀번호와 패스키를 병행 한 뒤 로그인 화면에서 패스키를 조금씩 앞으로 당기는 방식이 운영 위험이 적습니다. 순서는 보통 이렇습니다.

  1. 설정 화면에서 패스키 등록을 "선택" 으로 연다.
  2. 등록한 사용자에게 로그인 시 패스키를 먼저 제안한다.
  3. 등록률과 복구 문의를 몇 주 본다.
  4. 특정 사용자군(사내 사용자, 관리자)부터 패스키 필수로 바꾼다.

팀에는 "패스키가 비밀번호를 대체할 수 있는가" 보다 "어떤 사용자군부터 시도할지" 를 먼저 정하자고 말하는 편입니다. 전체 전환을 한 번에 하면 복구 문의가 한꺼번에 몰립니다.


정리#

패스키는 인증 UX 와 보안 구조를 동시에 바꾸는 결정입니다. 로그인 코드는 라이브러리가 해결해 주지만, 복구 절차와 사용자군별 전환 순서는 서비스가 직접 정해야 합니다. 이 글은 그 결정에 필요한 개념 지도만 그렸고, 실제 도입 때는 브라우저·OS 지원 범위, 기업 정책, 규제 요구를 릴리즈 노트 수준까지 따라가야 합니다.

문의 폼처럼 작은 지점에서 개인정보 처리를 정리하는 방법은 문의 폼 개인정보 처리 정리 글에, 장애가 났을 때의 대응 기준은 장애 대응과 SLA에 따로 적었습니다.

Follow · Ask · Work

이 글이 도움이 됐다면

짧은 작업 기록과 데모는 인스타그램에 올립니다. 만들고 싶은 것이 있다면 프로젝트 의뢰나 협업 제안도 편하게 보내 주세요.

글쓴이

파란여우

직접 만든 AI 앱·제품의 개발·배포를 공개하는 실전형 테크 크리에이터

파란여우 소개 Instagram @bluefox._.hi

시리즈 · 개발 노트

시리즈 전체 보기

이어서 보기

만들고 싶은 것이 있다면 프로젝트 의뢰로 알려 주세요.