FIDO2
패스워드 없는 인증 표준
패스워드 없는 인증 표준
FIDO2는 FIDO Alliance와 W3C가 공동 개발한 패스워드 없는 인증(Passwordless Authentication) 표준입니다. WebAuthn(Web Authentication API)과 CTAP(Client to Authenticator Protocol)의 두 가지 핵심 사양으로 구성됩니다. 사용자는 생체인식(지문, 얼굴), 보안키, PIN 등을 통해 안전하게 인증합니다.
FIDO2의 핵심은 공개키 암호화입니다. 등록 시 사용자 기기에서 공개키/비밀키 쌍이 생성되고, 비밀키는 기기에 안전하게 저장됩니다. 공개키만 서버로 전송되어 저장되므로, 서버가 해킹되어도 비밀키가 유출되지 않습니다. 피싱 공격도 원천 차단됩니다.
인증 흐름은 Challenge-Response 방식입니다. 서버가 랜덤 challenge를 보내면, 클라이언트는 비밀키로 서명하여 응답합니다. 서버는 저장된 공개키로 서명을 검증합니다. 비밀키가 기기를 떠나지 않으므로, Man-in-the-Middle 공격에도 안전합니다.
FIDO2는 모든 주요 브라우저(Chrome, Safari, Firefox, Edge)와 운영체제(Windows Hello, macOS Touch ID, Android)에서 지원됩니다. Passkey, WebAuthn, 보안키(YubiKey 등) 모두 FIDO2를 기반으로 합니다. 기업 환경에서는 MFA의 강력한 두 번째 요소로 활용됩니다.
// FIDO2/WebAuthn 등록 (Registration)
async function registerCredential(userId: string, userName: string) {
// 서버에서 challenge 및 옵션 가져오기
const optionsRes = await fetch('/api/webauthn/register/options', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId, userName })
});
const options = await optionsRes.json();
// Base64URL → ArrayBuffer 변환
options.challenge = base64URLToBuffer(options.challenge);
options.user.id = base64URLToBuffer(options.user.id);
// 브라우저 WebAuthn API 호출 - 생체인식/보안키 프롬프트
const credential = await navigator.credentials.create({
publicKey: {
...options,
authenticatorSelection: {
authenticatorAttachment: 'platform', // 내장 인증기 (Touch ID 등)
userVerification: 'required', // 생체인식 필수
residentKey: 'required' // Passkey 지원
},
pubKeyCredParams: [
{ type: 'public-key', alg: -7 }, // ES256 (ECDSA)
{ type: 'public-key', alg: -257 } // RS256 (RSA)
]
}
});
// 서버로 credential 전송하여 등록 완료
await fetch('/api/webauthn/register/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: credential.id,
rawId: bufferToBase64URL(credential.rawId),
type: credential.type,
response: {
clientDataJSON: bufferToBase64URL(credential.response.clientDataJSON),
attestationObject: bufferToBase64URL(credential.response.attestationObject)
}
})
});
}
// FIDO2/WebAuthn 인증 (Authentication)
async function authenticate() {
const optionsRes = await fetch('/api/webauthn/authenticate/options');
const options = await optionsRes.json();
options.challenge = base64URLToBuffer(options.challenge);
// 사용자에게 인증 프롬프트 표시
const assertion = await navigator.credentials.get({
publicKey: options,
mediation: 'optional' // Passkey 자동완성 지원
});
// 서버에서 서명 검증
const verifyRes = await fetch('/api/webauthn/authenticate/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: assertion.id,
response: {
authenticatorData: bufferToBase64URL(assertion.response.authenticatorData),
clientDataJSON: bufferToBase64URL(assertion.response.clientDataJSON),
signature: bufferToBase64URL(assertion.response.signature)
}
})
});
return verifyRes.json();
}
보안팀 미팅에서:
"MFA 피로 공격 때문에 OTP 방식이 뚫렸어요. FIDO2 보안키로 전환하면 피싱도 막고 사용자 경험도 좋아집니다. YubiKey 배포하거나, Windows Hello/Touch ID 같은 플랫폼 인증기만 써도 됩니다."
기술 면접에서:
"FIDO2가 피싱에 강한 이유는요?" - "origin binding 덕분입니다. credential 생성 시 도메인이 바인딩되어서, 공격자 사이트에서는 해당 credential을 사용할 수 없어요. 서버 challenge도 replay attack을 방지합니다."
아키텍처 설계에서:
"Passkey로 패스워드 완전히 없앨 수 있나요?" - "가능해요. 하지만 계정 복구 플로우가 중요합니다. 여러 기기에 Passkey 등록하게 하거나, 백업 코드, 신뢰할 수 있는 연락처 복구 등을 같이 설계해야 해요."