비밀번호는 30년 넘게 우리를 배신해 왔다. 2026년은 그 대체재가 실험이 아니라 기본값이 된 해다.
패스키(passkey)는 FIDO2와 WebAuthn 위에 세워진 공개키 기반 로그인이다. Apple·Google·Microsoft가 계정 생성 흐름의 기본값으로 밀어 넣기 시작하면서, 사용자는 이미 무엇인지 모른 채 패스키를 쓰고 있다. 이 글은 그 실체와 원리, 그리고 개발자·기업이 마주할 현실을 정리한다.
0건 패스키 피싱 성공 사례(원리상 서버에 비밀 미전송) | 15B+ 패스키 지원 온라인 계정 수(2025년 FIDO 얼라이언스 집계 규모) | 2~4x 비밀번호 대비 로그인 성공률 개선(대형 서비스 공개 사례 범위) | 3사 Apple·Google·MS 동기화 지원(사실상 전 플랫폼 커버) |
패스키란 무엇인가
패스키는 한 문장으로 요약하면 "비밀번호를 공개키 암호로 대체한 로그인 자격증명"이다. 계정을 만들 때 사용자의 기기는 공개키·개인키 쌍을 생성한다. 개인키는 기기(또는 사용자의 클라우드 키체인)를 절대 벗어나지 않고, 서버에는 공개키만 등록된다. 로그인할 때 서버가 보낸 챌린지(challenge)를 개인키로 서명하고, 서버는 저장해 둔 공개키로 그 서명을 검증한다. 서버는 검증에 필요한 비밀을 애초에 보관하지 않으므로, 데이터베이스가 통째로 유출돼도 로그인에 쓸 수 있는 비밀이 새지 않는다.
이 방식은 새로운 발명이 아니라 오래된 공개키 암호를 웹 인증에 맞게 표준화한 것이다. 세 층으로 나눠 보면 이해가 쉽다. FIDO2는 전체 규격의 우산이고, WebAuthn은 브라우저·웹사이트가 쓰는 자바스크립트 API이며, CTAP2는 노트북이 옆에 있는 스마트폰이나 보안키 같은 외부 인증기와 통신하는 프로토콜이다. "패스키"는 이 스택 위에서 만들어지는 자격증명을 가리키는 사용자 친화적 브랜드 이름일 뿐, 내부적으로는 WebAuthn 자격증명 그 자체다.
비밀번호와 OTP는 왜 실패했나
비밀번호의 근본 문제는 공유 비밀(shared secret)이라는 구조 자체에 있다. 사용자와 서버가 같은 값을 알아야 하므로, 그 값은 전송되고 저장되고 재사용되며 결국 어딘가에서 샌다. 재사용된 비밀번호 하나가 유출되면 크리덴셜 스터핑(credential stuffing)으로 수십 개 서비스가 연쇄로 뚫린다. 피싱 사이트는 진짜와 똑같은 화면을 띄우고 사용자가 직접 입력한 비밀번호를 그대로 받아 챙긴다.
SMS OTP나 인증 앱은 이 문제를 완화하지만 제거하지는 못한다. 실시간 피싱 프록시(AiTM, adversary-in-the-middle)는 사용자가 입력한 OTP를 즉시 진짜 사이트로 중계해 세션을 탈취한다. OTP 역시 사람이 읽고 옮겨 적는 전송 가능한 비밀이기 때문이다. 패스키가 결정적으로 다른 지점은 여기다. 서명은 브라우저가 확인한 출처(origin)에 묶여 생성되므로, 가짜 도메인에서는 애초에 유효한 서명이 만들어지지 않는다. 사람이 속아도 암호학이 속지 않는다.
동작 원리 — 등록과 인증
패스키에는 두 개의 의식(ceremony)이 있다. 처음 만드는 등록(registration)과 매번 로그인하는 인증(authentication)이다. 등록에서는 서버가 챌린지와 사용자 정보를 내려주고, 인증기가 키 쌍을 만들어 공개키를 돌려준다. 인증에서는 서버가 챌린지를 던지고, 인증기가 개인키로 서명한 어서션(assertion)을 돌려준다. 아래는 두 흐름의 의사코드다.
// [등록] 서버 → 클라이언트: 챌린지와 사용자 식별자 발급
const options = await server.beginRegistration(userId);
// options = { challenge, rp:{id:"youngsam.net"}, user:{id,name}, ... }
// 브라우저: 인증기에 키 쌍 생성을 요청 (지문/얼굴/PIN으로 사용자 확인)
const cred = await navigator.credentials.create({ publicKey: options });
// 클라이언트 → 서버: 공개키와 attestation을 전송해 등록 완료
await server.finishRegistration(userId, cred);
// 서버는 credentialId + publicKey + signCount 만 저장한다 (비밀 없음)
// -----------------------------------------
// [인증] 서버 → 클라이언트: 로그인용 챌린지 발급
const opts = await server.beginLogin();
// 브라우저: 저장된 개인키로 챌린지에 서명 (사용자 확인 포함)
const assertion = await navigator.credentials.get({ publicKey: opts });
// 클라이언트 → 서버: 서명 검증
const ok = await server.verify(assertion);
// - 저장된 공개키로 서명 검증
// - origin / rpIdHash 일치 확인 (피싱 차단 지점)
// - signCount 증가 확인 (복제 인증기 탐지)
if (ok) session.grant();여기서 사용자 확인(user verification)은 지문·얼굴·기기 PIN으로 이뤄진다. 이 생체정보는 서버로 전송되지 않는다. 기기 안에서 개인키 사용을 잠금 해제하는 로컬 게이트일 뿐이다. 즉 패스키는 "가진 것(기기)"과 "본인임(생체/PIN)"을 한 번에 증명하되, 그 어느 것도 네트워크로 흘려보내지 않는다.
디바이스 바운드 vs 동기화 패스키
대중화의 실질적 방아쇠는 동기화 패스키(synced passkey)였다. 초기 FIDO 보안키는 개인키가 물리적 기기에 갇혀 있어(디바이스 바운드), 기기를 잃으면 자격증명도 함께 사라졌다. 이 단절이 일반 사용자 확산을 오랫동안 막았다. 동기화 패스키는 개인키를 플랫폼의 종단간 암호화 키체인(iCloud 키체인, Google 비밀번호 관리자 등)에 넣어 같은 계정의 여러 기기로 복제한다. 새 폰을 사도 로그인이 따라온다.
| 구분 | 디바이스 바운드 | 동기화 |
|---|---|---|
| 개인키 위치 | 단일 기기·보안키에 고정 | E2E 암호화 클라우드 키체인 |
| 기기 분실 시 | 자격증명 소실(재등록 필요) | 새 기기에서 복원 |
| 보증 수준 | 높음(하드웨어 격리) | 플랫폼 계정 보안에 의존 |
| 대표 용도 | 고위험 업무·규제 환경 | 일반 소비자 서비스 |
둘은 대립이 아니라 정책 선택이다. 은행·관리자 콘솔은 디바이스 바운드(또는 하드웨어 보안키)를 요구하고, 커머스·소셜은 동기화로 편의를 극대화한다. 서버는 attestation과 인증기 플래그로 어떤 종류가 등록됐는지 구분해 정책을 강제할 수 있다.
플랫폼 지원 — 삼각 구도의 완성
2026년 현재 Apple, Google, Microsoft 세 진영이 모두 동기화 패스키를 기본 제공한다. iCloud 키체인은 Apple 생태계 안에서, Google 비밀번호 관리자는 안드로이드·크롬에서, Microsoft 계정과 Windows Hello는 윈도우에서 패스키를 관리한다. 크로스 디바이스 인증(예: 노트북 로그인 시 폰으로 승인)은 CTAP2 위의 hybrid transport로 처리되며, 근접성 확인을 위해 BLE(블루투스 저전력)를 사용해 원격 릴레이 피싱을 막는다.
여기에 credential exchange 표준화가 더해지면서, 오래 지적돼 온 "플랫폼 잠금(vendor lock-in)" 문제도 풀리기 시작했다. 사용자가 iCloud 키체인에서 서드파티 비밀번호 관리자로 패스키를 안전하게 이관할 수 있는 규격이 자리를 잡으며, 생태계 간 이동성이 실질적 현실이 됐다. 이것이 2026년을 "대중화의 해"라 부를 수 있는 또 하나의 근거다.
개발자 도입 — API·attestation·복구
구현에서 개발자가 가장 먼저 만나는 선택은 attestation을 요구할 것인가다. attestation은 인증기의 출처와 모델을 증명하는 서명이다. 특정 하드웨어만 허용해야 하는 규제 환경(정부·금융)에서는 필요하지만, 일반 소비자 서비스에서는 attestation: "none"으로 두는 편이 낫다. 불필요한 attestation 요구는 프라이버시 우려를 낳고, 일부 플랫폼에서 사용자 경험 마찰을 만든다.
// 소비자 서비스 권장 등록 옵션 예시
const options = {
challenge: randomBytes(32),
rp: { id: "youngsam.net", name: "youngsam.net" },
user: { id: userHandle, name: email, displayName: name },
pubKeyCredParams: [
{ type: "public-key", alg: -7 }, // ES256 (우선)
{ type: "public-key", alg: -257 } // RS256 (호환용)
],
authenticatorSelection: {
residentKey: "required", // 디스커버러블 → 아이디 없는 로그인
userVerification: "preferred"
},
attestation: "none" // 소비자용은 none 권장
};서버 검증에서 반드시 챙겨야 할 세 가지는 challenge 일치(재전송 공격 방지, 챌린지는 1회용·짧은 만료), origin/rpIdHash 일치(피싱 차단), 그리고 signCount 단조 증가(복제된 인증기 탐지)다. 라이브러리(예: SimpleWebAuthn, WebAuthn4J, 각 언어별 FIDO 서버)를 쓰더라도 이 검증 지점들을 이해하고 로그로 남겨야 한다.
현실적인 도입 전략은 병행 운영이다. 기존 비밀번호 로그인을 유지하되 로그인 성공 후 패스키 등록을 부드럽게 제안하고, 사용자당 최소 두 개 이상의 패스키(예: 폰 + 보안키) 등록을 유도한다. 이렇게 하면 기기 하나를 잃어도 다른 자격증명으로 즉시 복구 경로를 스스로 확보하게 된다.
기업 도입의 과제
조직 단위 도입은 소비자와 결이 다르다. 첫째, 기기 다양성이다. 공용 단말·가상 데스크톱·키오스크에는 플랫폼 인증기가 없거나 개인 계정 키체인을 쓸 수 없어, 로밍 보안키나 hybrid 흐름을 설계해야 한다. 둘째, 수명주기 관리다. 입사·퇴사·기기 교체 시 자격증명을 대량으로 발급·회수하는 프로비저닝이 SCIM·IdP와 맞물려야 한다. 셋째, 감사·규정이다. 어떤 인증기 종류가 어떤 리소스에 접근했는지 attestation 기반으로 증빙해야 하는 산업이 있다.
따라서 기업은 대개 패스키를 IdP(Identity Provider) 뒤에 둔다. 사용자는 SSO에 패스키로 한 번 로그인하고, 각 애플리케이션은 OIDC/SAML로 신뢰를 위임받는다. 이 구조에서 패스키는 "모든 앱을 바꾸는 일"이 아니라 "IdP의 1차 인증만 바꾸는 일"이 되어 도입 범위가 관리 가능해진다.
- 단계적 확대: 관리자·개발자 등 고위험 계정부터 하드웨어 보안키 패스키로 전환한다.
- 복구 이원화: 헬프데스크 재발급 절차와 자가 복구를 분리해 사회공학 공격 표면을 줄인다.
- 정책 강제: 민감 리소스는 동기화 패스키를 배제하고 디바이스 바운드만 허용한다.
남은 한계 — 2026년의 솔직한 현주소
패스키는 판도를 바꿨지만 완결되지 않았다. 가장 큰 미해결 과제는 여전히 계정 복구다. 아무리 강한 인증도 복구 채널이 약하면 그 수준으로 수렴한다. 둘째, 크로스 생태계 경험이 아직 매끄럽지 않다. credential exchange 표준이 이관을 열었지만, 실제 사용자가 안드로이드에서 아이폰으로, 또는 서드파티 관리자로 옮기는 흐름은 여전히 마찰이 있고 서비스마다 UX가 제각각이다.
셋째, UX 파편화다. 브라우저·OS 버전별로 다이얼로그 문구와 흐름이 달라 사용자가 "내 패스키가 어디 저장됐는지" 헷갈린다. 넷째, 레거시와의 공존이다. 당분간 비밀번호는 사라지지 않는다. 백업 경로로 남는 비밀번호가 여전히 공격 표면이 되며, 진짜 이득은 그 백업까지 정리해 끊어낼 때 실현된다. 그럼에도 방향은 분명하다. 신규 서비스는 이제 "비밀번호 우선"이 아니라 "패스키 우선, 비밀번호는 폴백"으로 설계하는 것이 상식이 됐다.
자주 묻는 질문
패스키를 쓰면 비밀번호를 완전히 없애도 되나요?
기술적으로는 가능하지만, 대부분의 서비스는 아직 병행합니다. 사용자가 모든 기기를 잃었을 때를 대비한 복구 경로가 필요하기 때문입니다. 다만 복구 채널이 이메일·SMS처럼 약하면 계정 보안이 그 수준으로 떨어지므로, 복수의 패스키 등록을 유도하고 복구 경로를 강화하는 것이 정석입니다.
패스키가 정말 피싱에 안 뚫리나요?
로그인 자격증명 자체는 origin에 묶여 있어 가짜 도메인에서 유효한 서명이 생성되지 않습니다. 사용자가 속아도 프로토콜이 막습니다. 다만 계정 복구 흐름, 세션 쿠키 탈취, 기기 자체의 악성코드 등 패스키 밖의 경로는 여전히 별도로 방어해야 합니다. 패스키는 "인증 단계의 피싱"을 없애는 것이지 모든 위협을 없애는 것은 아닙니다.
기기를 잃어버리면 로그인이 영영 안 되나요?
동기화 패스키라면 같은 플랫폼 계정으로 새 기기에서 자동 복원됩니다. 디바이스 바운드 패스키라면 해당 자격증명은 소실되므로, 미리 두 번째 인증기(다른 폰·보안키)를 등록해 두거나 서비스의 복구 절차를 이용해야 합니다. 그래서 "기기당 하나"가 아니라 "사용자당 둘 이상" 등록을 권장합니다.
개발자로서 attestation은 꼭 요구해야 하나요?
일반 소비자 서비스라면 대개 필요 없습니다. attestation은 특정 하드웨어 인증기만 허용해야 하는 규제·고위험 환경을 위한 기능입니다. 불필요하게 요구하면 프라이버시 우려와 UX 마찰만 늘어납니다. 소비자용은 attestation: "none"을 기본으로 두고, 필요할 때만 상향하세요.
패스키와 하드웨어 보안키(예: YubiKey)는 다른 건가요?
같은 FIDO2/WebAuthn 스택 위에 있습니다. 보안키는 개인키가 하드웨어에 고정된 디바이스 바운드 패스키의 한 형태이고, 폰·노트북의 패스키는 흔히 클라우드로 동기화되는 형태입니다. 둘 다 공개키 서명으로 로그인한다는 원리는 동일하며, 보안 요구 수준에 따라 선택합니다.
2026년 지금 새 서비스를 만든다면 어떻게 해야 하나요?
"패스키 우선, 비밀번호는 폴백"으로 설계하는 것이 현재의 상식입니다. 회원가입 직후 패스키 등록을 자연스럽게 유도하고, 아이디 없이 로그인 가능한 디스커버러블 자격증명을 쓰며, 검증 단계에서 challenge·origin·signCount를 반드시 확인하세요. 검증된 WebAuthn 서버 라이브러리를 쓰되 내부 검증 지점은 직접 이해하고 로깅하는 것을 권장합니다.

댓글 0