이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- 서버 세션 기반 인증에서 세션 ID는 왜 bearer credential인가?
- session fixation, 탈취, 권한 변경 반영 문제를 어떻게 방어하는가?
- Spring Security와 HTTP Cookie 설정에서 어떤 항목을 확인해야 하는가?
개요
Session 기반 인증은 서버가 인증 상태를 저장하고, 브라우저는 세션 ID를 쿠키로 보내는 방식이다.
세션은 폐기와 권한 변경 반영이 쉽다는 장점이 있다. 서버 저장소에서 세션을 지우거나 갱신하면 다음 요청부터 영향을 줄 수 있다.
대신 세션 저장소, 쿠키 보안 속성, session fixation 방지, 동시 세션 정책, 저장소 장애 대응이 필요하다.
세션 ID는 bearer credential이다. 세션 ID를 가진 쪽이 서버에서 인증된 사용자처럼 동작할 수 있기 때문이다.
공격 시나리오
공격자는 피해자에게 자신이 알고 있는 세션 ID를 쓰게 만들거나, 피해자의 세션 ID를 탈취하려 한다.
대표 흐름은 다음과 같다.
- 로그인 전 세션 ID가 로그인 후에도 그대로 유지된다.
- 공격자가 피해자에게 고정된
JSESSIONID를 심어 둔다. - 피해자가 로그인하면 같은 세션 ID에 인증 상태가 붙는다.
- 공격자는 그 세션 ID로 API를 호출한다.
또 다른 흐름은 쿠키 속성 누락이다.
Secure가 없어 평문 HTTP 구간에서 쿠키가 노출된다.HttpOnly가 없어 XSS로 세션 ID가 읽힌다.SameSite가 느슨해 CSRF 위험이 커진다.- URL에
;jsessionid=가 붙어 referrer와 로그에 남는다.
취약한 요청/응답 예시
HTTP/1.1 200 OK
Set-Cookie: JSESSIONID=abc123; Path=/
Content-Type: application/json
{"message":"login success"}이 쿠키에는 Secure, HttpOnly, SameSite가 없다.
GET /orders;jsessionid=abc123 HTTP/1.1
Host: app.example.comURL 기반 세션 추적이 허용되면 proxy log, browser history, referrer로 세션 ID가 새기 쉽다.
주제별 핵심 판단
- Session 인증은 서버 저장소의 session 상태와 브라우저 cookie가 함께 맞아야 성립한다.
- session fixation, idle timeout, concurrent session, logout 범위를 한 묶음으로 본다.
개선된 요청/응답 예시
세션 기반 인증에서 기본 원칙은 다음과 같다.
- 개선된 로그인 응답은 session id를 재발급하고
Secure,HttpOnly,SameSite속성을 명확히 설정한다. - logout 응답은 브라우저 cookie 삭제와 서버 session 무효화를 함께 수행해야 한다.
방어 설계
- 로그인 성공 시 세션 ID를 재발급한다.
- 세션 쿠키는
HttpOnly,Secure,SameSite를 명시한다. - URL rewriting 기반 세션 추적을 끈다.
- logout과 비밀번호 변경, MFA 해제, 권한 강등 시 기존 세션 폐기를 고려한다.
- 세션 저장소 장애를 인증 장애로 관측한다.
- 고위험 action은 오래된 세션이나 MFA 미검증 세션에 step-up을 요구한다.
세션 만료는 idle timeout과 absolute timeout을 나누어 생각한다. 사용자가 계속 활동해도 무한히 오래 유지되는 세션은 탈취 후 피해 기간이 길다.
Spring/HTTP 예시
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.sessionManagement(session -> session
.sessionFixation(fixation -> fixation.migrateSession())
.maximumSessions(3))
.logout(logout -> logout
.logoutUrl("/logout")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID"))
.build();
}쿠키 속성은 실행 환경에서 실제 응답으로 확인한다.
HTTP/1.1 200 OK
Set-Cookie: JSESSIONID=s%3Aopaque; Path=/; HttpOnly; Secure; SameSite=LaxSpring Boot 설정 예시는 다음처럼 둔다.
server.servlet.session.cookie.http-only=true
server.servlet.session.cookie.secure=true
server.servlet.session.cookie.same-site=lax
server.servlet.session.tracking-modes=cookie
server.servlet.session.timeout=30m관리자 기능이나 결제 기능처럼 CSRF 피해가 큰 상태 변경은 SameSite만 믿지 말고 CSRF token과 Origin 검증도 함께 본다.
운영 로그와 감사 지점
level=INFO event=session_issued result=success account_id=1004 session_id_hash=91ab auth_method=password mfa_verified=false ip=203.0.113.10 trace_id=3f21cc09level=WARN event=session_rejected result=blocked session_id_hash=91ab reason=session_fixation_suspected old_session_seen=true ip=203.0.113.20 trace_id=3f21cc0alevel=INFO event=session_revoked result=success account_id=1004 reason=password_changed revoked_count=4 trace_id=3f21cc0b세션 ID 원문은 로그에 남기지 않는다. hash나 내부 session record id로 추적한다.
실전 판단 기준
- 로그인 전후 세션 ID가 바뀌는가?
- cookie 삭제만으로 서버 session이 사라졌다고 가정하지 않는다.
- 권한 변경과 비밀번호 변경 후 기존 session을 유지할지 폐기할지 정책을 확인한다.
테스트 포인트
Set-Cookie에HttpOnly,Secure,SameSite가 있는가?;jsessionid=URL 추적이 비활성화되어 있는가?- logout 후 같은 세션 ID로 API 호출 시 401인가?
- 비밀번호 변경, MFA 해제, 권한 강등 후 기존 세션이 어떻게 처리되는가?
- 세션 저장소 장애 시 500으로 흘리지 않고 인증 장애와 알림으로 관측되는가?
위험 신호!
- 로그인 성공 후 세션 ID가 그대로 유지된다.
- 세션 쿠키에
Secure또는HttpOnly가 없다. - URL에
JSESSIONID가 나타난다. - 로그에 session id 원문이 남는다.
- 권한 변경 후 오래된 세션이 계속 이전 권한을 가진다.
- 세션 저장소 장애가 일반 DB 장애와 구분되지 않는다.
확인 질문
확인 질문
- session fixation 방지의 핵심은 무엇인가?
- 로그인 성공 시 기존 세션 ID를 새 세션 ID로 바꾸어 공격자가 미리 아는 ID에 인증 상태가 붙지 않게 하는 것이다.
- 세션 ID를 bearer credential로 보는 이유는 무엇인가?
- 세션 ID를 가진 쪽이 서버에서 인증된 사용자처럼 요청할 수 있기 때문이다.
- 세션 기반 인증에서 운영 로그에 남겨야 할 값은 무엇인가?
- session id 원문이 아니라 session id hash, account id, 발급/폐기 이유, MFA 상태, IP, trace id를 남긴다.