로그인 후 매 요청마다 "이 사용자가 누구지?" 를 어떻게 알까? cookie 의 session ID? JWT? OAuth 의 access token? 사용자가 비밀 번호 안 치고 Apple / Google 로 로그인 가능한 건 어떻게? 그리고 2024 년부터 보편화된 passkey 는 비밀번호를 어떻게 완전히 대체 하나? 이 가이드는 4 가지 authentication 모델 — session / JWT / OAuth / passkey — 의 실제 메카니즘을 정리한다.
모델 1 — Session Cookie (가장 전통적)
1. 로그인:
POST /login
{username, password}
↓
Server:
- bcrypt 검증
- random session ID 생성 (예: "abc123...")
- DB / Redis 에 {sessionId: userId} 저장
- Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax
2. 후속 요청:
Cookie: session=abc123
↓
Server:
- DB lookup: sessionId 으로 userId 찾기
- request.user = User{...}
3. 로그아웃:
- DB 에서 session 삭제
- Set-Cookie: session=; expires=0장점:
- session ID 가 opaque (의미 없는 random)
- 서버가 즉시 revoke 가능 (DB 에서 삭제)
- cookie 의 HttpOnly / Secure / SameSite 로 보안 안전
- 사용자 정보 변경 즉시 반영 (매 요청마다 DB 조회)
단점:
- 매 요청마다 DB 조회 (Redis 로 완화)
- multiple server 간 session 공유 필요 (sticky session 또는 shared store)
- scale-out 어려움
모델 2 — JWT (JSON Web Token)
1. 로그인:
POST /login
{username, password}
↓
Server:
- bcrypt 검증
- JWT 생성: header.payload.signature
payload = {sub: "user-42", exp: 1700000000}
signature = HMAC-SHA256(header.payload, SECRET)
- 응답: {token: "eyJ..."}
2. 후속 요청:
Authorization: Bearer eyJ...
↓
Server:
- signature 검증 (SECRET 으로)
- exp 만료 체크
- request.user = {id: payload.sub}
↑ DB lookup 불필요실험 — JWT 디코더 가 JWT 의 header / payload / signature 분리 표시. signature 검증은 HMAC 생성기 와 같은 알고리즘.
JWT의 장점·단점
장점:
- stateless — server 가 session storage 불필요. 여러 server / microservice 간 공유 쉬움
- self-contained — payload 에 user 정보 박힘
- mobile app · API · 분산 시스템에 적합
단점:
- revoke 어려움 — token 자체가 유효하면 만료 전 까지 사용 가능. blacklist 만들면 DB 부담 (그러면 stateless 의미 ↓)
- payload 길어짐 (cookie 의 ~30 byte 가 JWT 는 ~500 byte)
- payload 가 base64 — 누구나 디코드. 민감 정보 박지 말 것 (signed ≠ encrypted)
- secret 유출 시 모든 token 위조 가능 — key rotation 필수
Refresh Token — JWT 의 revoke 문제 완화
로그인 시:
- access token: 짧은 만료 (15 분), client storage
- refresh token: 긴 만료 (30 일), HttpOnly cookie + DB 저장
요청:
1. access token 으로 API 호출 → 만료 시 401
2. refresh token 으로 새 access token 발급
POST /auth/refresh (cookie 자동 전송)
↓
Server: DB 의 refresh token 검증 → 유효하면 새 access token
3. 사용자 로그아웃 또는 의심 시: DB 의 refresh token 삭제
→ 다음 갱신 시도 시 거부Two-token pattern — short-lived access (stateless) + long-lived refresh (stateful). Revoke 가능 + scale-out 친화.
모델 3 — OAuth 2.0 (제 3 자 로그인)
"Apple / Google / GitHub 로 로그인" 의 메카니즘. Authorization Code Flow + PKCE 가 표준:
1. 사용자가 "Sign in with Google" 클릭
2. 우리 site → Google 의 authorize URL:
https://accounts.google.com/o/oauth2/v2/auth
?client_id=...
&redirect_uri=https://ours.com/callback
&response_type=code
&scope=openid email profile
&state=random_token ← CSRF 방어
&code_challenge=... ← PKCE
&code_challenge_method=S256
3. 사용자가 Google 에서 로그인 + 우리 site 의 권한 동의
4. Google → 우리 site 의 callback (with code):
https://ours.com/callback?code=ABC123&state=random_token
5. 우리 server → Google 의 token endpoint:
POST https://oauth2.googleapis.com/token
{
code: "ABC123",
client_id: "...",
client_secret: "...",
code_verifier: "..." ← PKCE
}
↓
Google 응답: {
access_token: "ya29...", ← Google API 사용 가능
refresh_token: "...",
id_token: "eyJ..." ← JWT, 사용자 정보 포함
}
6. 우리 server:
- id_token 의 signature 검증 (Google 의 public key)
- payload 에서 user 정보 (email, name)
- 우리 DB 에 user 생성/찾기
- 우리 session 또는 JWT 발급 → 사용자 로그인 완료PKCE — Public client 의 안전망
Native app / mobile / SPA 는 client_secret 안전 보관 불가 (코드 리버스 가능). PKCE 가 추가 안전:
Client 가 매번 random code_verifier 생성 (~43 chars)
code_challenge = SHA-256(code_verifier) base64url
Step 2: code_challenge 만 Google 에 보냄 (verifier 보관)
Step 5: code_verifier 함께 보냄 → Google 이 hash 일치 검증
→ 중간자가 code 가로채도 code_verifier 모름 → 사용 불가OAuth vs OpenID Connect (OIDC)
- OAuth 2.0 = authorization (권한 부여). "이 user 가 내 Drive 파일 접근 허락"
- OIDC = OAuth 위 authentication layer. id_token (JWT) 에 사용자 정보 포함. "이 user 의 이메일이 X"
"Sign in with Google" 은 OIDC. 우리는 사용자가 누구인지 알고 싶음, Google Drive 접근 X.
모델 4 — Passkey (WebAuthn)
2022+ 표준. 비밀번호 완전 대체. Apple / Google / Microsoft 가 2024 default 로 push:
1. 가입 (registration):
- server: random challenge 생성
- browser → 사용자에게 "이 site 에 passkey 만들까요?" (Touch ID / Face ID)
- device 가 새 key pair 생성:
- private key: 디바이스에 저장 (secure enclave)
- public key: server 에 전송
- server 가 public key + user 매핑 저장
2. 로그인 (authentication):
- server: random challenge 생성
- browser → 디바이스 unlock (Touch ID / Face ID)
- device 가 private key 로 challenge 서명
- server 가 저장된 public key 로 signature 검증
- 성공 → 세션 시작장점:
- 비밀번호 X — 입력할 게 없음
- phishing 불가 — passkey 가 origin 에 묶임. 가짜 site 가 사용 불가
- DB breach 무력 — server 가 public key 만 저장. 유출돼도 로그인 불가
- iCloud Keychain / Google Password Manager 가 디바이스 간 sync
단점:
- 새 디바이스 가입 시 옛 디바이스 또는 backup 필요
- ecosystem lock-in (Apple / Google 의 cloud sync 가 portable X)
- 모든 server 가 지원 X (WebAuthn API 구현 필요)
2FA (Two-Factor Authentication)
- TOTP (Time-based One-Time Password) — Google Authenticator. 30 초마다 새 6-digit. HMAC-SHA1 of current time
- SMS — 문자로 code. SIM swapping 으로 약함
- WebAuthn / Security key — YubiKey 같은 hardware key. 가장 강함
- Passkey = 본질적으로 2FA 의 일종 (디바이스 + biometric)
비교 — 어떤 걸 언제
| Session Cookie | JWT | OAuth (OIDC) | Passkey | |
|---|---|---|---|---|
| Stateful | ✅ (DB) | ❌ (self-contained) | ❌ (token 위주) | ✅ (public key 저장) |
| Revoke | 즉시 | 어려움 | refresh token 으로 | 즉시 (public key 삭제) |
| Scale-out | shared store 필요 | 매우 쉬움 | 쉬움 | 매우 쉬움 |
| 모바일 / API | cookie 적용 어려움 | 가장 적합 | 가장 적합 | 이상적이지만 도입 진행 |
| UX 비밀번호 | 입력 필요 | 입력 필요 | 외부 provider 로 위임 | 없음 |
| 보안 | 표준 | secret 관리 필수 | 구현 복잡 | 가장 강 |
실무:
- 전통 web site — session cookie. 단순, 안전.
- 모바일 + web — JWT (access + refresh).
- 제 3 자 로그인 — OAuth/OIDC.
- 모던 / 보안 민감 — passkey + fallback.
흔한 함정
1. JWT 의 secret 유출
Secret 유출 시 모든 토큰 위조. .env 관리, key rotation, asymmetric key (RS256) 으로 verify only public key 공유.
2. JWT 에 민감 정보
# Bad
payload: {sub: "42", role: "admin", ssn: "..."}
↓ base64url 이지 암호화 X
↓ 누구나 디코드 가능
# Good
payload: {sub: "42"}
↓ 추가 정보는 DB lookup3. CSRF (Cookie 모델)
Cookie 가 cross-site 자동 전송 → SameSite=Lax/Strict + CSRF token 필요. JWT in Authorization header 는 CSRF 자연 방어.
4. localStorage 의 JWT
XSS 가능성 — attacker JavaScript 가 localStorage 접근. cookie 의 HttpOnly 가 더 안전. 단 CSRF 위험. trade-off.
5. OAuth state parameter 누락
CSRF 공격 가능 — attacker 가 자신의 OAuth code 로 사용자에게 callback 유도. state random token 으로 검증.
6. password reset 의 token 만료
"reset password" link 가 만료 없거나 너무 길면 → email 노출 / steal 시 영구 위험. 15-60 분, one-time use.
참고 자료
- RFC 6749 (OAuth 2.0) — datatracker
- RFC 7519 (JWT) — datatracker
- WebAuthn (Passkey) — W3C
- OWASP Authentication Cheat Sheet — OWASP
요약
- 4 가지 모델 — Session Cookie / JWT / OAuth (OIDC) / Passkey.
- Session = stateful + DB lookup, 즉시 revoke. 전통 web 의 default.
- JWT = stateless + self-contained, scale-out 쉬움. Revoke 어려움 → refresh token pattern.
- OAuth 2.0 (+ OIDC) = 제 3 자 로그인. PKCE 가 public client (mobile/SPA) 안전. state parameter 로 CSRF 방어.
- Passkey = 비밀번호 없음 + WebAuthn + 디바이스 biometric. Phishing / DB breach 무력. 2024 default 진입.
- 2FA — TOTP (Authenticator) / SMS (약함) / WebAuthn (강) / Passkey (이상).
- 저장 위치 — Cookie (HttpOnly + Secure + SameSite) vs localStorage (XSS 위험). trade-off.
- 실험 — JWT 디코더 가 JWT 분해, HMAC 생성기 가 signature 알고리즘, Bcrypt 해시 가 password hash.