세션 고정은 피해자가 공격자가 준비한 ID로 로그인하게 되는 공격입니다. 동작 원리와 확실하게 막는 방법을 설명합니다(공격 절차 없음).
왜 통하는가
원인은 세션 ID가 로그인 전후로 바뀌지 않는 설계입니다. 바뀌지 않는다면, 공격자가 로그인 전에 알던 ID가 로그인 후에도 "인증됨"으로 여전히 작동합니다. 흔한 오해는 "로그인하면 자동으로 새 세션이 생긴다"는 것이지만 — 많은 프레임워크는 명시적으로 재생성을 호출하지 않으면 같은 ID를 그대로 이어 씁니다.
CSRF 나 XSS 와 짝지을 수도 있지만, 고정 자체의 본질은 한 가지입니다. 로그인 시 ID가 재생성되지 않았다입니다.
일본 IPA(정보처리추진기구)의 가이드 "안전한 웹사이트 만드는 법"은 이를 부적절한 세션 관리의 한 형태로 다루며, 로그인 기능이 있는 모든 웹사이트가 주의해야 한다고 말합니다. 근본적인 해결책은 로그인 성공 후 새 세션을 시작하는 것입니다.
고정 vs 하이재킹 vs CSRF
이 셋은 헷갈리기 쉽습니다. 모두 로그인 상태를 악용하지만, 공격자가 무엇을 가지고 있는지와 어떤 방어가 통하는지가 다릅니다.
| 항목 | 세션 고정 | 세션 하이재킹 | CSRF |
|---|---|---|---|
| 공격자가 가진 것 | 직접 준비한 ID(처음부터 알고 있음) | 훔친, 이미 발급된 ID | ID는 전혀 없음 |
| 피해자에게 일어나는 일 | 심어 둔 ID로 로그인한다 | 발급된 ID를 도난당한다 | 의도하지 않은 요청을 보내게 된다 |
| 주된 방어 | 로그인 시 ID 재생성 | HttpOnly/Secure, XSS 방지, 짧은 유효 기간 | CSRF 토큰, SameSite |
고정은 로그인 시 ID 재생성이라는 한 가지 조치로 근본적으로 고쳐지는 공격입니다.
방어: 로그인 시 ID를 재생성하라
로그인 성공 시 세션 ID 재생성 (가장 중요)
URL을 통한 세션 ID를 받지 않기
쿠키 플래그 설정
HttpOnly(JS가 읽을 수 없음), Secure(HTTPS만), SameSite(교차 사이트 전송 억제)를 설정하세요. 관련 하이재킹 경로도 함께 약화시킵니다.세션을 합리적으로 만료시키기
OWASP의 Session Management Cheat Sheet는 권한이 바뀔 때마다 ID를 재생성하고, 서버가 직접 생성한 ID만 받는 "strict(엄격)" 방식을 쓰라고 권장합니다. 밖에서 들어온 모르는 ID를 그대로 채택하는 "permissive(허용)" 방식은 공격자가 고른 ID가 받아들여질 여지를 남깁니다.
주요 프레임워크의 기본 동작
대부분의 프레임워크는 표준 로그인 기능을 쓰면 ID를 자동으로 재생성합니다. 위험은 로그인을 직접 작성하거나 기본 보호를 껐을 때 생깁니다.
| 환경 | 기본 동작 | 직접 작성할 때 확인할 것 |
|---|---|---|
| PHP(순수 세션) | 스스로 재생성하지 않음. session.use_strict_mode 기본값은 0 | 로그인 성공 시 session_regenerate_id(true) 호출. use_strict_mode를 1로 |
| Laravel | 스타터 킷이나 Fortify를 쓰면 인증 시 자동 재생성 | 로그인을 직접 작성한다면 $request->session()->regenerate() 호출 |
| Spring Security | 기본으로 켜져 있음(Servlet 3.1 이상에서 changeSessionId) | 누군가 sessionFixation().none()을 설정하지 않았는지 확인 |
PHP에서는 세션 ID를 URL로 전달하는 설정(session.use_trans_sid 활성화, session.use_only_cookies 비활성화)이 PHP 8.4에서 비권장(deprecated)이 되었습니다. 오래된 설정 파일을 그대로 가져와 쓴다면 점검하세요.
내 사이트에서 확인하는 방법
관리하는 사이트에서 다음 네 가지를 확인하세요. 개발자 도구만 있으면 됩니다.
- 개발자 도구의 "Application"(Firefox는 "Storage")에서 세션 쿠키 값을 적어 두고, 로그인 후 다시 봅니다. 바뀌지 않았다면 로그인 시 ID가 재생성되지 않는 것입니다
- 로그인 페이지와 로그인 후 URL에
PHPSESSID=나;jsessionid=같은 세션 ID가 붙어 있지 않은지 확인합니다 - 같은 화면에서 세션 쿠키에
HttpOnly,Secure,SameSite가 있는지 확인합니다 - 로그아웃한 뒤 예전 쿠키 값을 되돌려 넣고 새로고침해, 다시 로그인된 상태가 되지 않는지 확인합니다
쿠키 속성은 소유를 확인한 사이트를 검사하는 사이트 보안 점검에서도 확인할 수 있습니다. 1번 확인을 로그인 흐름을 손볼 때마다 도는 자동 테스트로 만들어 두면, 재생성이 빠졌을 때 바로 알아챌 수 있습니다.
본 사이트의 견해: 프레임워크의 '재생성'을 끄지 마라
세션 고정은 정교한 맞춤 코드가 아니라 한 가지 조치(로그인 시 ID 재생성)만으로 대부분 막을 수 있습니다. 핵심은 프레임워크가 이미 제공하는 그 메커니즘을 실수로 끄거나 호출하기를 잊지 않는 것입니다. 검증된 구현의 안전한 기본값을 쓰는 것이 직접 세션 관리를 짜는 것보다 낫습니다. 본 사이트는 "인증/키 처리를 직접 만들지 말 것, 안전한 기본값을 끄지 말 것"을 지킵니다. IDOR 와 마찬가지로, 인증/인가는 모든 요청에서 메커니즘으로 강제되어야 합니다.
출처(1차 자료)
- IPA, "안전한 웹사이트 만드는 법" 1.4 세션 관리의 부적절 — ipa.go.jp(로그인 후 새 세션 시작, ID를 URL에 넣지 않기, 일본어)
- OWASP Session Management Cheat Sheet — cheatsheetseries.owasp.org(권한 변경 시 재생성, strict 방식, 쿠키 속성)
- MITRE CWE-384: Session Fixation — cwe.mitre.org
- PHP Manual, "Runtime Configuration"(세션) — php.net(
use_strict_mode등의 기본값, 8.4의 비권장 항목) - Laravel 문서, "HTTP Session" — laravel.com / Spring Security, "Authentication Persistence and Session Management" — docs.spring.io
다음으로 읽기
- 용어집: CSRF란 · XSS란 · IDOR이란 · JWT란
- 기초: 인증과 인가의 차이