- HTTP 쿠키는 웹의 상태를 유지하는 기본 장치지만, 브라우저·서버·표준 라이브러리가 허용 문자와 오류 처리에서 엇갈려 실제 장애로 이어질 수 있음
- RFC 6265 계열은 서버가 보내는
Set-Cookie값과 브라우저가 받아들이는 값의 조건이 다르며,document.cookie로 만든 값이 서버 측 파서의 가정과 충돌함 - Firefox, Chromium, Safari는 공백·따옴표·쉼표·백슬래시·Unicode 처리에서 서로 다르고, Safari는 금지 문자를 만나도 쿠키 전체가 아니라 앞부분만 저장하는 동작을 보임
- Go는 브라우저가 허용한 JSON 쿠키를 조용히 누락할 수 있고, Python
SimpleCookie는 이해하지 못하는 쿠키 이후의 로딩을 중단할 수 있으며, PHP·Ruby·Rust도 허용 범위가 제각각임 - Unicode 쿠키 하나가 Facebook, Netflix, Okta, WhatsApp, AWS, Apple Support 등 주요 사이트의 400/500 오류나 부분 장애를 유발할 수 있어, 쿠키 규격과 라이브러리 동작을 더 명확히 맞춰야 함
브라우저는 받지만 Go는 못 읽는 쿠키
- 쿠키는 JavaScript의
document.cookie나 HTTP 서버가 설정하는 데이터이며, 만료 전까지 범위가 맞는 HTTP 요청에 계속 포함됨 - 예시 JavaScript는 JSON 문자열을 그대로 세션 쿠키 값으로 저장함
- 값은
{"ginger":"snap","peanutButter":"chocolate chip","snicker":"doodle"}형태임 - JSON을 쿠키에 넣을 때 base64 직렬화를 하는 경우가 많지만, 브라우저는 이 값을 문제없이 설정하고
Cookie헤더로 보냄
- 값은
- 이 쿠키가 Go 표준 라이브러리를 쓰는 코드로 전달되면서 문제가 발생함
- Go 파서는 해당 쿠키를 해석하지 못함
- 실패가 스택 상위로 연쇄 전파됨
RFC 안의 두 기준이 어긋남
- 쿠키는 RFC 2109, RFC 2965, RFC 6265를 거쳐 정의됐고, 현재 갱신 중인 draft version이 있음
- RFC는 쿠키 값을 두 영역에서 다르게 다룸
- Section 4.1.1은 서버가
Set-Cookie로 보내는 값에서 제어 문자, 공백, 큰따옴표, 쉼표, 세미콜론, 백슬래시 등을 제외함 - Section 5.6은 브라우저가
Set-Cookie문자열을 파싱할 때 제어 문자를 제외하면 훨씬 더 넓게 받아들이도록 함
- Section 4.1.1은 서버가
- 핵심 충돌은 서버가 보내야 하는 값과 브라우저가 받아야 하는 값이 정렬돼 있지 않다는 점임
- 브라우저가 서버 자신이 설정한 쿠키만 받는다면 영향이 작지만,
document.cookie도 쿠키를 만들 수 있음 - 표준은
Cookie헤더를 처리하는 표준 라이브러리가 사용자 에이전트처럼 관대해야 하는지, 서버처럼 엄격해야 하는지 명확히 정하지 않음
- 브라우저가 서버 자신이 설정한 쿠키만 받는다면 영향이 작지만,
브라우저별 쿠키 값 허용 차이
-
Firefox
- Firefox의 쿠키 값 검사는 RFC 6265에서 금지한 문자 일부를 허용함
- 허용되는 RFC 권장 제외 문자는 다음과 같음
0x09horizontal tab0x20공백0x22큰따옴표0x2C쉼표0x5C백슬래시
- 이 동작은 과거 Chrome과의 호환성을 맞추기 위해 들어갔고 두 코드베이스에 남아 있음
network.cookie.blockUnicode설정은0x80이상 값을 거부할 수 있으며, 관련 작업은 bug 1797231에서 추적됨0x7F허용 문제는 bug 1797235에서 Firefox 108에 수정됨
-
Chromium
- Chromium은 쿠키 값에서 제어 문자와 세미콜론만 거부함
- Firefox보다 약간 더 엄격해
0x09horizontal tab은 받지 않음 - RFC와 달리 공백, 큰따옴표, 쉼표, 백슬래시, Unicode 문자는 받고 다시 보낼 수 있음
-
Safari / WebKit
- Safari의 쿠키 저장 코드는 닫힌 소스인
CFNetwork내부에 있어 직접 확인하기 어려움 - JavaScript로
0x00부터0xFF까지 쿠키 값을 설정해 확인한 결과, Safari는 다음 값을 허용함0x09horizontal tab0x20공백0x22큰따옴표0x5C백슬래시
- Safari는
0x7Fdelete와0x80-FFhigh ASCII / Unicode 문자를 허용하지 않음 - RFC는 제어 문자를 만나면 쿠키 전체를 무시하라고 하지만, Safari는 금지 문자를 만난 지점 앞까지의 값을 받아들임
-- , --값을 설정하면 쉼표 주변 공백을 제거하는 Safari 버그도 관찰됨
- Safari의 쿠키 저장 코드는 닫힌 소스인
언어와 표준 라이브러리의 파싱 차이
-
Go
- Go의 쿠키 코드는 서버가
Set-Cookie로 보내는 값에 대한 RFC 문구에 비교적 가깝게 동작함 - 실제 사용에서 흔한 공백과 쉼표는 허용하지만, 큰따옴표·세미콜론·백슬래시는 허용하지 않음
- 예시
Cookie헤더에 JSON 쿠키가 포함되면 Go의request.Cookies()결과에는cookie1=foo와cookie3=bar만 남음 - 브라우저가 받아들이는
cookie2는 예외나 명시적 오류 없이 조용히 빠짐
- Go의 쿠키 코드는 서버가
-
PHP
- PHP는 네이티브 쿠키 파싱 함수가 없어 정확한 허용 범위를 단정하기 어렵지만, 테스트 결과 제어 문자 처리 동작이 일관적이지 않음
0x00-0x09와0x0Dcarriage return 같은 값은 동작함0x10data link escape나0x7Fdelete를 쓰면 PHP는 400 Bad Request 오류를 냄- Unicode 쿠키도 테스트 출력에 나타남
-
Python
- Python의
http.cookies.SimpleCookie는 JSON 쿠키를 만나면 이후 쿠키 로딩을 조용히 중단함 - 예시 입력에서 출력은
cookie1=foo만 남음 - 하위 도메인이 기본 도메인에 문제 쿠키를 설정할 수 있다면, 해당 쿠키 하나가 사이트 전체의 쿠키 처리를 깨뜨릴 수 있음
- 제어 문자 처리도 불규칙함
- 일부 제어 문자는 빈 값으로 로드됨
- 값 앞뒤에
aa를 붙이면 제어 문자 쿠키가 로드되지 않음
- Python의
-
Ruby
- Ruby의
CGI::Cookie.parse는 파싱 시 매우 관대하게 동작하는 것으로 보임 - 제어 문자, 탭, 큰따옴표, 쉼표, 백슬래시,
0x7F, Unicode 문자를 받아들이고, 쿠키 jar에서 꺼낼 때 percent-encoding을 적용함 - 이 방식은 쿠키 세계에서 최적에 가까울 수 있지만,
document.cookie로 설정한 코드가 percent-encoding된 반사 값을 기대하지 않을 수도 있음
- Ruby의
-
Rust
- Rust는 기본 쿠키 처리 기능을 제공하지 않아 인기 있는
cookiecrate를 기준으로 확인함 - 기본 설정의
cookiecrate는 가장 관대한 쪽에 가까우며, 전달된 UTF-8 문자열을 받아들이는 것으로 보임
- Rust는 기본 쿠키 처리 기능을 제공하지 않아 인기 있는
실제 웹사이트에서 드러난 영향
- 테스트 사이트에서 서드파티 라이브러리 업데이트를 수동 검증하던 중 이 문제가 발견됨
- 자동 테스트에 잡히기 어려운 변경이었음
- 그대로 배포됐다면 이후 방문자가 깨진 쿠키를 받고, 업데이트 롤백과 쿠키 삭제 전까지 알 수 없는 오류로 잠길 수 있었음
- 이 문제는 작은 사이트나 특정 프레임워크에만 한정되지 않음
- 브라우저 콘솔에서 다음처럼 Unicode 쿠키를 도메인에 설정하면 여러 주요 사이트가 깨질 수 있음
document.cookie="unicodeCookie=🍪; domain=.grayduck.mn; Path=/; SameSite=Lax"
- 관찰된 사례는 다음과 같음
- Facebook: 오류 페이지가 표시되고 이미지도 깨짐
- Instagram 및 Threads: 단순한 500 오류가 발생함
- Netflix:
NSES-500오류를 반환하고 도움말 페이지도 깨짐 - Okta: 모든 로그인 페이지가 400 오류를 반환함
- WhatsApp: “whatsapp error”가 표시됨
- Amazon: 대부분은 동작하지만 일부 기능이 무작위로 깨짐
- AWS: 로그인 콘솔이 400 오류를 반환하고 중단됨
- Apple Support: 기기 목록을 불러오지 못함
- Best Buy: 내비게이션은 동작하지만 검색 기능이 동작하지 않음
- eBay: 대부분 수정됐지만 일부가 여전히 400 오류를 냄
- Home Depot: 수정 예정
- Intuit: 오류 원인을 식별한 유일한 사이트
- Outlook: 또 다른 400 오류 사례가 나타남
표준과 호환성 사이의 수정 난점
- 30년 된 기반 명세의 문제를 고치는 일은 매우 어렵고, 이 문제에는 좋은 해결책이 없을 가능성이 큼
- 브라우저 쪽에서 이런 쿠키를 차단하는 방안은 Mozilla와 Google 모두 검토하고 작업함
- Mozilla: bug 1797235, CVE-2023-5723, bug 1797231
- Google: bug 40061459
- 일방적 차단은 호환성 문제 때문에 복잡함
- 비 ASCII 쿠키는 전체 쿠키 중 0.01% 미만 수준으로 흔하지 않음
- Argentina, Mexico, Finland 같은 국가에서는 훨씬 더 자주 나타난다는 telemetry가 있음
- Mozilla는 빠르게 켤 수 있는
network.cookie.blockUnicode설정을 구현했지만, Chromium과의 동작 호환성 문제 때문에 활성화하지 않음
- 서버 쪽 수정도 가능할 수 있으나 수백만 개 웹사이트와 언어·프레임워크 내부 오류 처리에 걸쳐 있음
- Facebook이나 Netflix 같은 곳은 완화할 수 있을지 몰라도, 평균적인 사이트 운영자가 해결할 시간이나 능력을 갖추기는 어려움
- 근본적 해결은 IETF HTTP Working Group이 쿠키 명세를 내부적으로 정렬하고, 쿠키 처리 시스템이 어떻게 동작해야 하는지 엄격하게 정하는 데 있음
- 비 ASCII 문자의 허용 여부는 서버 측과 사용자 에이전트에서 동일해야 함
- 브라우저, 언어, 프레임워크가 쿠키를 처리하는 단계도 Content Security Policy 같은 현대 W3C 표준처럼 명시적이어야 함
- 잘못된 쿠키 하나 때문에 다른 쿠키 처리까지 중단하는 동작은 다양한 예기치 못한 장애로 이어질 수 있어 받아들이기 어려움
제안된 쿠키 처리 절차
field-value에서 시작해;와,로 나누어raw-cookie-pair목록을 만들되, 쉼표를 세미콜론의 동의어로 취급하지 않음- 각
raw-cookie-pair는 다음 순서로 처리함=가 없으면 다음 pair로 넘어감- 앞뒤 공백을 제거함
- 첫 번째
=앞은cookie-name-octets, 뒤는cookie-value-octets로 다룸 - 값이 큰따옴표로 시작하면 시작 큰따옴표를 하나 제거하고, 끝 큰따옴표가 있으면 하나 제거함
- 이름이나 값이 서버가 받아들일 수 없는 형태면 해당 pair를 건너뜀
- 남은
[cookie-name-octets, cookie-value-octets]튜플은 서버 정의 방식으로 처리함
- 서버는 추가로 쿠키 이름이 token이 아닌 튜플을 거부하고, 쿠키 값이
cookie-octet에 없는 octet을 포함하면 거부하는 방향이 제안됨