이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- Cookie는 인증 상태를 어떻게 저장하고 자동 전송하는가?
HttpOnly,Secure,SameSite,Domain,Path,__Host-prefix는 각각 어떤 위험을 줄이는가?- 쿠키 기반 인증에서 XSS와 CSRF를 어떻게 함께 고려해야 하는가?
개요
Cookie는 브라우저가 저장하고 조건이 맞으면 요청에 자동으로 붙이는 HTTP 상태 관리 수단이다.
세션 기반 인증에서는 쿠키에 세션 ID가 들어간다. JWT 기반 설계에서도 refresh token을 HttpOnly Cookie에 넣는 경우가 많다.
중요한 점은 Cookie가 인증 그 자체가 아니라 전달 수단이라는 것이다. Cookie가 안전하려면 값의 수명, scope, 자동 전송, JavaScript 접근 가능성, 서버 폐기 정책이 함께 맞아야 한다.
공격 시나리오
서비스가 app.example.com과 blog.example.com을 같이 운영한다고 가정한다.
인증 쿠키가 다음처럼 발급된다.
Set-Cookie: SESSION=opaque; Domain=.example.com; Path=/; SameSite=Noneblog.example.com에 XSS나 subdomain takeover가 생기면 공격자는 같은 상위 도메인의 cookie scope를 악용할 수 있다.
또 SameSite=None이면 cross-site 요청에 cookie가 자동 전송된다. 상태 변경 API가 CSRF token이나 Origin 검증 없이 동작하면 공격자는 피해자의 브라우저로 요청을 보낼 수 있다.
Cookie는 자동으로 보내지기 때문에 편리하지만, 바로 그 자동 전송이 공격 경로가 된다.
취약한 요청/응답 예시
HTTP/1.1 200 OK
Set-Cookie: SESSION=abc123; Domain=.example.com; Path=/; SameSite=None
Set-Cookie: refresh_token=raw-refresh; Path=/; SameSite=None
Content-Type: application/json
{"message":"login success"}문제는 여러 개다.
SESSION에HttpOnly와Secure가 없다.Domain=.example.com으로 모든 서브도메인에 전송된다.- refresh token이
HttpOnly없이 JavaScript에서 읽힐 수 있다. SameSite=None인데 CSRF 방어가 문서화되어 있지 않다.
주제별 핵심 판단
| 속성 | 줄이는 위험 | 주의점 |
|---|---|---|
HttpOnly | XSS로 JavaScript가 cookie를 읽는 위험 | XSS 요청 전송 자체를 막지는 못한다 |
Secure | HTTP 평문 전송 위험 | HTTPS 강제와 HSTS를 함께 본다 |
SameSite | cross-site 자동 전송 위험 | CSRF의 유일한 방어선으로 두면 약하다 |
Domain | cookie가 전달될 host 범위 | 넓게 잡으면 서브도메인에 흘러간다 |
Path | cookie가 전달될 path 범위 | 보안 경계라기보다 노출 범위 축소 장치다 |
Max-Age | 지속 시간 | 긴 수명은 탈취 피해 기간을 늘린다 |
__Host- prefix는 session cookie에 유용하다. Secure, Path=/, Domain 없음 조건을 강제해 cookie scope를 좁히는 데 도움을 준다.
개선된 요청/응답 예시
세션 쿠키는 가능하면 host-only cookie로 둔다.
- 개선된
Set-Cookie는Secure,HttpOnly,SameSite,Path를 명시하고 인증 cookie 범위를 좁힌다. - 민감 작업 응답은 cache를 금지하고, session 재발급 여부를 로그로 확인할 수 있어야 한다.
방어 설계
Set-Cookie: __Host-SESSION=opaque; Path=/; HttpOnly; Secure; SameSite=Laxrefresh token cookie는 endpoint 범위를 좁힐 수 있다.
Set-Cookie: refresh_token=opaque; Path=/api/auth/refresh; HttpOnly; Secure; SameSite=Strict; Max-Age=1209600쿠키 기반 인증의 방어는 다음을 함께 둔다.
- HTTPS와 HSTS
HttpOnly,Secure, 적절한SameSite- 불필요한
Domain제거 - 민감 상태 변경에 CSRF token 또는 Origin 검증
- login/logout/password change에서 cookie 재발급과 폐기
- access log에서 cookie 값 마스킹
SameSite=Lax가 많은 기본 흐름에 도움이 되지만, 결제, 권한 변경, 이메일 변경 같은 민감 action에는 별도 CSRF 방어를 둔다.
Spring/HTTP 예시
server.servlet.session.cookie.name=__Host-SESSION
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쿠키 기반 session 인증에서 CSRF를 끄는 것은 신중해야 한다.
@Bean
SecurityFilterChain cookieSessionSecurity(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
.sessionManagement(session -> session
.sessionFixation(fixation -> fixation.migrateSession()))
.build();
}CSRF token cookie는 JavaScript가 읽어 header로 다시 보내야 하므로 HttpOnly=false가 필요할 수 있다. 이 cookie는 인증 cookie와 역할이 다르다는 점을 분리해서 설명해야 한다.
운영 로그와 감사 지점
level=WARN event=cookie_scope_risk_detected result=blocked cookie=SESSION reason=domain_too_wide domain=.example.com path=/ trace_id=79d120aalevel=INFO event=session_cookie_issued result=success account_id=1004 cookie_name=__Host-SESSION same_site=Lax secure=true http_only=true trace_id=79d120ab로그에는 cookie 값 원문을 남기지 않는다. 값 대신 cookie 이름, 속성, 발급/폐기 이벤트, session id hash를 남긴다.
실전 판단 기준
- 로그인 응답의
Set-Cookie에HttpOnly,Secure,SameSite가 있는가? - cookie 속성은 브라우저 전송 조건을 바꾸는 것이지 서버 권한 검사를 대체하지 않는다.
- 도메인과 path 범위가 넓을수록 하위 애플리케이션 취약점의 영향도 커진다.
테스트 포인트
- 인증 cookie에 불필요한
Domain이 없는가? __Host-prefix 조건을 실제 브라우저가 받아들이는가?- 상태 변경 API가 cookie 자동 전송만으로 성공하지 않고 CSRF 방어를 요구하는가?
- access log, reverse proxy log, APM에 cookie 값 원문이 남지 않는가?
- logout 후 cookie 삭제와 서버 세션 폐기가 모두 일어나는가?
위험 신호!
Domain=.example.com을 기본값처럼 쓴다.- 인증 cookie에
HttpOnly또는Secure가 없다. - refresh token cookie가 모든 path에 전송된다.
SameSite=None인데 CSRF 방어가 없다.- URL rewriting으로
jsessionid가 노출된다. - 로그에
Cookieheader 전체가 남는다.
확인 질문
확인 질문
HttpOnly가 막는 것과 막지 못하는 것은 무엇인가?
- JavaScript가 cookie 값을 읽는 것은 줄이지만, XSS가 사용자의 브라우저에서 요청을 보내는 것 자체는 막지 못한다.
Domain을 넓게 잡으면 왜 위험한가?
- 의도하지 않은 서브도메인에도 인증 cookie가 전송되어 subdomain takeover나 XSS 피해 범위가 커지기 때문이다.
- 쿠키 기반 인증에서 CSRF를 따로 보는 이유는 무엇인가?
- 브라우저가 cookie를 자동 전송하므로 공격자가 피해자의 브라우저를 이용해 상태 변경 요청을 보낼 수 있기 때문이다.