diff --git a/.playwright-mcp/page-2026-09-03T06-48-11-954Z.yml b/.playwright-mcp/page-2026-09-03T06-48-11-954Z.yml deleted file mode 100644 index 12caa9e..0000000 --- a/.playwright-mcp/page-2026-09-03T06-48-11-954Z.yml +++ /dev/null @@ -1 +0,0 @@ -- generic [active] [ref=f3e1]: "{\"headers\":{\"host\":[\"app1.hyeonworks.com\"],\"user-agent\":[\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36\"],\"accept\":[\"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\"],\"accept-encoding\":[\"gzip, deflate, br, zstd\"],\"accept-language\":[\"en-US,en;q=0.9\"],\"priority\":[\"u=0, i\"],\"sec-ch-ua\":[\"\\\"Chromium\\\";v=\\\"152\\\", \\\"Not?A_Brand\\\";v=\\\"24\\\", \\\"Google Chrome\\\";v=\\\"152\\\"\"],\"sec-ch-ua-mobile\":[\"?0\"],\"sec-ch-ua-platform\":[\"\\\"Linux\\\"\"],\"sec-fetch-dest\":[\"document\"],\"sec-fetch-mode\":[\"navigate\"],\"sec-fetch-site\":[\"none\"],\"sec-fetch-user\":[\"?1\"],\"upgrade-insecure-requests\":[\"1\"],\"x-forwarded-host\":[\"app1.hyeonworks.com\"],\"x-forwarded-port\":[\"443\"],\"x-forwarded-proto\":[\"https\"],\"x-forwarded-server\":[\"traefik-697889c85-g7xpp\"],\"x-real-ip\":[\"100.123.124.30\"]},\"remoteAddr\":\"100.123.124.30\",\"localAddr\":\"10.42.0.10\",\"scheme\":\"https\",\"secure\":true,\"serverName\":\"app1.hyeonworks.com\",\"serverPort\":443,\"requestUrl\":\"https://app1.hyeonworks.com/api/echo\"}" \ No newline at end of file diff --git a/docs/evidence/two-hop-proxy-headers/03-browser-https-vs-app-http.png b/docs/evidence/two-hop-proxy-headers/03-browser-https-vs-app-http.png deleted file mode 100644 index 1f8a03a..0000000 Binary files a/docs/evidence/two-hop-proxy-headers/03-browser-https-vs-app-http.png and /dev/null differ diff --git a/docs/evidence/two-hop-proxy-headers/README.md b/docs/evidence/two-hop-proxy-headers/README.md index 922a084..d4d98d8 100644 --- a/docs/evidence/two-hop-proxy-headers/README.md +++ b/docs/evidence/two-hop-proxy-headers/README.md @@ -7,9 +7,12 @@ | 파일 | 내용 | |---|---| -| `01-environment.txt` | 세 계층의 설정 스냅샷 | -| `02-measurements.txt` | 정상 경로·대조 실험·위조 테스트·분배·리다이렉트 | -| `03-browser-https-vs-app-http.png` | 브라우저와 앱의 인식 차이 (Playwright) | +| `01-environment.txt` | 수정 전 세 계층의 설정 스냅샷 | +| `02-measurements.txt` | 수정 전 측정 · 대조 실험 · 위조 테스트 · 분배 | +| `stage-a-nginx-fixed.png` | A 단계 브라우저 화면 | +| `stage-b-traefik-trusts.png` | B 단계 브라우저 화면 | +| `stage-c-resolved.png` | C 단계 브라우저 화면 | +| `04-after-fix.txt` | 수정 후 측정 · 위조 테스트 · 분배 | --- @@ -65,25 +68,22 @@ nginx를 우회해 Traefik에 직접 요청하면서 올바른 헤더를 명시 ## 브라우저 증거 -`03-browser-https-vs-app-http.png` +스크린샷은 모두 **브라우저가 `/api/echo` 응답을 렌더링한 실제 화면**이다. +앱이 정렬된 JSON을 내보내도록 `spring.jackson.serialization.indent-output`을 +켜두었으므로 브라우저의 JSON 뷰어 설정과 무관하게 동일하게 읽힌다. -같은 요청을 두 관점에서 나란히 찍었다. +세 장은 **같은 요청을 세 가지 설정 상태에서** 찍은 것이다. -| 브라우저 | 앱(파드) | -|---|---| -| `location.href` = `https://app1.hyeonworks.com/api/echo` | `requestUrl` = `http://app1.hyeonworks.com/api/echo` | -| `protocol` = `https:` | `scheme` = `http` | -| `isSecureContext` = `true` | `secure` = `false` | +| 파일 | 켜진 스위치 | 화면에서 확인할 것 | +|---|---|---| +| `stage-a-nginx-fixed.png` | nginx 만 | `x-forwarded-proto: http` — Traefik 이 덮어씀 | +| `stage-b-traefik-trusts.png` | nginx + Traefik | **헤더는 `https`인데 `scheme: http`** | +| `stage-c-resolved.png` | 셋 다 | `scheme: https`, `secure: true` | -**브라우저는 TLS로 정상 접속했는데 앱은 평문 HTTP 요청으로 인식한다.** -이 상태에서 세션 쿠키에는 `Secure`가 붙지 않고, OAuth2 `redirect_uri`는 -`http://`로 생성된다. - -Playwright로 페이지에 접속한 뒤 `location` 값과 응답 JSON을 한 화면에 -렌더링해 촬영했다. 스크린샷은 뷰포트만 담기고 주소창은 담기지 않으므로, -브라우저 측 값을 페이지 안으로 끌어와 대조가 한 장에 들어오게 했다. - ---- +**`stage-b`가 가장 중요한 한 장이다.** `x-forwarded-proto: https`가 앱에 +도착해 있는데도 `scheme: http`, `secure: false`, `requestUrl: http://...`다. +**헤더가 도착하는 것과 앱이 그것을 읽는 것은 다른 문제**임을 한 화면이 +보여준다. ## 정상으로 확인된 것 diff --git a/docs/evidence/two-hop-proxy-headers/stage-a-nginx-fixed.png b/docs/evidence/two-hop-proxy-headers/stage-a-nginx-fixed.png index 1dbe55b..5341f52 100644 Binary files a/docs/evidence/two-hop-proxy-headers/stage-a-nginx-fixed.png and b/docs/evidence/two-hop-proxy-headers/stage-a-nginx-fixed.png differ diff --git a/docs/evidence/two-hop-proxy-headers/stage-b-traefik-trusts.png b/docs/evidence/two-hop-proxy-headers/stage-b-traefik-trusts.png index 80aa947..3dbbf3a 100644 Binary files a/docs/evidence/two-hop-proxy-headers/stage-b-traefik-trusts.png and b/docs/evidence/two-hop-proxy-headers/stage-b-traefik-trusts.png differ diff --git a/docs/evidence/two-hop-proxy-headers/stage-c-resolved.png b/docs/evidence/two-hop-proxy-headers/stage-c-resolved.png index 90d27c8..fd0e73a 100644 Binary files a/docs/evidence/two-hop-proxy-headers/stage-c-resolved.png and b/docs/evidence/two-hop-proxy-headers/stage-c-resolved.png differ