Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 237 additions & 0 deletions keyword/chapter009/appendix/passkey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
# FIDO란?

FIDO(Fast IDentity Online)는 **비밀번호에 의존하지 않는 인증(passwordless authentication)**을 목표로 하는 국제 인증 표준이다. 기존 비밀번호 방식이 가진 근본적인 한계를 **공개키 암호화(비대칭키)** 기반 인증으로 대체하는 것이 핵심이다.

## 왜 FIDO가 등장했는가

기존 비밀번호 인증은 다음과 같은 구조적 문제를 가진다.

- **서버에 비밀번호(해시)가 저장된다** → 서버가 털리면 자격 증명이 통째로 유출된다.
- **사용자가 비밀번호를 직접 입력한다** → 피싱 사이트에 그대로 넘어갈 수 있다.
- **같은 비밀번호 재사용** → 한 사이트 유출이 다른 사이트 침해로 번진다.

FIDO는 "**서버가 비밀번호를 알지 못하게 한다**"는 발상으로 이 문제들을 한 번에 해결한다. 사용자의 인증 정보(개인키)는 사용자 기기 안에서만 존재하고, 서버는 검증에 필요한 **공개키만** 저장한다.

## FIDO의 핵심 원리

FIDO 인증의 본질은 **공개키 암호화 + Challenge-Response**다.

1. 등록(Registration) 시 사용자 기기가 **키 쌍(개인키/공개키)**을 생성한다.
2. **개인키는 기기 안에 안전하게 보관**되고 절대 밖으로 나가지 않는다.
3. **공개키만 서버로 전송**되어 서버에 저장된다.
4. 로그인(Authentication) 시 서버는 매번 **랜덤한 challenge**를 보낸다.
5. 기기는 개인키로 challenge에 **서명**해서 응답하고, 서버는 저장된 공개키로 서명을 검증한다.

비밀번호처럼 "공유된 비밀"을 주고받는 게 아니라, **개인키로 서명한 결과만 오가기 때문에** 네트워크를 도청해도, 서버가 털려도 사용자 인증 정보를 위조할 수 없다.

## FIDO 표준의 발전

| 표준 | 설명 |
| --------- | ---------------------------------- |
| FIDO UAF | 비밀번호 없이 생체인증 등으로 인증 (Passwordless) |
| FIDO U2F | 기존 비밀번호 + 물리 보안키를 더하는 2차 인증 |
| **FIDO2** | UAF/U2F를 통합·발전시킨 최신 표준. 웹 표준으로 확장 |

### FIDO2 = WebAuthn + CTAP

현재 우리가 "패스키"라고 부르는 것의 기반이 되는 표준이 **FIDO2**이며, 두 가지 규격으로 구성된다.

- **WebAuthn (Web Authentication API)**: 브라우저와 서버(웹 애플리케이션) 사이의 표준 API. W3C 웹 표준이다. 자바스크립트에서 `navigator.credentials.create()` / `navigator.credentials.get()`으로 호출한다.
- **CTAP (Client to Authenticator Protocol)**: 브라우저(클라이언트)와 인증 장치(Authenticator) 사이의 통신 규격. 예를 들어 PC 브라우저가 USB 보안키나 스마트폰과 통신할 때 사용한다.

```text
[웹 서버] ←──── WebAuthn ────→ [브라우저/OS] ←──── CTAP ────→ [Authenticator]
(지문센서/보안키/스마트폰)
```

## 패스키(Passkey)와의 관계

**패스키(Passkey)**는 FIDO2/WebAuthn 기술을 일반 사용자가 쉽게 쓸 수 있도록 만든 사용자 친화적 구현이다. 기술적으로는 FIDO 자격증명(개인키)이지만, 다음과 같은 특징으로 대중화되었다.

- **동기화 가능(Synced Passkey)**: 개인키를 iCloud 키체인, Google 비밀번호 관리자 등 클라우드에 암호화해 동기화 → 기기를 바꿔도 사용 가능.
- **기기 종속(Device-bound Passkey)**: 개인키가 특정 기기(보안키 등)를 절대 벗어나지 않음 → 보안성이 더 높지만 분실 시 복구가 까다로움.

## 인증 수단(Authenticator)의 종류

