← 기술 지식

기술 지식 / 보안 / 인증·계정

패스키의 사이트 바인딩과 서버 검증

challenge·origin·RP ID·서명을 결합하는 검증 순서와 등록 소유권·사용자 확인·동기화 복구의 차이를 설명합니다.

보안 · 원리·구현·운영
기술 지식 트리

패스키 로그인은 “지문이 성공했다”는 UI 결과가 아니라 서버가 발급한 challenge와 허용된 origin·RP ID에 대해 등록된 공개키가 서명한 assertion을 검증하는 프로토콜입니다. 등록된 자격과 계정 소유권, 사용자 존재·확인 플래그, challenge의 일회성 소비를 같은 시도 상태로 연결해야 피싱 저항과 계정 연결 안전성을 함께 유지할 수 있습니다.

패스키의 사이트 바인딩과 비밀번호 재사용 방지

비밀번호는 사람이 어떤 입력 칸에든 복사할 수 있는 공유 비밀입니다. 패스키는 서비스(relying party, RP)에 등록된 공개키 자격으로 서버가 낸 challenge에 서명하게 하므로, 서버는 비밀번호 원문 대신 공개키로 증명을 확인합니다. 브라우저와 인증기가 그 자격이 사용될 사이트 범위를 함께 제한하므로, 가짜 사이트에 진짜 서비스 비밀번호를 입력하는 식의 피싱을 줄입니다.

검증 순서는 저장된 시도 조회 → type·challenge·origin 확인 → clientData hash 계산 → authenticatorData의 RP ID hash·플래그 확인 → 등록된 credential 공개키로 서명 검증 → challenge 원자 소비 → 세션 발급으로 적을 수 있습니다. 어느 한 단계라도 빠지면 “서명은 유효하지만 다른 서비스의 challenge에 대한 서명”이나 “이미 사용한 challenge의 재생”을 성공으로 만들 수 있습니다.

하지만 프런트엔드가 success=true를 보냈다고 인증이 끝나는 것은 아닙니다. 서버는 실제 assertion의 서명과 요청 문맥을 검증하고, 그 공개키가 어느 계정에 등록된 것인지 확인해야 합니다.

Origin과 RP ID의 구분

값 예시 서버에서의 역할
origin https://login.example.com 허용된 웹 출처인지 정확히 확인
RP ID example.com 자격이 속한 relying party 범위와 rpIdHash 검증
challenge 시도별 예측 불가능한 값 이번 요청에 대한 증명인지 확인
credential ID 등록된 공개키를 찾는 식별자 해당 계정·자격 상태에 연결

RP ID는 origin 전체 URL과 같지 않습니다. 일반 웹 문맥에서는 origin의 유효 도메인과 프로토콜이 허용하는 상위 도메인 관계를 따릅니다. 임의의 다른 사이트나 public suffix를 쓰는 규칙이 아닙니다. 정확한 플랫폼·교차 origin·네이티브 앱 프로파일은 검증된 WebAuthn 라이브러리와 해당 규칙을 따릅니다.

기대 origin을 요청의 임의 Host나 Origin 헤더에서 그대로 만들면 공격자가 기대값을 선택하게 됩니다. 배포된 서비스의 신뢰 설정에서 정하고, 개발 환경용 예외가 운영 허용 목록에 남지 않게 합니다.

서명의 요청·인증기 데이터 결합

일반적인 WebAuthn assertion에서 검증 서명 입력은 authenticatorData || SHA256(clientDataJSON)입니다. clientDataJSON에 요청 유형·challenge·origin 등이 있고, authenticatorData에는 RP ID 해시와 사용자 존재·확인 플래그 등 정보가 있습니다. JSON을 임의로 재직렬화한 바이트 대신 프로토콜이 정한 원문 바이트를 사용합니다.

