---
type: knowledge
domain: backend
status: active
last-reviewed: 2026-07-27
---

# 인증 구현 실전

> 한 줄 정의
> 인증은 **발명하지 않는 영역**이다 — 검증된 표준 흐름 중 무엇을 고르고 토큰을 어디에 보관할지가 결정의 전부다. 인증/인가 분리 원칙은 [[백엔드 원리]], 클라이언트 측 흐름은 [[프론트엔드 원리]], 공격자 관점은 [[보안 원리]].

## 세션 vs JWT — 기본값은 서버 세션

발주사 SSO·사내 IdP가 이미 있으면 그 결정이 우선이다 — 아래 표는 자체 로그인이 승인된 경우의 선택지다 ([[05_보안 설계]]).

| | 서버 세션 (기본값) | JWT |
|---|------------------|-----|
| 즉시 무효화 | 세션 삭제 한 번 | **불가능** — 만료까지 유효 (블랙리스트를 만들면 결국 세션이다) |
| 서버 상태 | 세션 저장소 필요 | 무상태 |
| 맞는 경우 | 단일 서비스 웹 앱 대부분 | 다중 서비스 간 신원 전달, 서드파티 API |

"JWT가 더 모던하다"는 채택 근거가 아니다. 단일 앱에서 JWT를 쓰면 세션의 단점(저장소)과 JWT의 단점(무효화 불가)을 동시에 갖게 되는 경우가 많다.

## JWT를 쓴다면 — 함정 목록

- **로그아웃 문제**: 액세스 토큰은 짧게(분 단위) + 리프레시 토큰으로 갱신. 긴 수명 액세스 토큰은 탈취 시 대응 수단이 없다.
- payload는 암호화가 아니라 **인코딩**이다 — PII·권한 상세를 넣지 않는다 (누구나 디코드해서 읽는다).
- 알고리즘 고정: 서버가 기대하는 alg만 허용 (`none` 허용 사고는 실존한다).
- 클레임 검증: `iss`·`aud`·`exp`는 항상 검증한다 — 다른 서비스용 토큰이 그대로 통하는 토큰 혼동을 막는다. 다중 서비스 간 전달이 목적이면 비대칭 서명(공개키 검증)이 기본.
- 시크릿은 충분히 길게, 로테이션 계획과 함께.

## 리프레시 토큰 로테이션

1. 리프레시 토큰은 **1회용** — 사용 즉시 새 토큰 발급, 구 토큰 폐기.
2. **폐기된 토큰이 다시 사용되면 = 탈취 신호** → 해당 사용자의 토큰 패밀리 전체 무효화.
3. 보관은 httpOnly 쿠키 기본값 — 스크립트로 못 읽게. 단 XSS 자체를 막는 게 아니며, 쿠키 인증은 CSRF 대책과 세트다(아래 세션 보안).

## 비밀번호 — 직접 만들지 않는 것 목록

- 해싱은 argon2id·bcrypt 계열만(신규는 argon2id 우선) — 자체 조합(sha256+salt 손구현) 금지. 솔트는 라이브러리가 자동 처리한다.
- bcrypt의 72바이트 초과 입력은 구현에 따라 **조용히 잘리거나 오류가 난다** — 채택 라이브러리의 동작을 확인하고 길이 상한 검증을 앞단에 둔다.
- 비밀번호 정책: 길이가 왕이다. 복잡도 강제(특수문자 필수)보다 최소 길이 + 유출 비밀번호 목록 대조가 실효적.

## 계정 존재를 노출하지 않는다

- 로그인 실패: "아이디 또는 비밀번호가 올바르지 않습니다" 통일 — "존재하지 않는 계정"은 계정 열거 공격에 답을 준다.
- 비밀번호 재설정: 가입 여부와 무관하게 "메일을 발송했습니다" 통일.
- 재설정 토큰은 1회용 + 짧은 만료 + 사용 시 기존 세션 전체 무효화.

## OAuth/소셜 로그인 — 3대 필수

| 항목 | 없으면 생기는 일 |
|------|----------------|
| `state` 파라미터 검증 | CSRF로 공격자 계정에 로그인 당함 |
| PKCE | 인가 코드 가로채기 (모바일·SPA는 필수, 이제는 전부 기본값) |
| `redirect_uri` 화이트리스트 (정확 일치) | 토큰이 공격자 도메인으로 배달됨 |

로그인 용도라면 OAuth(권한 위임)가 아니라 **OIDC의 ID 토큰**이 정본이다 — 서명·`iss`·`aud`·`exp`(필요 시 `nonce`)를 검증하고, 계정 식별은 이메일이 아니라 `(iss, sub)` 조합으로 한다.

소셜 로그인 계정 연결 시: 이메일이 같다고 자동 병합하지 않는다 — 이메일 소유 검증이 안 된 provider가 있다.

## 세션 보안 기본기

- 쿠키 플래그: `httpOnly` + `Secure` + `SameSite=Lax` 이상이 기본값 — 크로스 사이트 전송이 꼭 필요한 쿠키만 `None`으로 낮추고 CSRF 토큰을 함께 강제한다.
- **로그인 성공 시 세션 ID 재발급** (session fixation 차단).
- 비밀번호 변경·재설정 시 다른 기기 세션 전체 무효화.
- 민감 작업(결제 수단 변경 등) 전 재인증 요구.

## 관련 문서

- [[백엔드 원리]] — 인증/인가 분리·deny-by-default
- [[보안 원리]] — 5대 취약점의 인증 실패 항목
- [[프론트엔드 원리]] — 401→리프레시→재시도 클라이언트 흐름
- [[외부 연동과 웹훅]] — API 키·서명 검증
- [[00_백엔드 허브]] — 06_백엔드 진입점
