2.3강 — 추적 쿠키로 과거 보기, 그리고 HTTP/2·HTTP/3¶
이번 회차의 범위
- 광고·분석 업체의 쿠키가 왜 조사자에게 쓸모 있나: 증거가 없는 과거의 시각이 들어 있다
- 고전적인 분석 쿠키(
__utma,__utmb,__utmz)의 구조와 읽는 법. [변경됨] 지금은_ga와_ga_<ID> - "사용자"는 사람이 아니라 쿠키 저장소다: 브라우저, 비공개 모드, 삭제
- 마케팅 쿠키(
__hstc,hubspotutk,hsfirstvisit)와 밀리초 시각 - [변경됨] 브라우저가 쿠키 수명을 자른다: Chrome 400일, Safari 7일
- HTTP/2: 이진 형식, 헤더 압축, 다중화(직접 수집한 HTTP/2 트래픽을 세션 키로 풀어 확인). [변경됨] 서버 푸시는 브라우저에서 제거됐다
- HTTP/3: QUIC 위의 HTTP, 조사에서 달라지는 점
실습 환경과 표기
**(직접 실행 결과)**는 이 작업 환경에서 얻었습니다. HTTP/2 트래픽은 nghttp2 1.59.0의 nghttpd 서버를 루프백에 TLS(직접 만든 자체 서명 인증서)와 평문(h2c) 두 가지로 띄우고, curl 8.5.0(nghttp2 내장)으로 요청을 보내 tcpdump로 수집했습니다. TLS 세션 키는 SSLKEYLOGFILE로 남겼고, tshark 4.2.2로 풀었습니다. 쿠키 값은 모두 설명을 위해 직접 지어낸 값이며, 시각 변환은 date로 직접 계산했습니다.
1. 증거가 없는 과거를 들여다보는 창¶
(가상 사례) 조사 대상 직원의 PC를 떠 둔 pcap은 2026년 11월 1일부터 일주일 치뿐입니다. 그런데 질문은 "이 직원이 그 경쟁사 채용 사이트를 처음 본 게 언제인가?"예요. 일주일 치 패킷에 그 사이트 접속이 있더라도, 그건 그 주에 접속했다는 사실일 뿐 처음이 언제인지는 알려 주지 않습니다.
이럴 때 뜻밖의 도움이 되는 것이 광고·분석 업체의 쿠키입니다. 이런 업체들은 방문자가 언제 처음 왔는지, 몇 번 왔는지, 어디서 왔는지를 최대한 자세히 기록해야 돈을 벌어요. 그래서 그 정보를 쿠키 값 안에 넣어 두는 경우가 많습니다. 그 쿠키는 요청마다 Cookie 헤더로 실려 오가니(2.1강 6.4절), 오늘 뜬 패킷 한 줄에서 몇 달 전 첫 방문 시각이 나올 수 있어요.
비유하자면 카페의 도장 쿠폰입니다. 오늘 카페에 온 손님의 쿠폰을 보면, 쿠폰을 처음 받은 날짜, 지난번 방문일, 지금까지 찍은 도장 수가 적혀 있죠. 카페 CCTV는 일주일 치뿐이어도 쿠폰은 그보다 오래된 이야기를 해 줍니다. 비유와 다른 점은 두 가지예요. 첫째, 쿠폰은 사람이 아니라 지갑(브라우저의 쿠키 저장소)에 붙어 있습니다. 둘째, 쿠폰의 형식은 카페(업체)가 정하고 공개하지 않는 경우가 많아, 해석에는 늘 단서가 붙습니다.
2. 고전적인 분석 쿠키: __utma, __utmb, __utmz¶
교재는 오랫동안 가장 널리 쓰인 웹 분석 서비스의 옛 쿠키 세 가지를 자세히 다룹니다. 지금은 쓰이지 않지만(3절), 오래된 증거(몇 년 전 pcap, 디스크의 오래된 쿠키 파일)에서는 여전히 만날 수 있고, "쿠키 안의 숫자를 읽는 법"을 익히기에 좋은 예라 구조를 정리해 둡니다. 이름의 utm은 이 서비스의 바탕이 된 Urchin Tracking Module에서 왔습니다.
모두 값을 마침표(.)로 나눈 형식이고, 첫 값은 사이트(도메인)의 해시입니다. 같은 사이트의 쿠키끼리는 이 값이 같아요.
| 쿠키 | 수명 | 값의 구성 |
|---|---|---|
__utma |
2년 | 도메인 해시 . 방문자 식별 값 . 첫 방문 . 지난 방문 . 이번 방문 . 방문 횟수 |
__utmb |
30분 | 도메인 해시 . 이번 세션의 페이지 수 . 나가는 링크 카운트(10부터 감소) . 이번 방문 시각 |
__utmz |
6개월 | 도메인 해시 . 시각 . 방문 횟수 . 유입 경로 수 . 유입 정보(utmcsr=출처|utmccn=캠페인|utmcmd=매체|utmctr=검색어) |
(가상 사례) 다음은 이 강의를 위해 지어낸 __utma 값입니다.
시각 값은 유닉스 시간(1970년부터 센 초, 1.3강)이니 date로 바꿉니다.
(직접 실행 결과)
1767225600 → 2026-01-01 00:00:00 UTC (첫 방문)
1793000000 → 2026-10-26 07:33:20 UTC (지난 방문)
1793512345 → 2026-11-01 05:52:25 UTC (이번 방문)
마지막 14는 방문 횟수입니다. 11월 1일 하루의 패킷 한 줄에서 "이 브라우저는 이 사이트에 2026년 1월 1일 처음 왔고, 지금까지 14번째 방문이며, 직전 방문은 10월 26일"이라는 이야기가 나온 셈이에요. 일주일 치 pcap으로는 절대 알 수 없던 과거입니다. 2.4강의 실습 목표 중 하나인 "광고 쿠키로 가진 증거 너머의 과거를 본다"가 바로 이것입니다.
__utmz의 유입 정보는 이 사이트에 어떻게 왔는지를 알려 줍니다. utmcsr=search-engine|utmcmd=organic|utmctr=...이라면 검색으로, utmcmd=referral이면 다른 사이트의 링크로, utmcsr=(direct)이면 주소를 직접 치거나 즐겨찾기로 온 것입니다. 2.1강에서 본 것처럼 지금은 Referer가 출처만 알려 주는 경우가 많아서, 이런 유입 정보가 남아 있다면 귀합니다(오래된 증거에서 특히 그렇습니다).
3. [변경됨] 지금의 분석 쿠키: _ga와 _ga_<ID>¶
위의 __utm* 쿠키를 만들던 옛 추적 코드는 오래전에 쓰이지 않게 됐고, 그 뒤를 이은 세대(Universal Analytics)도 2023년 7월 1일 일반 속성의 데이터 처리가 끝났습니다(Google 고객센터 안내). 지금 널리 쓰이는 세대(GA4)의 공식 쿠키 안내는 다음 두 가지를 적습니다.
| 쿠키 | 기본 수명 | 공식 설명 |
|---|---|---|
_ga |
2년 | 사용자를 구별하는 데 쓰임 |
_ga_<컨테이너 ID> |
2년 | 세션 상태를 유지하는 데 쓰임 |
공식 문서는 값의 내부 구조를 밝히지 않습니다. 다만 _ga의 값이 GA1.1.<무작위 숫자>.<유닉스 시각> 꼴이고, 마지막 숫자가 그 쿠키가 처음 만들어진 시각이라는 해석이 널리 쓰입니다. 이번 실습의 HTTP/2 요청에 넣은 지어낸 값으로 보면,
(직접 실행 결과) 2026-11-01 02:26:40 UTC. 이 해석이 맞다면 "이 브라우저 저장소에 그 사이트의 _ga 쿠키가 처음 생긴 시각"입니다.
_ga_<ID>는 세션 시작 시각, 세션 번호, 마지막 활동 시각 같은 값을 담는 것으로 알려져 있는데, 2025년 무렵 값의 형식이 GS1.1.…에서 $로 구분하는 GS2.1.…로 바뀌었다는 보고가 여러 곳에서 나왔습니다. 이 형식 변화는 업체의 공식 문서가 아니라 제3자 분석에서 확인한 것이라, 실제 증거를 해석할 때는 그 시점의 값을 직접 보고, 같은 사이트를 시험 브라우저로 열어 쿠키가 어떻게 바뀌는지 확인한 뒤 씁니다.
[주의] 문서화되지 않은 값을 증거로 쓸 때
분석 업체는 쿠키 형식을 공개할 의무가 없고, 예고 없이 바꿉니다. 그러니 보고서에는 ① 어떤 근거로 그 숫자를 시각으로 읽었는지(공식 문서인지, 공개 분석인지, 직접 시험인지), ② 다른 증거와 맞는지(교재의 예처럼 브라우저 기록이나 쿠키 데이터베이스의 생성 시각과 비교)를 함께 적습니다. 해석이 하나뿐이라면 "~로 보인다"로 씁니다.
4. "사용자"는 쿠키 저장소다¶
쿠키가 알려 주는 "사용자"는 사람이 아니라 하나의 쿠키 저장소입니다. 이것을 혼동하면 결론이 크게 틀어집니다.
- 브라우저마다 다릅니다. 같은 사람이 Chrome과 Firefox를 쓰면 쿠키 저장소가 둘이라 "사용자"도 둘이 됩니다. 같은 브라우저라도 프로필이 다르면 마찬가지예요.
- 비공개 모드는 매번 새 저장소입니다. 비공개 창을 열 때마다 빈 저장소에서 시작하므로, 쿠키상으로는 매번 "처음 온 새 사용자"가 됩니다. 즉 비공개 모드라면
__utma의 첫 방문 시각은 그 비공개 창이 열린 뒤의 시각일 뿐이에요. - 지우면 다시 시작합니다. 사용자가 쿠키를 지웠다면 첫 방문 시각도 그 이후로 초기화됩니다.
- 한 저장소를 여러 사람이 쓸 수 있습니다. 공용 PC, 공유 계정.
그래서 쿠키의 첫 방문 시각은 "이 사람이 처음 방문한 시각"이 아니라 **"이 저장소가 그 사이트의 쿠키를 처음 받은 시각, 그리고 그 뒤로 지워지지 않았다"**는 뜻으로 읽습니다. 그래도 같은 쿠키 값이 여러 날, 여러 주소에서 보인다면 같은 브라우저 저장소를 잇는 강한 끈이 되고, 다른 증거(로그인 기록, 단말)와 함께 사람에게 이을 수 있습니다.
5. 마케팅 쿠키: __hstc, hubspotutk, hsfirstvisit¶
분석 서비스만 이런 쿠키를 쓰는 건 아닙니다. 한 사건에서 서로 다른 추적 업체의 쿠키가 여러 개 보이는 것은 흔한 일이고, 업체마다 형식을 찾아 해석해야 합니다. 교재는 마케팅 자동화 서비스의 쿠키를 예로 듭니다.
__hstc: 방문자 추적의 주 쿠키.도메인 해시 . 방문자 식별 값 . 첫 방문 . 지난 방문 . 이번 방문 . 방문 횟수로__utma와 구조가 거의 같습니다. 다만 시각이 밀리초 단위(13자리)예요.hubspotutk: 방문자 식별 값.__hstc의 두 번째 값과 같고, 그 서비스를 쓰는 여러 사이트에서 같은 사람을 잇는 데 쓰입니다.hsfirstvisit: 교재 시절에 있던 쿠키로, 처음 이 도메인에 들어온 주소와 밀리초 시각을 담았습니다.
밀리초 시각은 뒤의 세 자리를 떼거나 1000으로 나눠 변환합니다.
(직접 실행 결과) 2026-11-01 05:52:25 UTC (뒤의 678은 0.678초).
[변경됨] 수명은 바뀌었고, 공식 값은 확인이 필요하다
교재는 __hstc 2년, hubspotutk과 hsfirstvisit 10년이라고 적었습니다. 지금 여러 사이트의 쿠키 공개 목록에는 __hstc와 hubspotutk이 13개월로 적힌 경우가 많지만, 6개월로 적은 곳도 있어 서로 맞지 않습니다(2026-11-05 검색 결과). 업체가 개인정보 규제에 맞춰 수명과 쿠키 목록을 바꿔 왔기 때문이에요. 실제 사건에서는 그 시점의 업체 공식 안내와, 시험 브라우저에서 직접 받은 쿠키의 만료 시각을 확인합니다.
6. [변경됨] 브라우저가 쿠키 수명을 자른다¶
업체가 "2년"이나 "10년"으로 정해도, 브라우저가 그보다 짧게 자를 수 있습니다. GA4의 공식 쿠키 안내도 "사용자가 다시 오지 않으면 Chrome은 최대 400일, Safari는 7일" 같은 브라우저 제한을 함께 적고 있어요. Safari는 스크립트가 만든 쿠키의 수명을 짧게 제한하고, Chrome은 쿠키의 최대 수명을 400일로 둡니다.
조사에서의 의미: 쿠키에 적힌 첫 방문 시각이 3주 전이라고 해서 "3주 전에 처음 왔다"고 읽으면 안 됩니다. Safari 사용자라면 그 전에도 왔지만 쿠키가 7일마다 사라졌을 수 있어요. 반대로 첫 방문이 2년 전으로 나온다면, 그 브라우저가 그 기간 동안 쿠키를 지우지 않았다는 뜻이기도 합니다. 브라우저 종류와 버전(2.1강의 User-Agent와 Client Hints)을 함께 봐야 쿠키 시각의 상한을 알 수 있습니다.
7. 쿠키는 어디서 보이나¶
- 평문 HTTP의 패킷:
Cookie헤더가 그대로 보입니다. 지금은 드뭅니다. - HTTPS: 패킷만으로는 보이지 않습니다. TLS 가로채기 프록시(1.2강)의 기록, 세션 키 기록(1.12강, 8절)이 있어야 해요.
- 프록시 로그: 기본 형식에는 헤더가 남지 않습니다(1.3강). 쿠키를 남기도록 설정할 수는 있지만, 세션 쿠키는 그 자체로 로그인 열쇠라 저장에 신중해야 합니다.
- 단말의 쿠키 데이터베이스: 디스크 포렌식의 영역. 비공개 모드라면 남지 않습니다.
그리고 지금의 분석 쿠키는 대부분 방문한 사이트 자신의 도메인(퍼스트 파티)에 붙습니다. 그래서 _ga 쿠키는 분석 업체로 가는 요청이 아니라 그 사이트로 가는 요청의 Cookie 헤더에서 보여요. 이번 실습에서도 _ga 값은 사이트(127.0.0.1:8445)로 가는 두 요청 모두에 실렸습니다**(직접 실행 결과)**.
8. HTTP/2: 전혀 다른 모양, 같은 뜻¶
8.1 무엇이 달라졌나¶
HTTP/2(RFC 9113)는 메서드, 상태 코드, 헤더의 뜻은 HTTP/1.1과 같지만, 회선 위의 모양은 완전히 다릅니다.
- 이진 형식: 글자 대신 프레임이라는 이진 단위로 오갑니다. 1.9강의 스트림 따라가기로는 거의 읽을 수 없어요.
- 헤더 압축(HPACK, RFC 7541): 헤더를 압축하고, 자주 나오는 헤더는 번호로 대신 보냅니다. 같은 연결 안에서 앞서 보낸 헤더를 기억하는 상태 있는 압축이라, 연결의 앞부분을 놓치면 뒤의 헤더를 해석하지 못할 수도 있습니다.
- 의사 헤더: 요청 줄 대신
:method,:path,:authority(HTTP/1.1의Host),:scheme을, 상태 줄 대신:status를 씁니다(2.2강: 이유 문구 없음). - 다중화: 한 TCP 연결 안에 스트림(번호가 붙은 요청·응답 한 쌍)이 여러 개 동시에 오갑니다. 클라이언트가 연 스트림은 홀수 번호(1, 3, 5, …)입니다.
- TLS: 표준상 필수는 아니지만, 브라우저는 TLS 위에서만 HTTP/2를 씁니다. 평문 HTTP/2(h2c)는 서버끼리나 도구에서나 볼 수 있어요.
8.2 직접 보기: 키가 없으면 0줄, 있으면 표¶
nghttpd에 HTTPS로 두 파일을 요청했습니다. curl은 두 요청을 한 TCP 연결에 실었어요.
SSLKEYLOGFILE=$PWD/keys.log curl -s -k --http2 -A "Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0" \
-H "Cookie: _ga=GA1.1.1234567890.1793500000" https://127.0.0.1:8445/index.html https://127.0.0.1:8445/report.txt
세션 키 없이 tshark에 http2를 걸자 0줄이었습니다. 내용이 모두 TLS로 암호화돼 있으니까요. SSLKEYLOGFILE로 남긴 키를 주면 이렇게 됩니다.
tshark -n -r h2.pcap -o tls.keylog_file:keys.log -Y 'http2.type == 1' -T fields \
-e tcp.stream -e http2.streamid -e http2.headers.method -e http2.headers.path \
-e http2.headers.authority -e http2.headers.user_agent -e http2.headers.status
(직접 실행 결과, User-Agent 열 일부 생략)
0 1 GET /index.html 127.0.0.1:8445 Mozilla/5.0 (X11; Linux x86_64; rv:140.0) ...
0 1,1 200
0 3 GET /report.txt 127.0.0.1:8445 Mozilla/5.0 (X11; Linux x86_64; rv:140.0) ...
0 3,3 200
http2.type == 1은 HEADERS 프레임(헤더를 실은 프레임)만 고르라는 뜻입니다.- 첫 열이 모두
0, 즉 TCP 스트림 하나입니다. 그 안에서 HTTP/2 스트림1과3이 각각 요청 하나씩을 실었어요. 1.9강의 "TCP 스트림"과 HTTP/2의 "스트림"은 다른 개념이니 섞지 마세요. http2.headers.user_agent,http2.headers.path처럼 Wireshark는 압축을 푼 헤더를 이름으로 보여 줍니다. HTTP/1.1의http.user_agent와 필드 이름만 다르고 뜻은 같아서, 1.9강과 1.10강의 필터와 표 만들기를 그대로 옮겨 쓸 수 있습니다.
평문 HTTP/2(h2c)로 보낸 요청을 스트림 따라가기로 보면 맨 앞만 글자이고 나머지는 이진 데이터였습니다.
(직접 실행 결과, 앞부분)
PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n은 클라이언트가 HTTP/2를 시작한다는 고정된 시작 문자열(connection preface)입니다. 이 문자열이 보이면 HTTP/2 평문 연결이고, 그 뒤의 헤더는 HPACK으로 압축돼 사람이 읽을 수 없어요. Wireshark가 해석하게 하려면 그 포트를 HTTP/2로 지정합니다(-d tcp.port==8446,http2). 그렇게 하자 GET /index.html과 200이 바로 나왔습니다**(직접 실행 결과)**.
[보충] 브라우저 개발자 도구로 보는 HTTP/2
교재는 브라우저의 개발자 도구(네트워크 탭)로 HTTP/2 요청을 보는 방법도 소개합니다. 브라우저는 TLS의 끝점이라 암호화와 상관없이 :authority, :path 같은 헤더를 그대로 보여 주고, 프로토콜 열에 h2(HTTP/2), h3(HTTP/3)를 표시합니다. 조사에서는 시험용 브라우저로 같은 사이트를 열어 보며 어떤 요청이 어떤 모양으로 나가는지 익히는 데 씁니다. 조사 대상의 브라우저에서 이걸 할 수 있는 경우는 드뭅니다.
8.3 [변경됨] 서버 푸시는 브라우저에서 사라졌다¶
교재는 HTTP/2의 서버 푸시, 즉 서버가 요청받지 않은 객체를 브라우저 캐시에 미리 밀어 넣는 기능을 경고합니다. 캐시에 있는 파일이 사용자가 요청한 것인지, 서버가 밀어 넣은 것인지 구별할 수 없어 단말의 캐시 분석이 틀릴 수 있다는 거예요. 이 기능은 실제 효과가 적다는 이유로 Chrome 106(2022)에서 기본으로 꺼졌고, Firefox도 2024년에 끄겠다고 발표한 뒤 제거했습니다. 그러니 지금의 브라우저 캐시에서는 이 걱정이 거의 사라졌지만, 2022년 이전의 증거를 다룬다면 여전히 기억해 둘 점입니다. 대안으로 쓰이는 103 Early Hints 응답은 "이 자원을 미리 받아 두라"는 힌트만 보내고, 실제 요청은 브라우저가 합니다.
9. HTTP/3: QUIC 위의 HTTP¶
HTTP/3(RFC 9114, 2022)는 HTTP/2의 생각을 QUIC(RFC 9000) 위로 옮긴 것입니다. 조사에서 달라지는 점을 정리하면,
- UDP 443번을 씁니다. TCP가 아니라서, 1.2강에서 본 것처럼 TCP만 거치는 프록시를 비껴갈 수 있습니다. 방화벽 기록에서 UDP 443을 따로 봐야 하는 이유예요.
- 거의 모든 것이 암호화됩니다. QUIC는 TLS 1.3을 내장해, HTTP 헤더는 물론 TCP라면 보였을 전송 계층 정보(순서 번호, 확인 응답)까지 대부분 암호화합니다. 패킷만으로 볼 수 있는 건 주소, 포트, 크기, 시간, 연결 식별 값 정도이고, 첫 Initial 패킷의 Client Hello에서 SNI가 보이는 정도입니다(ECH를 쓰지 않는다면, 1.12강).
- 헤더 압축은 QPACK(RFC 9204)입니다. HPACK과 비슷한 생각이지만 다른 방식이에요.
- 풀려면 역시 세션 키가 필요합니다. Wireshark는
SSLKEYLOGFILE형식의 키로 QUIC를 풀 수 있다고 안내합니다. 이 작업 환경의curl은 HTTP/3를 지원하지 않게 빌드되어 있어 HTTP/3는 직접 시험하지 못했습니다.
결국 HTTP/2와 HTTP/3 모두, 조사자가 내용을 보려면 연결의 한쪽 끝(브라우저, TLS 가로채기 프록시, 서버 로그)의 기록이나 세션 키가 필요합니다. 패킷만 있다면 1.12강의 플로우 분석(크기와 시간 패턴)으로 갑니다.
10. 실제 조사에서는 이렇게 씁니다¶
- 쿠키 이름으로 업체를 알아낸다.
_ga,__utma,__hstc처럼 이름이 업체와 형식을 알려 줍니다. 모르는 이름이면 그 업체의 공개 문서와 쿠키 공개 목록을 찾습니다. - 숫자를 시각으로 바꿔 본다. 10자리는 초, 13자리는 밀리초인 경우가 많습니다. 바꾼 시각이 그럴듯한 범위(최근 몇 년)인지 확인합니다.
- "사용자"를 저장소로 읽는다. 브라우저, 프로필, 비공개 모드, 삭제를 고려하고, 브라우저의 쿠키 수명 제한으로 상한을 따집니다.
- 문서화 수준을 적는다. 공식 문서, 공개 분석, 직접 시험 중 무엇이 근거인지 보고서에 남깁니다.
- HTTP/2·3는 키나 끝점 기록부터 찾는다. 없다면 TLS 버전, SNI, 크기와 시간 패턴으로 갑니다.
- TCP 스트림과 HTTP/2 스트림을 구별한다. 한 TCP 연결에 여러 요청이 동시에 들어 있습니다.
11. 정리¶
- 광고·분석 쿠키에는 첫 방문, 지난 방문, 방문 횟수, 유입 경로 같은 과거의 정보가 들어 있어, 가진 증거의 기간 너머를 보여 줍니다. 지어낸
__utma값에서 첫 방문(2026-01-01), 지난 방문, 방문 횟수(14)를 읽었습니다. - [변경됨]
__utm*는 옛 세대이고, 다음 세대도 2023-07-01 처리가 끝났습니다. 지금은_ga와_ga_<ID>(공식 기본 수명 2년)이며, 값의 구조는 공식 문서에 없고 형식도 바뀐 적이 있습니다. - 쿠키의 "사용자"는 쿠키 저장소입니다. 브라우저, 비공개 모드, 삭제에 따라 달라집니다.
- 마케팅 쿠키는 밀리초 시각을 씁니다. [변경됨] 수명은 교재와 달라졌고, 공개 목록끼리도 맞지 않으니 확인이 필요합니다.
- [변경됨] 브라우저가 쿠키 수명을 자릅니다(Chrome 400일, Safari 7일). 첫 방문 시각의 상한은 브라우저에 달려 있습니다.
- HTTP/2는 이진 형식, HPACK, 의사 헤더, 다중화입니다. 키 없이는 0줄, 세션 키로 풀자 TCP 연결 하나에 HTTP/2 스트림 1과 3이 보였습니다. 평문은
PRI * HTTP/2.0으로 시작합니다. - [변경됨] 서버 푸시는 Chrome 106과 Firefox에서 제거됐습니다.
- HTTP/3는 UDP 443의 QUIC 위에서 거의 모든 것을 암호화합니다. 내용은 끝점 기록이나 세션 키로만 봅니다.
복습 문제¶
문제 1. (계산) 2026년 11월 4일의 pcap에 __utma=48210377.551902814.1735689600.1792800000.1793750400.37이 보입니다. 첫 방문, 지난 방문, 이번 방문의 날짜와 방문 횟수를 구하세요.
정답과 해설
date -u -d @1735689600은 2025-01-01 00:00:00 UTC, @1792800000은 2026-10-24 00:00:00 UTC, @1793750400은 2026-11-04 00:00:00 UTC입니다(초를 하루 86,400으로 나눠 계산하면 확인할 수 있어요). 방문 횟수는 마지막 값 37입니다. 즉 이 브라우저 저장소는 2025년 1월 1일 이 사이트의 쿠키를 처음 받았고, 2026년 11월 4일이 37번째 방문, 직전 방문은 10월 24일입니다. 단, 첫 방문은 "이 저장소가 그때 처음 받았고 그 뒤 지우지 않았다"는 뜻입니다(4절).
문제 2. (흔한 오해) "직원 PC에서 경쟁사 사이트의 _ga 쿠키 마지막 숫자가 사흘 전 시각이니, 이 직원은 사흘 전에 처음 그 사이트에 갔다." 이 결론이 틀릴 수 있는 경우를 셋 이상 드세요.
정답과 해설
① 비공개 모드였다면 창을 열 때마다 새 저장소라, 사흘 전에 연 비공개 창에서 처음 받은 쿠키일 뿐입니다. ② 쿠키를 지웠다면 그 이후로 초기화됩니다. ③ 다른 브라우저나 프로필로는 그 전에도 갔을 수 있습니다. ④ 브라우저의 수명 제한(예: Safari의 짧은 제한)으로 예전 쿠키가 사라지고 새로 만들어졌을 수 있습니다. ⑤ 애초에 그 숫자를 "첫 생성 시각"으로 읽는 것 자체가 공식 문서에 없는 해석입니다. 그러니 "이 브라우저 저장소의 그 사이트 _ga 쿠키는 사흘 전에 만들어진 것으로 보인다"까지가 쓸 수 있는 문장이고, 첫 방문은 다른 증거(프록시 로그, 브라우저 기록)로 거슬러 찾습니다.
문제 3. (계산) 마케팅 쿠키에 1793598745123이라는 값이 있습니다. 어떤 시각인가요? 이 값을 그대로 date -u -d @1793598745123에 넣으면 어떻게 될까요?
정답과 해설
13자리라 밀리초로 보고 앞 10자리 1793598745를 변환하면 2026-11-02 05:52:25 UTC이고, 뒤의 123은 0.123초입니다(1793512345가 11월 1일 05:52:25였고, 86,400초를 더하면 1793598745이니 정확히 하루 뒤예요). 13자리를 그대로 넣으면 date는 초로 받아들여 수만 년 뒤의 엉뚱한 날짜를 내거나 범위 오류를 냅니다. 그럴듯하지 않은 연도가 나오면 단위(초, 밀리초, 마이크로초)부터 의심하세요.
문제 4. (해석) 같은 hubspotutk 값이 서로 다른 세 회사의 웹 사이트로 가는 요청에서 보입니다. 무엇을 알 수 있고, 무엇은 알 수 없나요?
정답과 해설
알 수 있는 것: 그 마케팅 서비스를 쓰는 세 사이트를 같은 브라우저 저장소가 방문했다는 것. 이 값은 그 서비스가 방문자에게 준 식별 값이라, 사이트가 달라도 같은 사람(정확히는 같은 저장소)을 잇는 끈이 됩니다. 알 수 없는 것: 그 저장소를 쓴 사람이 누구인지(공용 PC일 수 있음), 그리고 그 값이 어떻게 만들어졌는지(교재도 구성은 문서화되지 않았다고 말합니다). 또 이 값이 여러 사이트에 공유되는 방식은 업체와 브라우저의 쿠키 정책(퍼스트 파티, 서드 파티 제한)에 따라 달라질 수 있으니, 그 시점의 동작을 확인하고 씁니다.
문제 5. (변경된 조건) 2016년에 만든 절차서에 "단말의 브라우저 캐시에 있는 파일은 사용자가 요청한 것으로 본다. 단, HTTP/2 서버 푸시 가능성을 적는다"고 되어 있습니다. 2026년 사건의 Chrome 캐시라면 이 단서는 어떻게 바꾸면 좋을까요?
정답과 해설
Chrome은 106(2022)부터 HTTP/2 서버 푸시를 기본으로 껐으므로, 그 이후 버전의 Chrome 캐시에는 서버가 밀어 넣은 객체가 들어올 가능성이 거의 없습니다. 그러니 2026년 사건이라면 "Chrome 106 이후로 서버 푸시는 비활성"이라고 근거와 함께 적고, 대신 다른 이유로 사용자가 직접 요청하지 않은 파일이 캐시에 있을 수 있다는 점을 적는 게 맞습니다. 페이지가 알아서 불러온 이미지·스크립트, 미리 가져오기(prefetch), 103 Early Hints로 브라우저가 미리 받은 자원 등이요. 캐시 파일 하나를 "사용자가 원해서 연 것"으로 읽지 않는다는 원칙은 그대로입니다.
문제 6. (구조) 8.2절의 결과에서 tcp.stream은 모두 0인데 http2.streamid는 1과 3입니다. 이 차이를 설명하고, 같은 요청을 HTTP/1.1로 보냈다면 어떻게 보였을지 말해 보세요.
정답과 해설
tcp.stream은 Wireshark가 TCP 연결마다 매긴 번호이고, http2.streamid는 HTTP/2가 한 연결 안에서 요청·응답 쌍마다 매긴 번호입니다. 두 요청이 하나의 TCP 연결 안에서 HTTP/2 스트림 1과 3으로 다중화된 거예요(클라이언트가 연 스트림은 홀수). HTTP/1.1이었다면 연결 유지(2.2강 4.1절) 덕분에 역시 한 TCP 연결로 갈 수는 있지만, 요청이 차례로 하나씩 오가고(앞 응답이 끝나야 다음 요청), 스트림 번호 같은 건 없습니다. 두 요청을 동시에 보내려면 TCP 연결을 두 개 여는 경우가 많고요. 그래서 HTTP/2 환경에서는 연결 수가 줄어들어, 플로우 기록에서 "연결 하나 = 요청 하나"로 세면 더 크게 틀립니다.
문제 7. (적용) 패킷 센서에 UDP 443 트래픽이 크게 늘었는데, 프록시 로그의 웹 요청 수는 그대로입니다. 무엇이 일어났을 가능성이 크고, 무엇을 확인하겠습니까?
정답과 해설
브라우저나 앱이 **HTTP/3(QUIC)**로 직접 나가면서 TCP만 거치는 프록시를 비껴갔을 가능성이 큽니다(1.2강). 확인할 것은 ① 방화벽에서 UDP 443 바깥 연결의 출발지와 목적지(어느 PC, 어느 서비스), ② 브라우저 정책(예: QUIC 허용 여부)과 그 변경 시점, ③ QUIC Initial 패킷의 SNI(ECH가 아니라면)로 목적지 이름, ④ 프록시 로그에서 같은 PC의 같은 서비스 요청이 줄었는지입니다. 조치로는 UDP 443을 막아 브라우저가 TCP로 돌아오게 하거나 정책으로 QUIC를 끄는 방법이 있고, 결정과 근거를 기록합니다.
문제 8. (흔한 오해) "HTTP/2는 헤더가 압축돼 있으니, 세션 키만 있으면 pcap의 어느 지점부터 읽어도 헤더가 다 보인다." 무엇이 빠졌나요?
정답과 해설
HPACK은 같은 연결 안에서 앞서 보낸 헤더를 동적 테이블에 기억해 두고 뒤에서는 번호로 가리키는 상태 있는 압축입니다. 그래서 수집이 연결 중간부터 시작됐거나 앞부분 패킷이 빠졌다면, 세션 키가 있어도 뒤의 헤더 중 일부는 무엇을 가리키는지 알 수 없어 온전히 풀리지 않을 수 있습니다. TLS 세션 키 자체도 연결 시작 때의 핸드셰이크와 짝지어야 쓸 수 있고요. 그러니 HTTP/2를 분석하려면 연결의 시작부터 수집된 pcap이 필요하고, 수집 기록에 시작 시각을 남겨 둡니다.
문제 9. (해석) 오래된 pcap(2014년)에서 __utmz=...utmcsr=search-engine|utmccn=(organic)|utmcmd=organic|utmctr=orchid%20alloy%20formula를 찾았습니다. 무엇을 알 수 있나요?
정답과 해설
__utmz는 이 사이트에 어떻게 들어왔는지를 담은 쿠키입니다. utmcmd=organic은 검색 결과(광고가 아닌)를 눌러 들어왔다는 뜻이고, utmctr의 값을 디코딩하면 "orchid alloy formula", 즉 그때 사용한 검색어입니다. 당시에는 검색 엔진에서 다른 사이트로 갈 때 검색어가 넘어오는 경우가 있어서 이런 값이 남았어요. 지금은 Referer가 출처만 넘기고(2.1강) 검색 엔진도 검색어를 넘기지 않는 경우가 많아, 이런 정보는 오래된 증거에서 특히 귀한 단서입니다. 다만 쿠키가 기록한 건 그 저장소의 마지막 유입 경로(쿠키가 갱신된 시점의 것)라는 점, 그리고 몇 번째 방문 때의 것인지는 다른 값과 함께 봐야 한다는 점을 함께 적습니다.
참고 자료¶
- 작업 환경에서 쓴 도구: nghttp2 1.59.0(
nghttpd), curl 8.5.0(nghttp2 내장,SSLKEYLOGFILE), tcpdump 4.99.4, tshark 4.2.2, GNUdate. 실행 결과는 2026-11-05에 얻었습니다. - Google, GA4 쿠키 사용 안내 (2026-11-05 검색 결과로 확인).
_ga,_ga_<container-id>기본 2년, 브라우저의 수명 제한(Chrome 400일, Safari 7일). - Google, Universal Analytics 종료 안내 (2026-11-05 검색 결과로 확인). 일반 속성 2023-07-01 처리 종료.
- Chrome 개발자 블로그, Removing HTTP/2 Server Push from Chrome (2026-11-05 검색 결과로 확인). Chrome 106에서 기본 비활성, 103 Early Hints 권장.
- IETF, RFC 9113: HTTP/2(3.4절 연결 시작 문자열, 5절 스트림), RFC 7541: HPACK, RFC 9114: HTTP/3, RFC 9204: QPACK, RFC 9000: QUIC, RFC 8297: 103 Early Hints.
- 마케팅 쿠키의 수명은 여러 사이트의 쿠키 공개 목록(2026-11-05 검색)에서 13개월과 6개월로 엇갈렸습니다. 업체의 현재 공식 안내를 확인하세요.
이 페이지에 대하여
여러 학습 자료를 바탕으로 직접 다시 쓴 개인 학습 노트입니다. 쿠키 값과 실험 트래픽은 직접 만들었고, 사례는 가상입니다.