홈페이지 하나를 캐싱했을 뿐인데, 로그인 토큰까지 함께 저장됐다
홈페이지 하나를 캐싱했을 뿐인데, 로그인 토큰까지 함께 저장됐다
분류: Bugbounty · 웹·서버 · 웹 캐시 기만(WCD) [정보성]
덱. 보안 연구자 tinopreter는 한 예약 플랫폼의 버그바운티 프로그램에서, 홈페이지 캐싱과 쿠키의 SameSite 속성이라는 서로 무관해 보이는 두 설정이 겹쳤을 때 사용자의 로그인 토큰이 통째로 새어 나갈 수 있다는 것을 보였습니다. 심각도는 HIGH, 포상금은 2,000달러로 원문에 명시돼 있습니다. 캐시라는 “성능을 위한 장치”가 그 자체로 인증 우회 경로가 될 수 있다는 걸 보여주는 사례입니다.
리드
캐싱은 매 요청마다 서버를 다시 두드리지 않기 위한 장치입니다. 그런데 캐시는 “이 응답은 누가 봐도 똑같다”는 전제 위에서만 안전합니다. 응답 안에 사용자 한 명만 봐야 할 정보가 섞여 있는데도 그 응답이 캐시 대상으로 분류되면, 캐시 서버(프록시·CDN)는 그 정보를 그대로 저장했다가 전혀 다른 사람에게 내어줍니다. 이런 결함을 웹 캐시 기만(Web Cache Deception, WCD)이라 부릅니다. 이번 사례가 교과서적인 이유는, 결함 하나만으로는 공격이 완성되지 않고 브라우저의 또 다른 기본 보호 장치인 SameSite 쿠키 정책까지 함께 넘어야 했다는 데 있습니다.
핵심 사실 요약
- 대상 플랫폼은 사용자 세션을 쿠키 헤더에 담긴 JWT(JSON Web Token)로 관리했습니다. 연구자는 홈페이지 응답에
X-Cache: MISS(캐시에 없어 서버가 새로 응답)와X-Cache: HIT(캐시에서 바로 응답)가 번갈아 나오는 것을 확인해, 홈페이지 전체가 캐시 대상이라는 걸 알아냈습니다. - 문제는 그 캐시 대상 홈페이지 응답 안에, 로그인한 사용자 본인의 JWT가 JavaScript 태그 속에 그대로 박혀 있었다는 점입니다. 캐시되면 안 되는 개인화된 데이터가 캐시되는 페이지에 섞여 있었던 것입니다.
- 공격자가
?cacheBuster=고유값같은 파라미터로 캐시 항목을 특정 요청 하나로 고정시키고, 피해자가 그 URL을 방문하도록 유도하면, 피해자의 JWT가 담긴 응답이 그 캐시 슬롯에 저장됩니다. 공격자가 같은 URL을 다시 요청하면 캐시가 그 저장된 응답, 즉 피해자의 토큰을 그대로 돌려줍니다. - 여기서 한 번 막혔습니다. 피해자를 공격자 페이지에서 자동으로 그 URL로 이동시키려 하니 브라우저가 요청에 세션 쿠키를 실어 보내지 않았습니다. 대상이 SameSite를 명시하지 않았더라도 최신 브라우저는 기본값으로
SameSite=Lax를 적용해, 다른 사이트에서 시작된 요청에는 쿠키를 붙이지 않기 때문입니다. - 연구자는
SameSite=Lax에 예외가 있다는 걸 활용했습니다. 요청이 최상위 탐색(top-level navigation) — 즉 브라우저 주소창 자체가 새 URL로 바뀌는 이동 — 으로 발생하면 Lax 정책이 적용되지 않고 쿠키가 그대로 실립니다. HTML<meta http-equiv="refresh">태그로 자동 최상위 이동을 일으키면, 사용자 클릭 없이도 이 조건을 만족시킬 수 있었습니다. - 결과적으로 공격자 페이지를 열기만 한 피해자는 자기도 모르게 최상위 이동으로 대상 홈페이지를 방문하고, 그 응답(JWT 포함)이 공격자가 미리 지정해 둔 캐시 슬롯에 저장됩니다. 공격자는 같은 URL을 다시 열어 피해자의 JWT를 그대로 읽어 계정을 탈취할 수 있었습니다.
- 원문에 따르면 이 결함은 HIGH로 분류돼 2,000달러가 지급됐습니다. 신고(3월 4일)부터 승인·포상(3월 19일), 수정 완료(5월 7일)까지의 타임라인이 원문에 기재돼 있습니다. ⚠️미확인 — 정확한 대상 서비스명·최종 패치 방식은 원문 저자가 기밀 유지를 위해 의도적으로 각색했다고 명시하고 있어, 이 초안에서 1차출처로 별도 확증하지 않았습니다.
짧은 논평
이 사례가 흥미로운 지점은 결함 하나가 아니라 두 결함의 “교집합”이라는 데 있습니다. 캐시 설정만 잘못됐다면 SameSite가 그 캐시된 값을 훔쳐가는 걸 막았을 것이고, SameSite 우회 방법만 알았다면 애초에 훔쳐올 만한 게 캐시에 없었을 겁니다. 저희가 다른 사례에서도 반복해 짚는 원칙과 같습니다 — 개별 방어선은 각자 제 역할을 하고 있어도, 그 경계가 서로 다른 계층(캐시 vs 쿠키 정책)에 있으면 공격자는 한쪽의 느슨함을 다른 쪽으로 밀어 넣는 지렛대로 씁니다. 캐시 규칙을 정할 때 “이 경로는 정적이라 캐시해도 된다”는 판단은 URL 패턴이 아니라 응답 본문에 실제로 무엇이 들어가는지를 기준으로 해야 한다는 교훈이기도 합니다.
References / 참고자료
- tinopreter (Clement Osei-Somuah), “Cracking SameSite for a $2,000 Web Cache Deception” — 이 글이 재구성한 원 분석. 캐시 발견 과정과 SameSite 우회 기법 전체를 다룸. https://medium.com/@tinopreter/cracking-samesite-for-a-2-000-web-cache-deception-746972278412
이 글은 위 원 분석을 우리 관점에서 다시 쓰고 짧은 논평을 더한 정보성 초안입니다. 특정 문장·요청 페이로드·스크린샷을 그대로 옮기지 않았으며, 작동하는 재현 절차는 싣지 않았습니다. ⚠️미확인 표시 항목은 발행 전 독립 팩트체크에서 확증·정정되어야 합니다.