주의: JWT를 localStorage에 저장하면 XSS 공격으로 토큰이 탈취된다. JWT를 브라우저에서 사용한다면 httpOnly + Secure + SameSite=Strict 쿠키에 저장해야 한다. 이 경우 세션 쿠키와 보안 특성이 거의 같아지므로, '어차피 쿠키를 쓴다면 세션이 더 단순하다'는 논리가 성립한다.
보안 고려사항 — JWT의 함정
JWT는 구현하기 쉬워 보이지만 잘못 구현하면 세션보다 더 위험한 결과를 낳는다. 자주 발생하는 보안 실수를 정리한다.
알고리즘 혼동 공격 (Algorithm Confusion)
서버가 alg 헤더를 그대로 신뢰하면 공격자가 alg를 none으로 바꿔 서명 검증을 우회할 수 있다. 또는 RS256 서버에 HS256 토큰을 보내 공개키를 비밀키로 사용하도록 혼동시킬 수도 있다. 항상 algorithms 옵션을 명시적으로 지정한다.
Refresh Token 탈취
Refresh Token은 수명이 길어 탈취되면 피해가 크다. Refresh Token Rotation 패턴을 적용해야 한다. Refresh Token을 사용할 때마다 새 Refresh Token을 발급하고 기존 것은 폐기한다. 이미 사용된 Refresh Token이 다시 들어오면 해당 계정의 모든 세션을 강제 로그아웃시킨다.
JWT 블랙리스트의 한계
JWT 즉시 무효화를 위해 블랙리스트를 구현하는 경우가 있다. 그러나 이는 JWT의 Stateless 장점을 완전히 버리는 것과 같다. 블랙리스트가 필요하다면 Access Token 수명을 짧게(5~15분) 유지하고, 만료를 통한 자연 무효화에 의존하는 것이 더 실용적이다.
JWT vs 세션 인증 — 무엇을 선택할 것인가 — 취약점 분석 플로우차트 (출처: 공식 문서 및 벤치마크 데이터 기반)
팁: Refresh Token은 반드시 httpOnly 쿠키에 저장하고, Access Token만 메모리(변수)에 보관하는 패턴을 사용한다. 이렇게 하면 XSS로는 Refresh Token을 탈취할 수 없고, CSRF로는 Access Token을 쓸 수 없다. 두 공격 벡터를 동시에 차단하는 가장 실용적인 구성이다.
실무 선택 기준 — 무엇을 써야 하나
상황별로 권장 방식이 다르다. 다음 기준으로 판단한다.
JWT를 선택해야 하는 경우
마이크로서비스 환경 — 여러 서비스가 동일한 토큰을 각자 검증
모바일 앱 — 쿠키 관리가 불편하고, Authorization 헤더가 자연스러운 환경
서드파티 API — 클라이언트가 다양하고 서버 세션 공유가 어려운 경우
수평 확장이 잦은 환경 — 서버 인스턴스 간 세션 공유 부담 없음
세션을 선택해야 하는 경우
즉시 로그아웃이 필수 — 의심 활동 감지 시 모든 세션 강제 종료
관리자 패널 — 높은 권한 계정의 세션을 서버에서 직접 제어
단일 서버 또는 소규모 서비스 — Redis를 추가하지 않아도 되는 단순한 구성
규제 준수 — 세션 감사 로그, 동시 세션 수 제한이 요구되는 환경
하이브리드 패턴
실무에서는 두 방식을 조합하는 경우도 많다. Access Token(JWT, 단수명) + Refresh Token(DB/Redis 기반, 장수명) 패턴이 대표적이다. 빠른 요청 처리에는 JWT를 쓰고, 세션 관리와 즉시 무효화는 서버 사이드 Refresh Token 관리로 처리한다.
참고: 어떤 방식을 선택하든 HTTPS 없이는 모두 무의미하다. 쿠키의 Secure 플래그, HSTS 헤더, TLS 1.2 이상 설정은 인증 방식과 무관하게 항상 적용해야 한다.