docs: retake staged evidence screenshots with indented output

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
DongHyeonka
2026-09-03 15:52:47 +09:00
co-authored by Claude Opus 5
parent b708c8d503
commit 98874b0c6c
6 changed files with 19 additions and 20 deletions
@@ -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\"}"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

+19 -19
View File
@@ -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://...`다.
**헤더가 도착하는 것과 앱이 그것을 읽는 것은 다른 문제**임을 한 화면이
보여준다.
## 정상으로 확인된 것
Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 90 KiB