- **Platform Authenticator (내장형)**: 기기에 내장된 인증 장치. 지문(Touch ID), 얼굴(Face ID), Windows Hello 등.
- **Roaming Authenticator (외장형)**: 분리 가능한 인증 장치. YubiKey 같은 USB/NFC 보안키, 또는 스마트폰을 PC의 인증 수단으로 사용하는 경우.

## FIDO의 장점

- **피싱 저항성**: 자격증명이 도메인(Origin)에 묶여 있어 가짜 사이트에서는 동작하지 않는다. 사용자가 속아서 가짜 사이트에 접속해도 개인키 서명이 발생하지 않는다.
- **서버 유출에 강함**: 서버에는 공개키만 있으므로 DB가 털려도 인증을 위조할 수 없다.
- **재사용·도청 무력화**: 매번 다른 challenge에 서명하므로 응답을 가로채도 재사용 불가(Replay 방지).
- **사용자 편의성**: 비밀번호 암기·입력이 사라지고 생체인증으로 빠르게 로그인.

## 한계 및 고려사항

- **계정 복구(Recovery)**: 기기 분실 시 복구 흐름 설계가 까다롭다. 보통 동기화 패스키나 보조 인증 수단으로 보완한다.
- **기존 시스템 전환 비용**: 비밀번호 기반 시스템을 FIDO로 옮기려면 등록/인증 흐름과 DB 스키마(공개키 저장) 변경이 필요하다.
- **백엔드 구현**: 서버는 WebAuthn 규격에 맞춰 challenge 발급·관리, 공개키 등록, 서명 검증 로직을 구현해야 한다. (Java에서는 `webauthn4j` 등의 라이브러리 활용)


# Challenge-Response Authentication

Challenge-Response(시도-응답) 인증은 **서버가 매번 임의의 질문(Challenge)을 보내고, 클라이언트가 그에 대한 올바른 응답(Response)을 만들어 보내야만 인증되는 방식**이다. 핵심은 **비밀 자체를 네트워크로 전송하지 않는다**는 점이다.

## 왜 필요한가

비밀번호를 그대로 전송하는 방식은 두 가지 공격에 취약하다.

- **도청(Eavesdropping)**: 네트워크를 가로채면 비밀번호가 그대로 노출된다.
- **재전송 공격(Replay Attack)**: 한 번 가로챈 인증 데이터를 그대로 다시 보내면 똑같이 인증이 통과된다.

Challenge-Response는 **매번 달라지는 challenge**를 사용해 이 두 문제를 동시에 해결한다. 비밀(개인키나 비밀번호)은 응답을 "계산"하는 데만 쓰이고 직접 전송되지 않으며, challenge가 매번 다르므로 가로챈 응답을 재사용할 수 없다.

## 기본 동작 흐름

```text
1. 클라이언트 → 서버 : 로그인 시도 (나 로그인할게)
2. 서버 → 클라이언트 : Challenge 전송 (랜덤한 nonce 값)
3. 클라이언트 : 자신의 비밀(개인키/비밀번호)로 challenge를 가공해 Response 생성
4. 클라이언트 → 서버 : Response 전송
5. 서버 : 자신이 보낸 challenge + 알고 있는 정보로 Response 검증
6. 검증 성공 → 인증 완료 / 실패 → 거부
```

여기서 **Challenge는 보통 nonce(number used once)**, 즉 한 번만 쓰이는 랜덤 값이다. 매 인증마다 새로 생성되기 때문에 같은 응답이 두 번 유효할 수 없다.

## 두 가지 방식

challenge에 대한 응답을 "어떻게 만드느냐"에 따라 크게 두 갈래로 나뉜다.

### 1. 대칭키(공유 비밀) 기반

서버와 클라이언트가 **같은 비밀**을 공유하고 있다고 가정한다. 클라이언트는 challenge와 비밀을 함께 해시(HMAC 등)해서 응답을 만들고, 서버도 같은 계산을 해서 비교한다.

- 예: CHAP(Challenge-Handshake Authentication Protocol), 일부 API 인증
- **한계**: 서버도 동일한 비밀을 저장하고 있어야 하므로, **서버가 털리면 비밀이 유출**된다.

