Cookie 보안 플래그 완전 가이드|Secure / HttpOnly / SameSite / __Host-
웹 애플리케이션의 세션 관리에 가장 많이 사용되는 쿠키이지만, 올바르게 설정하지 않으면 <strong>세션 하이재킹</strong> (session hijacking), <strong>CSRF</strong> (cross-site request forgery), <strong>XSS</strong>를 통한 토큰 탈취 같은 공격을 초래합니다. 본 글에서는 쿠키에 추가해야 할 4가지 주요 플래그 (<code>Secure</code> / <code>HttpOnly</code> / <code>SameSite</code> / <code>__Host-</code> 접두사)의 의미와 구현 시 주의할 점을 정리합니다.
Set-Cookie 헤더 구조
서버가 브라우저에 쿠키를 설정할 때 HTTP 응답에 다음과 같은 헤더를 반환합니다.
Set-Cookie: session_id=abc123; Path=/; Domain=example.com; Expires=Wed, 22 Apr 2026 10:00:00 GMT; Secure; HttpOnly; SameSite=Lax
세미콜론으로 구분된 복수의 속성이 있지만, 크게 나누면 다음 2 종류가 있습니다.
- <strong>스코프 속성</strong>: Path / Domain / Expires / Max-Age — Cookie가 언제 어디로 전송되는지 결정
- <strong>보안 속성</strong>: Secure / HttpOnly / SameSite — 전송/접근 제한
Secure — HTTPS로만 전송
<code>Secure</code> 속성이 있는 쿠키는 브라우저에서 <strong>HTTPS 연결일 때만</strong> 서버로 전송됩니다. 없으면 피해자가 <code>http://</code>로 사이트에 접근할 때 쿠키가 평문으로 전송되어 중간자 공격(Man-in-the-Middle)으로 세션 ID가 탈취될 수 있습니다.
<strong>철칙:</strong> 인증 관련 쿠키에는 반드시 붙이세요. 로컬 개발은 localhost이므로 HTTPS가 아니어도 Secure 쿠키가 전송되지만, 본번의 HTTPS를 전제로 하면 문제없습니다.
HttpOnly — JavaScript에서 접근 불가
<code>HttpOnly</code>를 붙이면 <code>document.cookie</code>로 읽을 수 없게 됩니다. 이로 인해 XSS 공격으로 삽입된 JavaScript가 Cookie를 도용할 수 없게 됩니다.
XSS는 여전히 발생 가능한 취약점이므로 세션 쿠키에는 필수로 생각하세요. JavaScript 측에서 쿠키 값을 읽을 필요가 있는 경우(예: CSRF 토큰), <strong>읽기 전용의 별도 쿠키를 만드는</strong> 것이거나 <strong><meta> 태그를 통해</strong> 값을 전달하는 것이 원칙입니다.
SameSite — 크로스사이트 요청에서의 전송 제어
SameSite 속성은 CSRF 대책의 핵심입니다. 값은 다음의 3가지입니다:
| 값 | 동작 | CSRF 방어 |
|---|---|---|
Strict | 크로스사이트 요청에는 전혀 보내지 않음 (외부 링크에서 올 때도) | 최강 |
| <code>Lax</code> (기본값) | 최상위 GET 네비게이션에서만 전송 (링크 클릭 OK, POST 폼 NG) | 강함 |
None | 모든 크로스사이트 요청으로 전송 (Secure 필수) | 없음 |
2020년 이후의 모던 브라우저는 지정되지 않은 SameSite를 <code>Lax</code>로 취급하므로 명시하지 않아도 최소한의 CSRF 보호를 얻을 수 있습니다. 하지만 의도를 명확히 하기 위해 명시적으로 설정하는 것이 좋습니다.
SSO 연동이나 iframe 임베드에서 <code>SameSite=None</code>을 사용할 경우, <strong>반드시 Secure도 함께 설정</strong>해야 하는 것이 현대 브라우저의 요구사항입니다. Secure가 없는 <code>SameSite=None</code> 쿠키는 브라우저에 의해 거부됩니다.
__Host- / __Secure- 접두사
Cookie 이름이 <code>__Host-</code>로 시작하는 경우 브라우저는 다음 3 가지 조건을 강제합니다.
- <code>Secure</code> 플래그 필수
- <code>Domain</code> 속성을 붙여서는 안 됩니다 (=요청을 보낸 정확한 호스트만)
- <code>Path=/</code>이어야 함
이들은 「다른 서브도메인에서 덮어쓸 수 없음」「Host 헤더 위조로 임의로 쿠키를 설정할 수 없음」이라는 강력한 보장을 제공합니다. 세션 쿠키에는 <code>__Host-session</code>과 같이 프리픽스를 붙이는 것이 가장 견고합니다.
다른 <code>__Secure-</code> 접두사는 필수 Secure 플래그만 적용합니다 (Domain / Path 제약 없음).
4096 바이트 제한
Cookie의 value는 RFC 6265에서 <strong>총 4096 바이트</strong>로 권장됩니다. 큰 JSON이나 배열을 Cookie에 담으면 이를 초과할 수 있으며, 브라우저가 조용히 잘라낼 수 있습니다. 큰 데이터는 서버 사이드 세션에 두고, Cookie에는 세션 ID만 넣는 것이 정석입니다.
구현 예시: 인증 세션 쿠키
올바르게 설정된 인증 쿠키는 다음과 같은 형태입니다.
Set-Cookie: __Host-session=eyJ0eXAi...; Path=/; Max-Age=3600; Secure; HttpOnly; SameSite=Lax
- <code>__Host-</code>: 서브도메인 오염 및 호스트 위장 방지
- <code>Path=/</code>: 사이트 전체에서 접근 가능
- <code>Max-Age=3600</code>: 1시간 후 만료
- <code>Secure</code>: HTTPS를 통해서만 전송
- <code>HttpOnly</code>: JavaScript에서 접근 불가
- <code>SameSite=Lax</code>: 크로스사이트 POST에서 전송되지 않음 (CSRF 보호)
PHP (Laravel) 예제
// config/session.php
return [
'secure' => true, // Secure フラグ
'http_only' => true, // HttpOnly フラグ
'same_site' => 'lax', // SameSite=Lax
'path' => '/',
'cookie' => '__Host-session',
];
Node.js (Express) 예제
const session = require('express-session');
app.use(session({
name: '__Host-session',
secret: process.env.SESSION_SECRET,
cookie: {
secure: true,
httpOnly: true,
sameSite: 'lax',
path: '/',
maxAge: 3600 * 1000,
},
}));
기존 사이트의 Cookie 설정을 확인하는 방법
<a href="/ja/check/cookies/">DevLab의 쿠키 검사 도구</a>를 사용하면 자신의 사이트나 타사 사이트의 쿠키가 올바르게 설정되었는지 즉시 확인할 수 있습니다. URL을 입력하기만 하면 반환된 모든 Set-Cookie 헤더를 분석하고 다음과 같은 진단을 표시합니다.
- 각 Cookie의 Secure / HttpOnly / SameSite 유무
- SameSite=None인데 Secure가 없는 등의 위반
- __Host- 접두사의 일관성
- 4096 바이트 초과 경고
- 전체 요약 (Secure 비율 / HttpOnly 비율 / SameSite 분포)
요약
Cookie 보안 플래그는 임의로 설정하는 것이 아니라, 공격 시나리오와 대응 방안을 이해한 후에 설정하는 것이 중요합니다. 최소한 프로덕션 환경의 인증 세션 Cookie에는 <strong>Secure + HttpOnly + SameSite + __Host- 접두사 + 짧은 Max-Age</strong>를 붙여야 합니다. 기존 사이트의 설정 재검토에는 <a href="/ja/check/cookies/">Cookie 검사 도구</a>의 사용을 권장합니다.