이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • Session은 stateless HTTP 위에서 인증 상태를 어떻게 유지하는가?
  • scale out, 배포, 장애 상황에서 session 방식이 어떤 운영 문제를 만드는가?
  • session, JWT, refresh token을 선택할 때 어떤 trade-off를 봐야 하는가?

개요

Session은 HTTP 요청 사이에 인증 상태를 이어 주는 서버 측 상태다. 일반적으로 브라우저는 session id가 담긴 cookie를 보내고, 서버는 그 id로 session store에서 사용자 상태를 찾는다. 이 구조는 단순하지만 서버 확장, 저장소 장애, 배포, 보안 사고와 바로 연결된다.

  • 서버 메모리 session은 단일 인스턴스에서는 쉽지만 scale out에서 약하다.
  • shared session store는 장애 격리와 latency를 새로 고민하게 한다.
  • session id는 로그인 성공, 권한 상승, 비밀번호 변경 같은 시점에 회전해야 한다.
  • JWT는 서버 저장소 의존성을 줄이지만 폐기와 권한 변경 반영이 어렵다.

원리

대표 흐름은 다음과 같다.

POST /login
  -> server authenticates user
  -> server creates session in store
  -> response Set-Cookie: sid=...
 
GET /me
  -> browser sends Cookie: sid=...
  -> server loads session
  -> server returns current user

장애는 두 위치에서 생긴다. 브라우저가 session cookie를 보내지 못하거나, 서버가 session id로 상태를 찾지 못한다. 따라서 로그인 장애는 cookie 디버깅과 session store 디버깅을 함께 해야 한다.

Scale Out 판단

방식장점위험
서버 메모리 session구현이 단순하고 빠름다른 인스턴스로 가면 session 없음, 재시작 시 유실
Sticky session기존 구조를 크게 바꾸지 않음특정 인스턴스 과부하, 배포/장애 시 끊김
Redis/JDBC session store인스턴스 무상태화에 가까움session store 장애가 인증 장애로 확대
Stateless token서버 store 조회 감소즉시 폐기, 권한 변경, token 탈취 대응 어려움

시니어는 “어떤 방식이 정답인가”보다 “장애 때 어디가 single point가 되는가”를 먼저 본다.

Session Fixation과 Rotation

session fixation은 공격자가 미리 알고 있는 session id를 사용자 로그인 후에도 유지하게 만들어 계정을 탈취하는 공격이다. 로그인 성공, MFA 통과, 권한 상승 후에는 session id를 새로 발급해야 한다.

Spring Security는 기본적으로 session fixation protection을 제공하지만, 커스텀 로그인이나 gateway 인증을 만들면 직접 확인해야 한다.

http
    .sessionManagement(session -> session
        .sessionFixation(sessionFixation -> sessionFixation.migrateSession())
    );

JWT와 Session의 선택 기준

JWT는 모든 문제를 해결하는 대체재가 아니다. 특히 브라우저 기반 서비스에서는 token 저장 위치가 보안 설계를 좌우한다.

  • localStorage 저장은 XSS에 취약하다.
  • HttpOnly cookie 저장은 CSRF와 SameSite 설계를 같이 해야 한다.
  • access token 수명을 짧게 하면 refresh token 회전과 탈취 탐지가 필요하다.
  • 권한이 자주 바뀌는 시스템은 stateless JWT만으로 즉시 반영이 어렵다.

기업 운영에서는 감사, 강제 로그아웃, 기기별 session 관리, suspicious login 탐지가 요구될 수 있다. 개인 프로젝트라도 최소한 token TTL, refresh 정책, 로그아웃 동작을 문서화해야 한다.

코드와 명령으로 확인하기

로그인 후 cookie jar에 저장하고 다음 요청에 재사용한다.

curl -i -c cookies.txt https://api.example.com/login \
  -H 'Content-Type: application/json' \
  --data '{"email":"me@example.com","password":"secret"}'
 
curl -i -b cookies.txt https://api.example.com/me

분산 환경에서 인스턴스별 session 상태를 의심할 때는 응답에 디버그용 instance id를 임시로 넣어 비교할 수 있다.

X-Instance-Id: app-3
X-Request-Id: 018f...

이 header는 내부 디버깅용으로만 쓰고 외부 노출 정책을 정한다.

인프라 협업 포인트

  • 서버 메모리 session이면 LB sticky 설정과 배포 방식이 인증 안정성에 직접 영향을 준다.
  • Redis session이면 Redis HA, eviction policy, timeout, network latency, connection pool을 앱 팀과 인프라 팀이 함께 봐야 한다.
  • blue-green 배포에서 session serialization class가 바뀌면 구버전 session을 신버전이 읽지 못할 수 있다.
  • gateway에서 인증을 끝내고 app에 user header를 넘기는 구조라면 header 위조 방지와 trusted proxy 경계가 필요하다.

실전 팁

  • session store metric은 auth SLO의 일부다. Redis latency, error rate, eviction, memory 사용량을 인증 장애와 연결한다.
  • logout은 cookie 삭제와 server-side session invalidation이 함께 되어야 한다.
  • session id를 로그에 그대로 남기지 않는다. 필요하면 hash 후 일부만 사용한다.
  • idle timeout과 absolute timeout을 구분한다. 보안 민감 서비스는 “활동 중이면 영원히 연장”을 피한다.
  • 모바일 앱, 웹, admin은 session 수명과 재인증 기준이 다를 수 있다.

위험 신호!

  • 서버 메모리 session인데 scale out과 rolling deploy 전략이 없다.
  • session store 장애가 전체 로그인 장애로 번지는지 모니터링하지 않는다.
  • JWT를 쓰면서 강제 로그아웃과 권한 회수 요구사항을 무시한다.
  • 로그인 성공 후 session id가 회전되지 않는다.

확인 질문

확인 질문

  • scale out 환경에서 서버 메모리 session이 위험한 이유는 무엇인가?
    • 다음 요청이 다른 인스턴스로 가거나 인스턴스가 재시작되면 session 상태를 찾지 못할 수 있기 때문이다.
  • JWT를 쓰면 session store가 완전히 필요 없어지는가?
    • access token 검증은 stateless하게 할 수 있지만 refresh token, 폐기 목록, 강제 로그아웃, 기기 관리에는 상태 저장이 필요할 수 있다.
  • session 장애를 볼 때 cookie와 store를 모두 봐야 하는 이유는 무엇인가?
    • 브라우저가 session id를 안 보내는 문제와 서버가 session id로 상태를 못 찾는 문제가 증상상 비슷하기 때문이다.

참고 문서