### 2. 비대칭키(공개키) 기반 - FIDO/패스키가 채택

클라이언트는 **개인키**로 challenge에 **서명**하고, 서버는 저장해 둔 **공개키**로 서명을 검증한다.

- 서버는 공개키만 가지고 있으면 되므로 **서버가 털려도 개인키를 알아낼 수 없다.**
- 개인키는 사용자 기기를 절대 벗어나지 않는다.
- 이것이 FIDO2/WebAuthn(패스키)이 사용하는 방식이다.

```text
[등록 단계]
사용자 기기 : 키 쌍 생성 → 개인키 보관, 공개키만 서버로 전송

[인증 단계]
서버 → 클라이언트 : challenge 전송
클라이언트 : 개인키로 challenge에 서명(sign)
클라이언트 → 서버 : 서명된 응답 전송
서버 : 저장된 공개키로 서명 검증(verify)
```


## 대칭키 vs 비대칭키 Challenge-Response

| 구분 | 대칭키 기반 | 비대칭키 기반 (FIDO) |
|------|-----------|-------------------|
| 서버 저장 정보 | 공유 비밀 (원문/해시) | 공개키 |
| 서버 유출 시 | 비밀 유출 → 위조 가능 | 공개키만 유출 → 위조 불가 |
| 응답 생성 | 비밀로 해시(HMAC) | 개인키로 서명 |
| 응답 검증 | 같은 비밀로 재계산 비교 | 공개키로 서명 검증 |
| 대표 사례 | CHAP, API HMAC 서명 | FIDO2 / WebAuthn / 패스키 |

## 재전송 공격(Replay)을 막는 이유

Challenge-Response의 핵심 보안 가치는 **Replay 방지**다.

- 매 요청마다 challenge(nonce)가 달라진다.
- 공격자가 네트워크에서 응답을 통째로 가로채도, 그 응답은 **그 challenge에만** 유효하다.
- 다음 인증에서는 새로운 challenge가 오기 때문에 가로챈 응답은 무용지물이 된다.

이를 보장하기 위해 서버는 보통 다음을 관리한다.

- challenge에 **짧은 유효시간(TTL)**을 둔다.
- 한 번 사용된 challenge는 **재사용 불가**로 폐기한다(예: Redis에 저장 후 사용 시 삭제).

## 다른 인증과의 비교 관점

토큰/세션 방식과 비교하면 역할이 다르다.

- **세션/토큰**: 인증에 *성공한 이후* 상태를 어떻게 유지·전달할지(인가)에 대한 방식.
- **Challenge-Response**: 애초에 인증 그 자체를 *어떻게 안전하게 수행할지*에 대한 방식.

즉 Challenge-Response로 안전하게 인증한 뒤, 그 결과를 세션이나 토큰으로 유지하는 식으로 함께 쓰일 수 있다.

# 왜 비대칭키 암호화 방식이 패스키에 적절한지

패스키(Passkey)는 FIDO2/WebAuthn 기반의 인증 수단으로, 그 핵심에는 **비대칭키 암호화(공개키 암호화)**가 있다. 왜 대칭키가 아니라 비대칭키를 쓰는지는 패스키가 해결하려는 문제(비밀번호의 한계)를 보면 명확해진다.

## 대칭키 vs 비대칭키

| 구분 | 대칭키 | 비대칭키 |
| -------- | ---------------------- | ----------------- |
| 키 구성 | 하나의 키(비밀)를 양쪽이 공유 | 개인키 + 공개키 한 쌍 |
| 암호화/검증 | 같은 키로 암호화·복호화 | 개인키로 서명 → 공개키로 검증 |
| 비밀 노출 위험 | 양쪽 모두 비밀 보관 → 노출 지점 2곳 | 개인키는 한쪽(사용자)만 보관 |
| 대표 알고리즘 | AES, HMAC | RSA, ECDSA, EdDSA |

대칭키는 양쪽이 **같은 비밀**을 들고 있어야 하지만, 비대칭키는 **개인키는 사용자만, 공개키는 누구나** 가질 수 있다. 이 비대칭성이 패스키의 모든 장점을 만든다.

## 1. 서버가 비밀을 저장하지 않는다

