JWT(JSON Web Token) 깊게 이해하기
1) JWT가 뭔데?
로그인에 성공한 사용자에게 서버가 서명해서 발급하는 짧은 문자열 토큰입니다. 이후 요청은 이 토큰 하나만 실어서 오면 서버는 서명 검증과 **만료(exp)**만 확인하고 사용자를 신뢰합니다. 이때 서버가 별도 세션 저장소(메모리/DB)를 안 써도 돼서 “무상태(stateless) 인증”이 쉬워집니다.
2) 구조 (점으로 구분된 3부분)
-
Header:
{ "alg": "HS256", "typ": "JWT" }같은 메타 -
Payload(Claims): 사용자/권한/만료정보 등
-
등록 클레임:
iss(발급자),sub(주체/사용자ID),aud(대상),exp(만료),iat(발급시각),nbf(이 시각 전엔 무효) -
공개/사설 클레임:
role,scope,tenant_id등 서비스 용도에 맞게
-
-
Signature:
HMACSHA256(base64url(header)+"."+base64url(payload), secret)-
HS256(대칭키) / RS256·ES256(비대칭키) / EdDSA 등
-
기본은 **서명(JWS)**이야. **암호화(JWE)**가 아니므로, 페이로드는 누구나 볼 수 있어. 민감정보(비밀번호, 주민번호)는 절대 넣지 않는게 좋습니다!
3) 동작 흐름(Access + Refresh 조합 권장)
-
로그인: 아이디/비번 확인 성공 → 서버가 Access Token(JWT, 짧은 만료) 발급. 보통 5~15분.
-
요청: 클라가
Authorization: Bearer <access_jwt>헤더로 API 호출. -
검증: 서버는 서명과 exp 검증 → 통과 시 컨트롤러 진입.
-
갱신(만료 임박/만료): Refresh Token(길게, DB/화이트리스트 관리) 제출 → 새 Access Token 재발급.
-
Refresh는 보통 httpOnly+Secure 쿠키로 보관하고, 서버 DB(또는 캐시)에 유효 목록/버전을 둬서 강제 로그아웃/탈취 대응 가능.
-
4) 저장 위치 & 보안 고려
-
httpOnly + Secure 쿠키(권장): JS로 접근 불가 → XSS에 강함. 대신 CSRF 방어(SameSite=Lax/Strict, CSRF 토큰) 필요.
-
메모리/전역 상태(SPA 런타임 변수): 새로고침 시 사라짐, XSS엔 여전히 취약.
-
localStorage: 편하지만 XSS에 약함 → 일반적으로 비권장.
포인트: XSS를 반드시 줄입니다(컨텐츠 보안 정책 CSP, 철저한 이스케이프). 쿠키 쓸 땐 CSRF까지 생각합니다.
5) 세션 vs JWT
| 항목 | 서버 세션 | JWT |
|---|---|---|
| 상태 관리 | 서버가 가짐(상태ful) | 무상태(stateless) 가능 |
| 확장성 | 세션 스토어 필요(Sticky/Redis) | 수평 확장 쉬움 |
| 강제 로그아웃 | 즉시 가능(세션 삭제) | 기본은 어려움(만료 대기/블랙리스트 필요) |
| 사용처 | 전통 웹, SSR | SPA/모바일/API 게이트웨이/마이크로서비스 |
한 서버, 단순 로그인이라면 세션이 더 단순할 때 많습니다. 멀티 서비스/모바일/게이트웨이면 JWT가 편.
6) 무효화/강제 로그아웃 전략
-
짧은 Access Token + Refresh(표준)
-
토큰 버전 필드(예:
token_version)를 사용자 DB에 두고, 로그아웃/비번 변경 시 버전 증가 → 검증 시 버전 불일치면 거부 -
블랙리스트/화이트리스트(단기 캐시): 고위험 상황 시 만료 전이라도 막기
-
키 회전(Key Rotation): RS256/ES256 사용 시 키ID(
kid)로 다중키 운영 → 점진 폐기
7) 실수/취약점 포인트
-
alg를 신뢰하고none알골 허용하는 실수 금지 -
HS256 쓰면서 weak secret(짧은/유출 키) 사용 금지
-
민감정보 페이로드 금지(서명은 무결성, 기밀성 X)
-
만료 없는 토큰, 과도하게 긴 만료 금지
-
XSS/CSRF 대책 누락, HTTPS 미사용 등