뭉크테크
Using web cache poisoning to exploit DOM-based vulnerabilities 본문
주제 설명
웹사이트가 서버에서 JSON 같은 데이터를 받아서 화면에 보여주거나 사용하는데,
이걸 검증 없이 바로 DOM에 넣는 경우 문제가 생긴다.
거기에 웹 캐시 포이즈닝 기법까지 섞으면?
공격자가 모든 사용자에게 악성 JSON을 먹일 수 있게 되는 것이다.
🍱 비유로 설명: 식당과 배달
생각해봐.
너가 단골로 가는 식당이 있는데, 이 식당은 항상 주문한 대로 음식을 정해진 배달 상자에 넣어서 줘.
근데 배달 시스템이 이상해서,
누군가 몰래 **"짜장면" 박스에 "상한 음식"**을 넣었는데,
그걸 모든 짜장면 주문한 사람에게 똑같이 배달하고 있는 거야.
게다가, 식당은 그걸 그냥 검사도 안 하고, "이게 짜장면이지 뭐~" 하고 그대로 손님 접시에 담아줘.
🧨 그리고 그 상한 음식 안에 "냉장고 열면 터지는 폭탄"이 들어있으면?
=> 손님(=웹사이트 사용자)의 브라우저에서 악성 스크립트가 실행되는 거야.
🧪 기술적으로 설명: 흐름 정리
1. 공격자가 캐시를 오염시켜서,
어떤 경로(/api/geolocate.json 같은 곳)에 이런 응답을 넣어둠:
{ "someProperty": "<svg onload=alert(1)>"
2. 웹사이트 프론트엔드는 그 JSON을 가져와서,이렇게 처리함. 검증 없이 DOM에 박음
const el = document.createElement("div");
el.innerHTML = response.someProperty; // <-- 여기가 문제
document.body.appendChild(el);
3. 브라우저는 그걸 보고 "어? SVG 태그네~ onload 있네?"
→ 악성 스크립트 실행 (예: alert(1))
🤝 CORS 활용
브라우저는 기본적으로 다른 도메인에서 가져온 데이터는 접근 못하게 막아
= 이게 바로 SOP (동일 출처 정책)
근데 JSON을 다른 도메인에서 가져오려면, 서버가 명시적으로 이렇게 허락해야 돼:
👉 그래서 공격자가 JSON 응답을 조작하면서 CORS 헤더도 같이 붙임
그럼 브라우저가 "오케이~ 접근해도 된다네?" 하고 받아들이는 것,
✅ 결론 – 이걸 쉽게 말하면?
- 웹사이트가 외부에서 데이터를 받아올 때
- 그걸 아무 검사 없이 DOM에 넣으면
- 공격자가 그 데이터를 중간에 바꿔치기해서 악성 스크립트를 넣을 수 있음
- JSON, JavaScript, 이미지, CSS 이런 것 다 포함이야
- CORS는 그 데이터를 브라우저가 받아들이게 만드는 도장(허가증) 같은 거고,
- 캐시 포이즈닝은 그 허가증이 있는 악성 음식을 모든 사람에게 뿌리는 방법인 거지
기타 전제로 알고 있어야하는 지식들 혹은 의문점 해결
🔐 기본 전제: SOP는 브라우저의 보안 정책
| SOP (Same-Origin Policy) | "다른 origin의 리소스에는 접근할 수 없다"는 브라우저가 자체적으로 적용하는 보안 정책 |
| 적용 대상 | 브라우저에서 실행되는 JavaScript (XSS, fetch, XMLHttpRequest 등) |
| 보호 대상 | 사용자 (특히 쿠키/세션 등 자동으로 따라가는 민감한 데이터 보호 |
✅ SOP는 서버랑 무관해. 서버가 뭘 설정 안 해도 브라우저가 알아서 차단함.
🚦 CORS: SOP의 예외를 선언하는 서버측 약속
🌍 근데 요즘은 마이크로서비스 시대라...
- 프론트: app.example.com
- API 서버: api.example.com
origin 다름 → SOP에 의해 브라우저는 막음
하지만 이건 우리가 의도한 정상적인 요청임
그래서 등장한 게…
🚦 CORS: SOP의 예외를 선언하는 서버측 약속
개념설명
| CORS (Cross-Origin Resource Sharing) | SOP로 막히는 요청 중, 특정 조건 하에 허용해주는 프로토콜 |
| 주체 | 서버가 응답 헤더로 명시함 (브라우저가 그걸 해석해서 허용 여부 판단) |
Access-Control-Allow-Origin: https://app.example.com
브라우저는 이걸 보고 "오케이, 너는 이 origin 허용했구나" 하고 허용해주는 거지.
💡 요약 구조 흐름
- 프론트는 항상 SOP에 의해 막힐 수 있는 위험을 가짐
- 서버는 그걸 CORS 헤더로 예외적으로 풀어줌
- 브라우저는 그걸 읽고 판단해서 허용하거나 막음
⚠️ 보충설명 하나만
Q. 프론트는 그냥 fetch 날리는 거고,
SOP 적용은 브라우저가 막는 거지, 프론트 코드가 막는 건 아니야.
그러니까 "프론트가 SOP 정책을 받는다"는 말보다는
**"브라우저가 프론트의 요청을 SOP로 검사하고 제한한다"**가 더 정확해.
"공격자가 어떻게 하면 서버가 이런 JSON(오류 응답)을 반환하도록 만들 수 있느냐?"
🔥 1. 기본 전제: 서버가 사용자 입력을 반영하는 구조여야 함
공격자는 입력값이 서버 응답에 포함되는 포인트를 찾아야 해. 예를 들어 이런 API가 있다고 가정하자:
- GET /api/user?name=Alice
이걸 요청하면 서버가 다음과 같이 응답한다고 치자:
- { "username": "Alice" }
✅ 2. 공격자는 이걸 조작해본다
- GET /api/user?name=<svg onload=alert(1)> HTTP/1.1
Host: example.com
서버가 따로 필터링 안 하고, 사용자 입력을 그대로 JSON에 넣어버리면?
- { "username": "<svg onload=alert(1)>" } 라고 반환
💣 그럼, 어떻게 이런 구조를 찾아낼까?
🔍 1. 공격자가 할 탐색 과정
- 다양한 파라미터에 "><svg/onload=alert(1)> 같은 페이로드 넣어서 서버 응답 검사
- User-Agent, Referer, Host, X-Forwarded-Host 등의 헤더도 조작해 봄
- 응답 JSON에 이 값들이 포함되기만 하면 성공
🎯 3. 캐시 오염 조건까지 맞추기
서버가 저 응답을 공용 캐시에 저장하는 구조라면, 이 오염된 응답은 다른 사용자에게도 반환됨.
공격 예시 시나리오 (전체 흐름):
1. 공격자가 요청:
GET /api/user?name=<svg onload=alert(1)>
Host: example.com
2. 응답:
{ "username": "<svg onload=alert(1)>" }
3. 서버가 캐시에 저장 (예: CDN이 `/api/user?name=...` 를 캐싱함)
4. 피해자:
GET /api/user?name=<svg onload=alert(1)>
or 심지어
GET /api/user?name=Alice ← 캐시 키가 name 무시하면 이것도 먹힘
5. 응답:
{ "username": "<svg onload=alert(1)>" }
6. 프론트:
document.getElementById("profile").innerHTML = data.username;
→ XSS 터짐
😈 더 지능적인 예시
만약 서버가 쿼리 파라미터는 필터링해놨다면, 공격자는 헤더를 이용할 수도 있어:
- GET /api/user HTTP/1.1
Host: example.com
X-User: <svg onload=alert(1)>
왜 그게 캐싱되며, 왜 그런 요청이 응답에 반영되는가?
🚨 먼저 핵심 요약
| 질문 | 답변 |
| ❓ 왜 그게 캐싱됨? | 캐시 서버(CDN 등)가 정확한 캐시 키를 사용하지 않거나, 쿼리 파라미터 무시 또는 헤더 포함 등으로 동작하기 때문 |
| ❓ 왜 그런 이상한 요청이 응답에 반영됨? | 서버 코드가 입력 검증 없이 사용자 입력을 그대로 응답에 넣는 경우 (API 응답 포함, 템플릿 주입 등) |
정상적인 CDN, reverse proxy, 웹서버는 다음 기준으로 캐시 키를 만든다:
캐시 키 = [요청 메서드] + [경로] + [쿼리 파라미터] + [Host 헤더] + [캐시 정책 헤더]
💣 문제 상황 1: 캐시 키가 단순함
- GET /api/user?name=Alice
+ GET /api/user?name=<svg onload=alert(1)>
- 캐시 서버가 ?name=... 같은 쿼리 파라미터를 무시하도록 구성돼 있으면?
둘 다 같은 캐시 키로 인식됨.
➡️ 즉, 오염된 응답을 캐시에 저장하면 정상적인 사용자도 그 오염된 응답을 받게 되는 거지.
✅ 캐시 키(Cache Key)란?
"CDN이나 프록시 서버가 어떤 요청에 대해 캐시를 저장하거나 꺼낼 때 기준으로 삼는 요소"야.
즉, 어떤 요청이 왔을 때:
"이거 예전에 저장해둔 거 있어?"
하고 찾아볼 때 무엇을 기준으로 찾을지 정하는 기준값이 바로 캐시 키.
GET /api/profile?user=jaemin
캐시 서버는 이걸 보고 이렇게 판단함:
"오, 이 요청은 GET /api/profile?user=jaemin이니까
캐시 키는 /api/profile?user=jaemin으로 저장해야지~"
근데!!
CDN 설정에 따라 ?user=jaemin 같은 쿼리 파라미터를 무시하면??
"음... 그냥 /api/profile로 캐시 키 만들자"
그럼 이런 위험한 상황이 생겨:
| 요청 | 실제 캐시 키로 저장됨 | 결과 |
| /api/profile?user=jaemin | /api/profile | 정상 응답 |
| /api/profile?user=<svg onload=alert(1)> | /api/profile | ❗️악성 응답 덮어쓰기 |
| /api/profile?user=admin | /api/profile | ❗️오염된 응답 전달됨 |
➡️ 캐시 키가 제대로 구성되지 않으면, 아무 사용자나 오염된 캐시 응답을 받게 되는 거야.
🔍 구성 요소
캐시 키는 보통 이렇게 구성할 수 있어:
| 요소 | 예시 | 캐시 키 포함 여부 |
| HTTP 메서드 | GET, POST | 보통 O (POST는 캐싱 안함) |
| 호스트 (Host 헤더) |
example.com | 보통 O |
| 경로 | /api/profile | O |
| 쿼리 파라미터 | ?user=bob | CDN 설정에 따라 다름 |
| 헤더 | Authorization, Cookie, X-User, 등 | 기본은 X |
| 쿠키 | session=abc123 | 기본은 X |
🔐 방어적 캐시 키 설계 예시
CDN 설정에서 이렇게 할 수 있어:
- Cache Key = URL Path + Query String + Host
- Vary by Header: Authorization, X-Requested-With
- POST는 캐시하지 않음
- JSON은 Cache-Control: no-store로 캐시 아예 금지
이렇게 하면 공격자가 쿼리 파라미터나 헤더를 바꿔도 캐시 오염 못하게 할 수 있어.
🎯 CDN / NGINX / 웹 캐시에서 설정하는 방식
1. 쿼리 스트링 포함 여부
CDN은 기본적으로 ?user=jaemin 같은 쿼리 스트링을 무시할 수도 있어.
- CloudFront의 기본 캐시 정책: 쿼리 스트링 무시
- 쿼리를 캐시 키에 포함하려면:
→ Cache Policy → Query strings → Include all 설정
💣 무시하면? → /api/data?user=jaemin, /api/data?user=admin 전부 같은 캐시 키로 간주돼서 오염됨.
2. 헤더 포함 여부
Authorization, User-Agent, Accept-Language 같은 헤더는 기본적으로 캐시 키에 포함되지 않음.
근데 이걸 포함해야 할 때가 많아.
예: 사용자 인증 헤더에 따라 응답 달라지는 경우
→ Vary: Authorization 또는
CDN 정책에서 "이 헤더를 캐시 키에 포함시켜"라고 설정해야 함
📌 CloudFront에서는 이렇게 함:
📌 NGINX에서는 Vary 헤더로 알려줄 수 있어:
3. 쿠키 포함 여부
이것도 마찬가지야.
CloudFront나 NGINX는 기본적으로 쿠키를 무시함.
→ session=abc123 같은 쿠키 값은 캐시 키에 안 들어감.
근데 쿠키에 따라 응답이 달라진다면?
Vary: Cookie
또는 CloudFront 정책에서 Cookie 포함 설정 필요
👍 안전한 캐시 키 구성 예:
GET + /api/data + ?lang=ko + Authorization 헤더 + sessionid 쿠키
이렇게 구성해야 사용자의 인증 상태나 언어 설정에 따라 정확히 다른 캐시 사용 가능
🔥 NGINX에서 커스텀 캐시 키 만들기 (proxy_cache_key)
location /api/ {
proxy_cache_key "$scheme$host$request_uri$http_authorization";
}
➡️ 이건
- http,
- example.com,
- /api/data?lang=ko,
- Authorization 헤더
를 모두 키에 넣는 방식이야.
❗실제 구성에서는 $cookie_sessionid 같은 것도 쓸 수 있음.
🔐 보안 측면에서 꼭 넣어야 할 것들
| 상황 | 캐시 키에 꼭 넣어야 하는 요소 |
| 사용자 인증됨 | Authorization 헤더 / session 쿠키 |
| 쿼리로 다른 데이터 반환됨 | query string 전체 or 일부 |
| 지역/언어별 응답 다름 | Accept-Language or lang 쿼리 |
| 브라우저별 응답 다름 | User-Agent |
그럼 쿠키나 헤더 같은 개별 사용자을 구분할 수 있는 무언가를 캐싱 요소 잡게 되면, 그러면 각 사용자마다 개별적으로 웹캐싱이 되어야 할텐데, 그럼 그만큼 개별로 CDN 서버가 저장해야하닌까 부하가 심해지지 않나
맞아, 쿠키나 Authorization 같은 사용자 고유한 정보를 캐시 키로 쓰게 되면, 사용자 수만큼 캐시가 따로 생기고 → 캐시 효율은 개박살 나고 → CDN 부하도 올라가.
그걸 한 줄로 요약하면:
✅ 보안 vs 성능의 트레이드오프
🔥 왜 성능이 떨어지게 되냐면
예를 들어 봐봐:
- 사용자 100만 명
- 각자 Authorization 헤더 다 다름
- 그럼 /user/profile 요청에 대해 캐시 키가 100만 개야
➡️ 그럼 CDN이나 프록시 서버는 100만 개 캐시 항목을 따로따로 저장해야 함
- 메모리/디스크 사용량 급증
- LRU 같은 캐시 제거 정책 발동 → 금방 지워짐
- 캐시 히트율 감소 (cache hit rate ↓)
즉,
🔺 보안을 위해 개별 사용자 단위로 분리했더니
🔻 캐싱 효율이 낮아지고 서버 리소스만 죽어나감
✅ 그럼 어떻게 균형을 맞추냐?
▶ 1. 개인화가 필요 없는 리소스만 캐시
예:
- JS, CSS, 이미지 → 전부 사용자에 무관 → 헤더 무시하고 캐시함
- 이런 애들은 Cache-Control: public, max-age=31536000 같은 걸로 설정함
▶ 2. API 결과 중 일부만 개인화하면, Vary 헤더를 잘 씀
Vary: Authorization 이렇게 쓰면,
이 응답은 Authorization 헤더 값에 따라 다르게 캐시됨!
하지만 이건 여전히 성능 문제는 존재해.
▶ 3. Edge-side includes (ESI) 같은 기술
- HTML 중 일부만 동적으로 바꿔줌
- CDN에서 템플릿 캐시 + 일부만 사용자 정보로 동적 구성
<html>
<body>
<h1>오늘의 인기글</h1>
<!-- ESI 태그로 사용자 정보는 따로 렌더링 -->
<esi:include src="/user/profile-fragment" />
</body>
</html>
이렇게 하면:
- /user/profile-fragment 는 사용자마다 다르니까 캐싱 안 함
- 나머지 HTML은 CDN이 캐시 가능
➡️ 캐시 효율도 잡고, 보안도 지킬 수 있는 완전한 하이브리드 구조지
📦 실제로 ESI 누가 쓰냐?
- Akamai, Fastly, Cloudflare 일부 지원
- Varnish (리버스 프록시 캐시 서버)에서 아주 많이 씀
- AWS CloudFront는 ESI 자체 지원은 안 하지만, Lambda@Edge 같은 기능으로 유사 구현 가능
🧠 그럼 이걸 왜 잘 안 쓰냐?
단점도 있어:
| 단점 | 설명 |
| 구현 난이도 ↑ | HTML을 쪼개고, 각 조각을 API화해야 함 |
| 성능 기대치 조절 필요 | 조각마다 요청 발생 → 요청 수 ↑ |
| ESI 지원 CDN이 한정적 | 일부 CDN만 네이티브 지원함 |
🔧 그럼 현실적으로는 어떻게 써?
대기업 기준이라면:
- 페이지 전체는 CDN 캐시
- <esi:include> 또는 JS로 동적 API 요청
- 사용자 이름, 장바구니, 알림 같은 정보만 실시간 API로 가져와서 렌더링
프론트 단에서도 요즘은 React/Vue에서 SSR + Hydration으로 구현하거나, Next.js에서 getServerSideProps() 따로 써서 똑같이 흉내내지.
▶ 4. 쿠키·Authorization 있는 요청은 아예 캐시 안 하기도 함
- 이러면 캐시 무효화(bypass) 해서 보안 우선으로 감
- 즉, 해당 요청은 캐시를 사용하지 말고, 백엔드에 직접 물어보라는 의미
- 왜냐하면 이런 요청은 어차피 사용자마다 응답이 다르니까, 굳이 캐시할 필요가 없음
- 대신 성능은 포기해야지
💣 요약 정리
| 항목 | 내용 |
| ✔ 캐시 키에 사용자 고유 요소 포함 | 보안 안전함 (사용자마다 캐시 분리) |
| ✘ 캐시 키에 사용자 고유 요소 포함 | 캐시 메모리 낭비, 성능 저하, 히트율 낮아짐 |
| 🤔 해결법 | 필요한 경우에만 정교하게 개인화, 나머지는 퍼블릭 캐시로 최대한 분리 |
실습 시나리오

이번 공격도 캐시 포이즈닝 어택에 쓰일만한 취약점이 있는지 확인하고자 대상 서버 홈페이지에 param miner 툴을 적극 활용해봤다.

그 결과, x-forwarded-host 헤더가 반응이 있다는 것을 알게 된다. 즉, 해당 헤더를 이용하여 웹 캐싱 포이즈닝을 시도하고자 한다.

요청 파라미터에 cb=1234 를 삽입하여 캐시버스터를 만들어 캐시 포이즈닝 테스트를 돌려보고자 했고, X-Forwarded-Host: example.com 를 요청 헤더에 삽입하여 응답을 살펴보았다. 그 결과, 응답 헤더의 스크립트 부분을 보면,
- data = {"host":"example.com ","path":"/"} 를 볼 수 있다.
즉, 삽입이 됐긴 됐다만, js 파일이 아닌, 데이터를 저장하는 것으로 보인다. 그리고 그 데이터라 함은 host를 키로 하는 example.com 이라는 호스트 도메인 값을 저장하는 것으로 보인다.

그리고 data 라는 문구를 해당 응답에서 검색해본 결과, data 라는 변수를 객체로서 활용하고, 이어서 /resources/json/geolocate.json 이라는 파일을 참조하는 것을 알게된다. 즉, data에 담긴 호스트 즉, example.com 사이트에 접속하여 /resources/json/geolocate.json 파일을 참조하여 얻어낸 json 데이터를 initGeoLocate 함수에서 인자 값으로 쓰이는 것으로 보였다. 해당 함수는 따로 어딘가에 정의되어 쓰이는 것으로 보였다. 즉, js 파일에서 정의한 함수로 보인다.

대상 사이트에 접속하면 저렇게 geolocate.js 파일을 요청하고 받는 것을 볼 수 있다. 그리고 해당 파일의 내용을 보면 위와 같이 나와있고, 이때, initGeoLocate 함수가 정의된 것을 볼 수 있다. 그리고 해당 함수에 주석을 달아 아래에 설명을 이어간다.
// 사용자의 위치 정보를 받아와서 무료 배송 메시지를 표시하는 함수
function initGeoLocate(jsonUrl) {
// 주어진 URL(jsonUrl)로부터 위치 정보를 요청함
fetch(jsonUrl)
.then(r => r.json()) // 응답(response)을 JSON 형식으로 파싱
.then(j => {
// id가 'shipping-info'인 요소를 가져옴 (메시지를 삽입할 대상)
let geoLocateContent = document.getElementById('shipping-info');
// 배송 아이콘 이미지를 생성
let img = document.createElement("img");
img.setAttribute("src", "/resources/images/localShipping.svg"); // 이미지 경로 설정
geoLocateContent.appendChild(img); // shipping-info 요소에 이미지 추가
// 사용자의 국가명을 포함한 텍스트 메시지를 담을 div 생성
let div = document.createElement("div");
div.innerHTML = 'Free shipping to ' + j.country; // JSON 응답에서 country 값 추출하여 문장 완성
geoLocateContent.appendChild(div); // shipping-info 요소에 텍스트 메시지 추가
});
}
저기서 jsonUrl 이라 함은 앞서 살펴본 /resources/json/geolocate.json 데이터를 말하는 듯하다. 즉, 해당 함수를 실행시키전에, 일단 저 데이터를 서버에게 요청하는 것인데, 그에 대한 응답 형태는 바로 다음 아래와 같다.

위 응답을 보면,
{
"country": "United Kingdom"
}
라는 형태의 응답을 받는 것이다.

그리고 그 응답이 앞 단계를 거쳐 결국, j.country 라는 것을 통해 json 응답에서 country 라는 key를 불러와 값으로 "United Kingdom" 을 불러오는 것이다. 그런데 위 그림을 잘 보면, innerHTML 사용하고 있다. 즉, 문자열이 아니라 HTML 코드 자체로 파싱되는 것을 알 수 있다. 이는 DOM XSS 공격을 제대로 살릴 수 있는 취약한 코드이다. 즉, /resources/json/geolocate.json 데이터 안에 "country": 라는 키의 값으로 태그를 활용한 악성스크립트를 삽입하면 된다.

참고로, 해당 코드의 실행 결과는 위 그림에서의 빨간색 박스친 저 Free... 라고 하는 부분이다.

그래서 json 응답을 해주는 내 악성 사이트에서 응답 파일 위치는 /resources/json/geolocate.json 라고 설정하였고, 바디 부분은
{
"country": "<img src=1 onerror=alert(document.cookie) />"
}
로 하였다. 그래야 브라우저가 initGeoLocate 함수를 호출할 때, 이 json 데이터를 활용하여 실행시킬 것이고, 이때, "
"</img src=1 onerror=alert(document.cookie) > " 라는 값을 innerHTML 함수의 매개변수로 들어가 브라우저에서 동적으로 랜더링되어 해당 스크립트를 실행시키기 때문이다.


그렇게 캐시 포이즈닝을 다시 한 후 해당 페이지를 리로딩시켜봤지만, 위와 같은 에러가 발생하였다. CORS관련 정책 위배 문가 나타났다. 해당 원인으로는 요청했던 자원에 대한 헤더에 Access-Control-Allow-Origin 헤더가 없는 것이 원인이다.

기존 요청(GET /resources/json/geolocate.json)을 잠깐 살펴보자. Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors 이라고 나온다. 즉, 현재 페이지와 같은 origin에서, CORS 준수하는 JavaScript fetch() 요청을 통해, **데이터(JSON 등)**를 불러오고 있어요. 라는 의미이다. 해당 파라미터의 다른 값들도 살펴보자
Sec-Fetch-Site
| 값 | 의미 |
| same-origin | 현재 페이지와 완전히 같은 origin에서 보낸 요청 (보통 안전한 요청) |
| same-site | 같은 도메인이지만 서브도메인이 다른 경우 (예: a.example.com → b.example.com) |
| cross-site | 완전히 다른 origin |
| none | 브라우저가 출처 정보를 제공하지 않음 (비정상적 상황) |
Sec-Fetch-Mode
| 값 | 의미 |
| cors | fetch() 또는 XMLHttpRequest 등에서 cross-origin 요청 (CORS 정책 적용 대상) |
| no-cors | 최소한의 요청만 허용됨. 응답 body 접근 불가 |
| same-origin | fetch 대상이 same-origin일 때 |
| navigate | 문서 전체를 불러올 때 (예: <a href>, window.location) |
| websocket | 웹소켓 요청일 때 |
헤더보안에 주는 시사점
| Sec-Fetch-Site | 요청 출처 확인, CSRF/XSS 시 공격 탐지에 도움 |
| Sec-Fetch-Mode | CORS 적용 대상인지 판단 가능 |

그래서 응답 헤더에 Access-Control-Allow-Origin: *를 삽입하여 저장한 뒤, 다시 캐시포이즈닝을 하였다. 그 결과,

응답이 잘 왔음을 위그림의 빨간 박스 내용을 확인해보면, 알 수 있다. 즉, Sec-Fetch-Site가 cross-site로 되어 있어도, 응답에서 헤더 Access-Control-Allow-Origin: * 를 설정해뒀기 때문이다. 즉, 다른 어떤 사이트들이 요청해도 응답해준다는 헤더를 응답 바디 부분에 삽입하였기때문이다.

그 결과, 캐시 버스터 테스트에서 웹 캐시 포이즈닝 어택이 성공한 것을 확인하였다.

캐시버스터를 제거하여 실제 본 대상 서버에 웹 캐시 포이즈닝 어택을 실시하였다. 그 결과, 제대로 캐싱이 되었는지, 아래 그림을 보면, 대상 사이트 리로딩시 심어둔 악성 스크립트가 실행된 것을 확인할 수 있다.