비밀번호 방식의 가장 큰 약점은 **서버가 인증 비밀(비밀번호 해시)을 저장한다**는 점이다. 서버 DB가 털리면 자격 증명이 통째로 유출된다.

비대칭키 방식에서는 **서버에 공개키만 저장**된다.

- 공개키는 이름 그대로 **공개되어도 무방한** 값이다.
- 공개키만으로는 서명을 위조할 수 없다(개인키를 역산할 수 없으므로).
- 따라서 **서버 DB가 통째로 유출되어도 공격자는 사용자로 위장할 수 없다.**

만약 대칭키를 썼다면 서버도 같은 비밀을 가져야 하므로, 서버 유출 = 비밀 유출이 되어 비밀번호 방식과 다를 게 없어진다. 비대칭키이기 때문에 "서버는 검증만 가능하고 위조는 불가능"한 구조가 성립한다.

## 2. 개인키가 사용자 기기를 절대 벗어나지 않는다

패스키의 개인키는 사용자 기기의 안전한 영역(Secure Enclave, TPM 등)에서 생성·보관되며 **네트워크로 전송되지 않는다.**

- 등록 시에도 **공개키만** 서버로 보낸다.
- 인증 시에도 개인키 자체가 아니라 **개인키로 서명한 결과(Response)**만 전송된다.
- 따라서 네트워크를 도청해도, 중간자(MITM)가 끼어도 개인키를 얻을 수 없다.

대칭키라면 어느 시점엔 비밀을 공유·전달해야 하는 순간이 생기지만, 비대칭키는 **전송이 필요한 것이 "공개되어도 되는 값(공개키)"과 "위조 불가능한 서명"뿐**이라 전송 과정 자체가 안전하다.

## 3. 피싱(Phishing)에 강하다

패스키의 자격증명은 **특정 도메인(Origin)에 묶여서** 생성된다(WebAuthn의 `rpId`).

- `bank.com`에서 만든 패스키는 `bank.com`에서만 동작한다.
- 사용자가 속아서 `bank-fake.com`에 접속해도 브라우저가 Origin이 다름을 인지하여 **개인키 서명 자체가 일어나지 않는다.**

이것은 "사용자가 비밀을 직접 입력하지 않는다"는 비대칭키 구조 덕분이다. 입력할 비밀이 없으니 피싱 사이트에 넘겨줄 비밀도 없다.

## 4. Challenge-Response와 결합해 재전송을 막는다

비대칭키는 **서명(Signature)**이라는 강력한 도구를 제공한다. 서버가 매번 다른 challenge를 보내고 사용자가 개인키로 서명하면:

- 응답은 그 challenge에만 유효하다 → **Replay 공격 무력화**.
- 서버는 공개키로 서명을 검증만 하면 된다 → 서버는 비밀을 몰라도 검증 가능.

```text
서버 → 사용자 : challenge (랜덤 nonce)
사용자 기기 : 개인키로 challenge 서명 (개인키는 기기 밖으로 안 나감)
사용자 → 서버 : 서명된 응답
서버 : 저장된 공개키로 서명 검증 → 통과 시 인증
```

## 정리: 비대칭키가 패스키에 적절한 이유

비밀번호(대칭적 비밀 공유) 방식이 가진 **3대 약점**을 비대칭키가 정확히 메운다.

| 비밀번호의 문제 | 비대칭키(패스키)의 해결 |
|----------------|----------------------|
| 서버에 비밀 저장 → 유출 위험 | 서버엔 공개키만 저장 → 유출돼도 위조 불가 |
| 비밀을 네트워크로 전송 | 개인키는 전송 안 함, 서명만 전송 |
| 사용자가 비밀을 입력 → 피싱 | 입력할 비밀이 없음 + Origin에 묶임 → 피싱 무력 |

즉, **"검증은 누구나(서버) 할 수 있지만 위조는 개인키 소유자만 할 수 있다"**는 비대칭키의 본질이, 패스키가 추구하는 *서버가 비밀을 모르는 안전한 인증*을 가능하게 한다. 대칭키로는 이 구조를 만들 수 없기 때문에 패스키는 반드시 비대칭키 기반이어야 한다.
Loading