이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- 세션, JWT, 토큰, 쿠키를 같은 비교군으로 두면 왜 헷갈리는가?
- 세션 로그인과 JWT Resource Server 방식은 매 요청에서 SecurityContext를 어떻게 복원하는가?
- CSRF, 로그아웃, 강제 만료는 인증 저장 방식과 운반 방식에 따라 어떻게 달라지는가?
개요
Security 학습에서 가장 많이 엉키는 지점은 세션, JWT, 토큰, 쿠키를 같은 층위의 선택지로 비교하는 것이다. 이들은 서로 다른 역할을 한다. 세션은 서버 저장 방식이다. JWT는 토큰 값의 형식이다. 쿠키는 브라우저가 값을 저장하고 자동 전송하는 운반 수단이다.
기존 문서의 짧은 결론도 정확했다. 쿠키는 브라우저가 값을 저장하고 자동으로 보내는 방식이고, 토큰은 사용자를 증명하는 문자열이다. JWT 방식은 매 요청마다 토큰을 검증해서 Authentication을 다시 만들고, 세션 방식은 Session ID로 서버 세션의 SecurityContext를 복원한다.
세션 방식
세션 로그인에서는 인증 성공 후 서버가 SecurityContext를 HttpSession에 저장한다. 클라이언트는 Session ID를 쿠키로 받는다. 이후 요청에서 브라우저가 JSESSIONID 쿠키를 자동 전송하면 서버는 해당 세션을 찾아 SecurityContext를 복원한다.
로그인 성공
-> 서버 HttpSession에 SecurityContext 저장
-> Set-Cookie: JSESSIONID=...
-> 이후 요청에서 Cookie: JSESSIONID=...
-> 서버가 SecurityContext 복원이 방식은 서버가 인증 상태를 관리하므로 로그아웃과 강제 만료가 상대적으로 명확하다. 대신 서버 세션 저장소, 확장성, sticky session 또는 shared session store 같은 운영 고려가 생긴다.
JWT 방식
JWT Resource Server 방식에서는 클라이언트가 Authorization: Bearer access-token 헤더로 토큰을 보낸다. 서버는 토큰 서명, 만료, issuer, audience 등을 검증하고 Authentication을 구성한다.
보호 API 요청
-> Authorization: Bearer access-token
-> JwtDecoder가 JWT 검증
-> JwtAuthenticationProvider가 Authentication 생성
-> SecurityContext에 저장
-> AuthorizationFilter가 권한 판단Access token만 보면 stateless하게 동작할 수 있다. 하지만 refresh token 저장, 로그아웃, 탈취 대응, 강제 만료가 필요하면 서버 측 상태가 다시 등장한다. 실무 JWT 설계는 “세션을 없앴다”가 아니라 “어떤 상태를 어디에 둘 것인가”의 문제다.
Cookie와 CSRF
쿠키는 브라우저가 요청에 자동으로 붙여 보내는 운반 수단이다. 이 자동 전송 성질 때문에 CSRF 문제가 생긴다. 사용자가 로그인된 브라우저에서 공격 사이트를 방문했을 때, 브라우저가 대상 사이트 쿠키를 자동으로 보내면 의도하지 않은 요청이 인증된 요청처럼 처리될 수 있다. Spring Security는 기본적으로 POST 같은 unsafe HTTP method에 CSRF 보호를 적용한다.
Authorization 헤더에 Bearer token을 JS가 직접 붙이는 구조는 CSRF 위험 모델이 다르다. 브라우저가 자동으로 Authorization 헤더를 붙이지 않기 때문이다. 반대로 JWT를 쿠키에 담아 자동 전송하게 만들면 CSRF 고려가 다시 중요해진다. SameSite 쿠키 설정은 방어를 도울 수 있지만, 애플리케이션의 CSRF 전략 전체를 대체한다고 단정하면 안 된다.
CSRF 방어의 핵심은 브라우저가 자동으로 포함하지 않는 위치에 token을 함께 보내게 하는 것이다. SPA에서 CookieCsrfTokenRepository를 쓰는 경우에도 cookie에 저장된 token을 JavaScript가 읽어 X-XSRF-TOKEN 같은 header로 보내는 흐름을 이해해야 한다.
Spring Security 6의 CSRF 처리에서는 deferred token과 BREACH 보호 같은 세부 동작도 고려된다. 또한 인증 성공이나 로그아웃 이후 기존 CSRF token cookie가 정리될 수 있으므로, SPA는 초기 로드뿐 아니라 로그인/로그아웃 직후에도 새 token을 받을 경로를 마련해야 한다.
로그아웃과 무효화
세션 방식 로그아웃은 서버 세션을 무효화하고 SecurityContext를 지우면 된다. JWT access token은 발급 후 만료 전까지 서명 검증만으로 유효할 수 있으므로 즉시 무효화가 어렵다.
JWT에서 강제 만료가 필요하면 짧은 access token 만료 시간, refresh token rotation, refresh token 저장소, access token jti denylist, 사용자 tokenVersion 같은 전략을 조합한다. 이 선택은 보안 요구, 성능, 저장소 비용, 사용자 경험의 균형이다.
실전 팁
- 세션, JWT, 쿠키를 비교할 때 “저장 위치, 값의 형식, 운반 수단”으로 층위를 나눈다.
- SPA + Bearer token 구조인지, 쿠키 기반 인증 구조인지에 따라 CSRF 대응을 다시 판단한다.
- JWT access token은 짧게, refresh token은 저장소와 rotation 정책으로 관리하는 구성을 검토한다.
- 로그아웃 요구사항이 강하면 완전 stateless JWT만으로 해결하려 하지 않는다.
- Authorization header, Cookie, SameSite, HttpOnly, Secure 설정을 함께 설계한다.
- CSRF를 끌 때는 “비브라우저 API인지”, “브라우저가 인증 정보를 자동 전송하는지”, “unsafe method가 있는지”를 함께 확인한다.
- SPA는 로그인 성공과 로그아웃 이후 새 CSRF token을 다시 받는 흐름을 테스트한다.
위험 신호!
- “JWT를 쓰므로 CSRF는 무조건 꺼도 된다”고 단정한다.
- SameSite 쿠키만 설정하면 CSRF 검토가 끝났다고 본다.
- 로그인 이후 갱신된 CSRF token 없이 첫 POST를 보내 403이 나는 흐름을 놓친다.
- JWT를 localStorage에 저장하면서 XSS 위험을 고려하지 않는다.
- refresh token을 access token과 같은 수명과 저장 위치로 둔다.
- 로그아웃 버튼이 클라이언트 토큰 삭제만 하고 서버 refresh token을 폐기하지 않는다.
- 세션 방식과 JWT 방식이 섞였는데 SecurityContext 저장 정책이 불명확하다.
확인 질문
확인 질문
- 세션, JWT, 쿠키의 역할 차이는 무엇인가?
- 세션은 서버 인증 상태 저장 방식, JWT는 토큰 값의 형식, 쿠키는 브라우저 자동 전송 운반 수단이다.
- JWT Resource Server는 매 요청에서 무엇을 하는가?
- Bearer token을 검증하고 Authentication을 구성해 SecurityContext에 올린다.
- JWT를 쿠키에 담아 자동 전송하면 왜 CSRF를 다시 고려해야 하는가?
- 브라우저가 쿠키를 자동으로 보내므로 공격 사이트에서 의도하지 않은 인증 요청을 만들 수 있기 때문이다.