자격의 서명과 로그인 시도를 함께 검증합니다서버의 인증 시도: challenge·기한·용도. 브라우저 assertion: clientData·authData·서명. WebAuthn 검증: origin·RP·서명·플래그. 시도 원자 소비·세션 발급원래 기대값증명 입력모든 조건 충족서버의 인증 시도challenge·기한·용도브라우저 assertionclientData·authData·서명WebAuthn 검증origin·RP·서명·플래그시도 원자 소비·세션 발급
자격의 서명과 로그인 시도를 함께 검증합니다 화살표는 검증에 필요한 입력입니다. 유효한 서명만으로 충분하지 않으며 저장된 challenge와 허용 사이트·자격 상태가 모두 맞아야 세션을 발급합니다.
그림을 글로 읽기
  • 서버의 인증 시도: challenge·기한·용도
  • 브라우저 assertion: clientData·authData·서명
  • WebAuthn 검증: origin·RP·서명·플래그
  • 시도 원자 소비·세션 발급
  1. 서버의 인증 시도 → WebAuthn 검증: 원래 기대값
  2. 브라우저 assertion → WebAuthn 검증: 증명 입력
  3. WebAuthn 검증 → 시도 원자 소비·세션 발급: 모든 조건 충족

검증을 마치기 전에 challenge가 만료되지 않았고 아직 사용되지 않았으며, 이번 요청이 로그인인지 등록인지 확인합니다. 모든 검증에 성공했을 때 challenge 소비를 원자적으로 처리해 같은 challenge를 두 요청이 써 세션을 두 번 만들지 않게 합니다. 사용자 존재(UP)와 사용자 확인(UV)은 다른 플래그이므로, 민감 작업에 UV를 요구했다면 assertion의 실제 검증 결과가 그 조건을 충족해야 하며 UI의 지문 아이콘으로 대신할 수 없습니다.

패스키 등록과 기존 계정 소유권 검증

로그인은 이미 등록된 공개키로 증명하는 흐름이고, 등록은 새 공개키를 계정에 추가하는 흐름입니다. 등록 요청이 보낸 account ID를 그대로 믿거나 약한 세션에서 무제한 새 자격을 추가하면 강한 서명 검증도 도움이 되지 않습니다. 기존 계정의 적절한 재인증과 일회성 등록 시도를 결합합니다.

동기화된 패스키와 한 기기에 바인딩된 자격은 복구·백업 특성이 다릅니다. 서명 카운터도 모든 동기화 자격에서 엄격하게 증가한다고 가정하지 않습니다. 카운터와 백업 관련 신호의 처리 정책은 라이브러리·인증기 특성과 맞춰야 하며, 감소 하나만으로 모든 계정을 영구 차단하는 단순 정책은 오탐을 만들 수 있습니다.

세션 탈취와 복구 보안의 분리

패스키로 로그인했어도 발급된 세션 쿠키가 탈취되거나 이미 허용된 브라우저에서 악성 동작이 일어나면 자원 인가와 세션 회수가 필요합니다. 이메일 복구만으로 새 패스키 등록을 열어 주면 로그인 입구의 피싱 저항이 복구 입구에서 약해집니다. 자격 등록·삭제·분실·동기화 복구를 함께 설계합니다.

가상 인증기와 테스트 계정으로 다른 origin, 다른 RP ID, challenge 재사용·만료, 잘못된 공개키, UV 불충족, 사용자 취소를 시험합니다. 정상 등록 뒤 로그인뿐 아니라 다른 계정에 같은 자격을 잘못 연결하지 않는지도 검사합니다. 실습에서는 정상 assertion을 기준으로 origin 변경, RP ID 변경, challenge 재사용·만료, 공개키 교체, UV 불충족을 한 번에 하나씩 변형합니다. 예상 결과는 서명 자체가 맞아도 해당 문맥 검증이나 플래그 정책에서 거절되고, 정상 등록 뒤에는 credential ID가 의도한 계정에만 연결되는 것입니다. 실제 브라우저·인증기·앱 연동은 사용하는 라이브러리와 배포 origin의 공식 계약으로 별도 검증해야 합니다.

이 노트는 프로토콜 검증 설명이며 실제 인증기나 WebAuthn 브라우저 테스트를 실행한 결과는 아닙니다.

후속·관련 개념

이 개념의 확장

함께 읽을 문서

전체 기술 지식
선택 연습 · 관련 질문 1개

질문은 이해를 확인하는 보조 자료입니다. 학습 경로와 문서의 범위는 질문 수와 독립적입니다.