쿠키 톺아보기 (1) — 동작 원리와 옵션, 인증에 쓰이는 이유
HTTP 쿠키의 등장 배경과 동작 흐름, 주요 옵션(HttpOnly/Secure/SameSite), 그리고 왜 쿠키가 인증 토큰 운반 수단의 표준이 되었는지 정리합니다.
들어가며
document.cookie를 출력해보거나 개발자 도구의 Application 탭에서 쿠키 목록을 확인해본 적은 있지만, 막상 “쿠키가 정확히 어떤 흐름으로 동작하고, 왜 옵션이 이렇게 많은가”를 한 줄로 설명하기는 의외로 어렵다. 쿠키는 30년 가까이 자리를 지킨 기술이지만, 그동안 보안 위협이 누적되면서 옵션과 정책도 함께 복잡해졌다.
이 글에서는 쿠키가 등장한 배경부터 요청·응답 흐름, 주요 옵션, 그리고 왜 쿠키가 인증 토큰을 운반하는 표준 수단으로 자리잡았는지까지 정리한다.
쿠키란 무엇인가
정의
쿠키(Cookie) 는 웹 서버가 사용자의 브라우저에 저장하도록 지시하는 작은 텍스트 데이터다. 한 쌍의 키와 값에 여러 옵션이 붙는 구조다.
sessionId=abc123xyz; Domain=example.com; Path=/; Secure; HttpOnly
왜 등장했는가
HTTP는 본래 stateless 프로토콜이다. 각 요청은 서로 독립적이며, 서버는 “방금 요청한 사람”과 “지금 요청한 사람”이 동일한지 알 방법이 없다. 사용자가 로그인했는지, 어떤 상품을 장바구니에 담았는지조차 매 요청마다 다시 알려줘야 한다.
1994년 Netscape가 이 문제를 해결하기 위해 쿠키를 도입했다. 서버가 응답에 작은 데이터 조각을 실어 보내면, 브라우저가 이를 저장했다가 이후 모든 요청에 자동으로 함께 보내주는 방식이다. 덕분에 서버는 별도 식별 로직 없이도 “이 요청은 그 사용자의 것”임을 알 수 있게 됐다.
동작 흐름
쿠키는 두 개의 HTTP 헤더로만 동작한다. 서버가 응답에 넣는 Set-Cookie와, 브라우저가 요청에 자동으로 붙이는 Cookie다.
1) 브라우저 → 서버: 첫 요청
GET /login HTTP/1.1
2) 서버 → 브라우저: 응답에 Set-Cookie 헤더 포함
HTTP/1.1 200 OK
Set-Cookie: sessionId=abc123; HttpOnly; Secure
3) 브라우저: 쿠키를 도메인별로 분리해 저장
4) 브라우저 → 서버: 이후 같은 도메인 요청에 자동 첨부
GET /mypage HTTP/1.1
Cookie: sessionId=abc123
핵심은 저장과 첨부가 모두 자동이라는 점이다. 개발자가 직접 헤더를 만들거나 매 요청마다 토큰을 붙일 필요가 없다. 링크 클릭, 폼 제출, 이미지 로드 같은 브라우저의 기본 동작에도 쿠키는 함께 실려 나간다.
주요 옵션
쿠키는 옵션의 조합으로 동작이 크게 바뀐다. 각 옵션이 무엇을 통제하는지 정확히 알아야 적절한 설정을 고를 수 있다.
| 옵션 | 설명 | 예시 |
|---|---|---|
| Name=Value | 쿠키의 키와 값 (필수) | sessionId=abc123 |
| Domain | 쿠키가 전송될 도메인. 미지정 시 현재 도메인만 적용 | Domain=example.com |
| Path | 쿠키가 전송될 경로 범위 | Path=/api |
| Expires | 만료 일시 (절대 시각, GMT) | Expires=Wed, 21 Oct 2026 07:28:00 GMT |
| Max-Age | 만료까지 남은 시간(초). Expires보다 우선 | Max-Age=3600 |
| Secure | HTTPS 연결에서만 전송 | Secure |
| HttpOnly | JavaScript(document.cookie)로 접근 불가. XSS 방어 | HttpOnly |
| SameSite | 크로스 사이트 요청 시 전송 정책 (CSRF 방어) | SameSite=Lax |
| Partitioned | 서드파티 쿠키를 사이트별로 분리 저장 (CHIPS) | Partitioned |
Domain과 Path
Domain과 Path는 쿠키의 전송 범위를 결정한다. 브라우저는 요청 URL이 쿠키의 도메인·경로 조건에 부합할 때만 해당 쿠키를 첨부한다.
Domain을 명시하지 않으면 쿠키는 설정한 도메인에서만 유효하다. Domain=example.com처럼 명시하면 api.example.com, cdn.example.com 같은 모든 서브도메인에도 전송된다. 범위가 넓을수록 누출 위험도 커지므로, 굳이 공유가 필요 없다면 생략하는 편이 안전하다.
Path는 특정 경로 이하로 전송 범위를 좁힌다. 예를 들어 Path=/api로 설정하면 /api/* 요청에만 쿠키가 첨부되고, /static/*이나 /images/* 요청에는 첨부되지 않는다. 이 옵션은 단순히 보안적 의미뿐 아니라 네트워크 비용과 CDN 캐시 효율에도 직접적인 영향을 준다.
Expires와 Max-Age
쿠키의 수명을 결정하는 옵션이다. 둘 중 하나라도 명시되어 있으면 Persistent Cookie가 되어 디스크에 저장되고, 둘 다 없으면 Session Cookie가 되어 브라우저가 종료될 때 사라진다.
Expires는 절대 시각, Max-Age는 상대 시간(초)이다. 두 옵션이 모두 있다면 Max-Age가 우선한다. 시간 동기화 이슈에서 자유롭기 때문에 최신 코드는 대부분 Max-Age를 사용한다.
Secure
이 플래그가 붙은 쿠키는 HTTPS 연결에서만 전송된다. HTTP 평문으로는 절대 노출되지 않으므로, 중간자 공격으로 인한 탈취를 막을 수 있다.
인증과 관련된 쿠키라면 사실상 필수 옵션이다. 또한 뒤에서 다룰 SameSite=None을 사용할 때는 브라우저가 Secure를 함께 요구한다.
HttpOnly
HttpOnly가 붙은 쿠키는 JavaScript에서 일체 접근할 수 없다. document.cookie로 읽으려 해도 보이지 않고, 존재 여부조차 알 수 없다.
이 옵션이 중요한 이유는 XSS(Cross-Site Scripting) 공격으로부터 인증 토큰을 보호하기 위함이다. 페이지에 악성 스크립트가 주입되더라도, HttpOnly 쿠키는 스크립트가 훔쳐갈 수 없다.
[XSS 발생 시 저장 위치별 위험도]
- localStorage → 즉시 탈취 가능
- HttpOnly 쿠키 아님 → 즉시 탈취 가능
- HttpOnly 쿠키 → 탈취 불가능
OWASP가 “인증 토큰은 절대 localStorage에 저장하지 말 것”을 권고하는 이유가 여기에 있다.
SameSite
가장 최근에 추가된 옵션이자, 가장 큰 변화를 가져온 옵션이다. 크로스 사이트 요청에서 쿠키를 어떻게 다룰지 결정한다.
| 값 | 동작 | 사용 사례 |
|---|---|---|
| Strict | 같은 사이트 요청에만 전송. 외부 링크 클릭으로 진입해도 미전송 | 금융, 결제 등 민감 작업 |
| Lax (기본값) | 같은 사이트 + 안전한 top-level 네비게이션(GET)에만 전송 | 일반 로그인 세션 |
| None | 모든 크로스 사이트 요청에 전송. 반드시 Secure 필수 | 서드파티 임베드, 외부 위젯 |
2020년 Chrome 80부터 SameSite의 기본값이 Lax로 바뀌었다. 명시하지 않으면 외부 사이트에서 발생한 요청에는 쿠키가 첨부되지 않는다. 이 변경 하나만으로도 상당수의 CSRF 공격이 자동으로 차단됐다.
Partitioned
크롬을 비롯한 브라우저들이 추적 방지를 위해 서드파티 쿠키를 단계적으로 폐지하면서, 정상적인 사용 사례를 살리기 위해 도입된 옵션이다. 같은 위젯이라도 임베드된 사이트별로 별개의 쿠키 저장소를 갖게 만들어, 사이트 간 추적은 막으면서도 필요한 컨텍스트는 유지한다.
이 옵션은 CHIPS(Cookies Having Independent Partitioned State) 라는 명세로 표준화되어 있다.
쿠키의 종류
쿠키는 두 가지 축으로 분류할 수 있다.
지속 기간 기준
- Session Cookie:
Expires/Max-Age가 없는 쿠키. 브라우저 종료 시 자동 삭제된다. - Persistent Cookie: 만료 시간이 명시된 쿠키. 시간이 지나거나 사용자가 직접 삭제할 때까지 유지된다.
출처 기준
- First-party Cookie: 현재 방문 중인 사이트가 직접 설정한 쿠키. 로그인 세션, 사용자 설정 등이 여기에 해당한다.
- Third-party Cookie: 외부 도메인(광고, 분석 SDK 등)이 설정한 쿠키. 사이트 간 추적에 사용될 수 있어 최근 브라우저들이 차단 추세다. Safari는 이미 기본 차단했고, Chrome도 단계적 폐지를 진행 중이다.
왜 인증에 쿠키를 쓰는가
쿠키의 용도는 여러 가지가 있지만, 그중에서도 인증은 가장 중요하고 가장 까다로운 사용 사례다. 굳이 쿠키를 쓰는 이유는 단순한 관습이 아니라, 쿠키가 인증 정보 운반에 적합하도록 설계됐기 때문이다.
① 브라우저가 자동으로 첨부한다
로그인 성공 후 서버가 세션 쿠키를 발급하면, 이후 사용자가 어떤 페이지를 열든 어떤 API를 호출하든 브라우저가 알아서 Cookie 헤더에 담아 전송한다.
이게 왜 중요한가? <a> 링크 클릭, <form> 제출, 이미지 로드, SSR 페이지 진입 같은 브라우저의 기본 동작도 자동으로 인증된 요청이 되기 때문이다. localStorage에 토큰을 두고 Authorization 헤더로만 보내는 방식은 이런 네비게이션 요청에 토큰이 붙지 않아, SSR이나 직접 링크 진입 시 인증이 끊긴다.
② HttpOnly로 스크립트 접근을 원천 차단할 수 있다
앞서 설명했듯, HttpOnly 쿠키는 JavaScript가 일체 접근할 수 없다. XSS가 발생해도 공격자가 인증 토큰을 가져갈 수 없다.
| 저장 위치 | XSS 발생 시 |
|---|---|
localStorage | 즉시 탈취 가능 |
HttpOnly 쿠키 | 탈취 불가능 (차단됨) |
③ Secure와 SameSite로 전송 자체를 통제할 수 있다
Secure는 HTTPS에서만 전송을 보장해 평문 노출을 차단하고, SameSite=Lax/Strict는 외부 사이트가 위조한 요청에 쿠키가 실려 가지 않도록 막아준다. 특히 SameSite는 쿠키의 가장 큰 약점이었던 CSRF를 브라우저 차원에서 방어해주는 결정적인 장치다.
④ 서버가 세션을 즉시 무효화할 수 있다
세션 쿠키의 일반적인 구조는 다음과 같다.
[브라우저] [서버]
sessionId=abc123 ←→ 세션 저장소(Redis 등)
abc123 → { userId: 42, role: "admin" }
쿠키에는 식별자만 담고, 실제 사용자 정보는 서버 측 저장소에 둔다. 서버가 저장소에서 해당 세션을 삭제하면 즉시 로그아웃된다.
| 방식 | 강제 로그아웃 |
|---|---|
| 세션 쿠키 | 서버에서 세션 삭제 → 즉시 무효 |
| JWT(stateless) | 만료 시간까지 유효. 블랙리스트를 별도 구현해야 함 |
비밀번호 변경, 계정 도용 의심, 권한 회수 같은 상황에서 즉시성은 보안상 매우 중요하다.
⑤ 도메인/경로 단위로 정밀하게 범위를 제한할 수 있다
Domain과 Path로 어디까지 전송할지를 세밀하게 통제할 수 있다. 토큰이 불필요한 정적 자원이나 외부 도메인으로 새어 나가는 것을 방지할 수 있다.
대안과의 비교
쿠키 외에도 인증 정보를 다룰 수 있는 방법은 여럿 있다. 각각 어떤 한계가 있는지 정리해보자.
| 방식 | 한계 |
|---|---|
Authorization 헤더 + localStorage | XSS에 취약, 링크·SSR 요청에 자동 첨부 안 됨, 새 탭에서 직접 접근 불가 |
| URL에 토큰 포함 | 브라우저 히스토리·서버 로그·Referer 헤더로 유출, 공유 시 노출 |
| HTTP Basic 인증 | 매 요청마다 자격증명을 평문에 가까운 형태로 전송, 로그아웃 어려움, UX 열악 |
| IP 기반 | 모바일·공유망에서 IP가 자주 바뀜, 보안성 낮음 |
쿠키는 이 모든 단점을 한 번에 해결한다. 자동 전송, 스크립트 차단, 전송 제어, 서버측 즉시 무효화 — 이 네 박자가 모두 갖춰져 있다.
클라이언트 저장 기술과의 비교
쿠키와 자주 비교되는 다른 저장 기술도 정리해두자.
| 기술 | 용량 | 자동 전송 | 만료 | 주 용도 |
|---|---|---|---|---|
| Cookie | ~4KB | O | 설정 가능 | 세션, 인증 |
| localStorage | ~5–10MB | X | 영구 | 클라이언트 캐시 |
| sessionStorage | ~5–10MB | X | 탭 종료 시 | 일회성 데이터 |
| IndexedDB | 수십~수백 MB | X | 영구 | 대용량 구조화 데이터 |
용량만 보면 쿠키가 압도적으로 작지만, 자동 전송이라는 특성 덕분에 인증 토큰 운반에는 쿠키가 가장 적합하다.
보안 권장 설정
지금까지의 내용을 종합해서, 로그인 세션 쿠키에 권장하는 설정은 다음과 같다.
Set-Cookie: sessionId=xxx;
HttpOnly;
Secure;
SameSite=Lax;
Path=/;
Max-Age=86400
각 옵션이 막아주는 위협은 다음과 같이 정리할 수 있다.
| 위협 | 방어 옵션 |
|---|---|
| XSS (스크립트로 쿠키 탈취) | HttpOnly |
| CSRF (위조 요청) | SameSite=Lax 또는 Strict |
| 중간자 공격 (HTTP 평문 노출) | Secure |
| 세션 고정 | 로그인 성공 시 세션 ID 재발급 |
| 불필요한 도메인 누출 | Domain 범위 최소화 |
마무리
쿠키는 단순한 키–값 저장소처럼 보이지만, 30년에 걸쳐 누적된 옵션 하나하나가 모두 의미 있는 보안·전송 정책을 담고 있다. 핵심을 다시 정리하면 다음과 같다.
- 쿠키는 HTTP의 stateless 한계를 보완하기 위해 등장한, 자동 전송되는 작은 데이터다.
- 각 옵션은 전송 범위(
Domain/Path), 수명(Expires/Max-Age), 보안(Secure/HttpOnly/SameSite) 을 통제한다. - 인증에 쿠키가 표준이 된 이유는 자동 첨부, 스크립트 차단, 전송 제어, 서버측 즉시 무효화라는 네 박자가 갖춰져 있기 때문이다.
localStorage같은 다른 저장소는 자동 전송이 없고 XSS에 취약하므로, 인증 토큰의 위치로는 부적합하다.