🔒 보안

Passkey

FIDO2 기반 패스워드 대체 인증

📖 상세 설명

Passkey는 FIDO2/WebAuthn 기반의 패스워드 대체 인증 방식으로, Apple, Google, Microsoft가 공동 지원하는 차세대 인증 표준입니다. 사용자는 지문, 얼굴 인식, PIN 등 기기 인증만으로 웹사이트에 로그인할 수 있습니다. 패스워드 없이도 피싱에 강하고 편리한 인증이 가능합니다.

Passkey의 핵심 혁신은 멀티 디바이스 동기화입니다. 기존 FIDO2 보안키는 해당 기기에서만 사용 가능했지만, Passkey는 iCloud Keychain, Google Password Manager, Windows Hello 등을 통해 여러 기기에서 동기화됩니다. 새 기기에서도 기존에 등록한 Passkey로 바로 로그인할 수 있습니다.

기술적으로 Passkey는 비밀키/공개키 쌍을 사용합니다. 비밀키는 사용자 기기의 보안 영역(Secure Enclave, TPM)에 저장되고, 절대 서버로 전송되지 않습니다. 공개키만 서버에 저장되므로, 서버 해킹 시에도 비밀키가 유출되지 않습니다. 피싱 사이트에서는 원본 사이트의 credential을 사용할 수 없습니다.

Passkey는 점진적 도입이 가능합니다. 기존 패스워드 로그인과 병행하다가, 사용자가 Passkey를 등록하면 우선 표시합니다. 브라우저 자동완성(Conditional UI)으로 Passkey가 있으면 자동 제안되어, 사용자 경험이 매우 자연스럽습니다.

💻 코드 예제

// Passkey 등록 (Registration)
async function registerPasskey() {
  // 서버에서 등록 옵션 가져오기
  const optionsRes = await fetch('/api/passkey/register/options', {
    method: 'POST',
    credentials: 'include'
  });
  const options = await optionsRes.json();

  // ArrayBuffer 변환
  options.challenge = base64URLToBuffer(options.challenge);
  options.user.id = base64URLToBuffer(options.user.id);

  // Passkey 생성 (생체인식 프롬프트)
  const credential = await navigator.credentials.create({
    publicKey: {
      ...options,
      authenticatorSelection: {
        residentKey: 'required',        // Passkey (Discoverable Credential)
        userVerification: 'required',   // 생체인식 필수
        authenticatorAttachment: 'platform' // 내장 인증기 우선
      }
    }
  });

  // 서버에 등록
  await fetch('/api/passkey/register/verify', {
    method: 'POST',
    credentials: 'include',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      id: credential.id,
      rawId: bufferToBase64URL(credential.rawId),
      response: {
        clientDataJSON: bufferToBase64URL(credential.response.clientDataJSON),
        attestationObject: bufferToBase64URL(credential.response.attestationObject)
      },
      type: credential.type
    })
  });
}

// Passkey 인증 - Conditional UI (자동완성)
async function authenticateWithConditionalUI() {
  // Conditional UI 지원 확인
  if (!window.PublicKeyCredential?.isConditionalMediationAvailable?.()) {
    return fallbackToTraditionalLogin();
  }

  const optionsRes = await fetch('/api/passkey/authenticate/options');
  const options = await optionsRes.json();
  options.challenge = base64URLToBuffer(options.challenge);

  // mediation: 'conditional'로 자동완성 UI 활성화
  // 사용자가 input 필드 클릭 시 Passkey 자동완성 표시
  const credential = await navigator.credentials.get({
    publicKey: options,
    mediation: 'conditional' // 핵심: 자동완성 통합
  });

  // 서버 검증
  const verifyRes = await fetch('/api/passkey/authenticate/verify', {
    method: 'POST',
    credentials: 'include',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      id: credential.id,
      response: {
        clientDataJSON: bufferToBase64URL(credential.response.clientDataJSON),
        authenticatorData: bufferToBase64URL(credential.response.authenticatorData),
        signature: bufferToBase64URL(credential.response.signature),
        userHandle: credential.response.userHandle
          ? bufferToBase64URL(credential.response.userHandle)
          : null
      }
    })
  });

  return verifyRes.json();
}

// HTML: Conditional UI용 input
// <input type="text" autocomplete="username webauthn" />
// autocomplete="webauthn"이 Passkey 자동완성을 트리거

// Passkey 지원 여부 확인
async function isPasskeySupported(): Promise<boolean> {
  if (!window.PublicKeyCredential) return false;

  // Platform Authenticator (Touch ID, Windows Hello 등) 지원 여부
  return await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
}

🗣️ 실무 대화 예시

제품 기획에서:

"패스워드 피로가 심해서 이탈률이 높아요. Passkey 도입하면 Touch ID 한 번으로 로그인 가능하고, 피싱도 막을 수 있어요. Google, Apple 계정처럼 자동 동기화도 되고요."

기술 면접에서:

"Passkey와 기존 보안키의 차이점은?" - "기존 FIDO2 보안키는 해당 기기에만 바인딩되지만, Passkey는 클라우드 동기화됩니다. discoverable credential이라 아이디 입력 없이도 인증 가능하고요."

UX 설계에서:

"기존 패스워드 로그인이랑 어떻게 공존시키죠?" - "Conditional UI 쓰면 됩니다. 이메일 input에 autocomplete='webauthn' 추가하면, Passkey 있는 사용자에겐 자동완성으로 제안되고, 없으면 기존 로그인 플로우로 갑니다."

⚠️ 주의사항

🔗 관련 용어

FIDO2 OAuth 2.1 MFA Zero Trust Token-based Auth

📚 더 배우기

📄 passkeys.dev - Passkey 개발자 가이드 📄 web.dev - Passkey 구현 가이드 📄 Apple Developer - Passkeys