refactor: 문서 개선 중
This commit is contained in:
+70
@@ -0,0 +1,70 @@
|
||||
---
|
||||
kind: CASE
|
||||
slug: a-boundary-that-leaks-only-under-load
|
||||
title: 부하 아래에서 지키라고 만든 경계가 부하 아래에서만 샌다
|
||||
topic: duplicate-mechanisms
|
||||
topicName: 중복 장치 — 조립된 쪽이 약한 쪽일 때
|
||||
project: clean-architecture-backend-template
|
||||
status: 게시 전
|
||||
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
||||
rootTreeNode: case:a-boundary-that-leaks-only-under-load
|
||||
source:
|
||||
- final/document.md#8-2
|
||||
- final/document.md#a20
|
||||
- final/document.md#5-5
|
||||
- final/document.md#8-2 항목 6
|
||||
- final/document.md#a20 §7
|
||||
---
|
||||
|
||||
# 부하 아래에서 지키라고 만든 경계가 부하 아래에서만 샌다
|
||||
|
||||
같은 가족의 구현 중 하나가 제한값 검사와 증가를 분리해서 수행하고, 다른 구현은 CAS loop로 두 동작을 하나의 원자적 갱신으로 묶는다. 정적 코드 비교로 경계 차이는 확인했지만 실제 경쟁 부하는 재현하지 않았다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **중복 장치를 찾으면 어느 쪽이 조립됐는지 먼저 확인한다**
|
||||
같은 목적의 구현이 둘일 때 실제 호출 경로가 어느 쪽인지 확인하는 기준이다.
|
||||
- **CAS tuple과 update count**
|
||||
경쟁 상태에서 읽기와 쓰기를 분리하지 않는 상태 전이 규칙을 설명한다.
|
||||
|
||||
## 문제
|
||||
|
||||
제한값을 읽어 “아직 여유가 있다”고 확인한 뒤 별도 연산으로 값을 증가시키면 두 worker가 같은 이전 값을 동시에 읽을 수 있다. 각 worker는 개별적으로는 검사를 통과하지만 합산 결과는 경계를 넘을 수 있다.
|
||||
|
||||
같은 가족에는 CAS loop로 읽은 revision과 기대 값을 WHERE 조건에 포함해 한 worker만 갱신하도록 만든 구현이 있다.
|
||||
|
||||
## 결론
|
||||
|
||||
이 경계는 단일 worker 테스트만으로는 충분히 검증되지 않는다. 제한 확인과 증가가 하나의 원자적 상태 전이가 아니면 경쟁 부하에서만 초과가 나타날 수 있다.
|
||||
|
||||
현재 판정은 두 구현의 코드 구조 비교다. 실제 concurrent load를 걸어 초과를 재현하지 않았으므로 발생 빈도나 임계 동시성은 주장하지 않는다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
sourceRevision : 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
||||
현재 source repository 재대조 : UNVERIFIABLE
|
||||
확인 방식 : 동일 가족 구현의 정적 비교
|
||||
|
||||
## 재현 조건
|
||||
|
||||
1. 제한 확인과 증가가 분리된 구현의 read/write 순서를 확인한다.
|
||||
2. 같은 가족의 CAS 구현이 기대 값과 revision을 갱신 조건에 포함하는지 확인한다.
|
||||
3. source repository가 있는 환경에서는 두 worker 이상으로 같은 경계를 동시에 갱신해 실제 초과 여부를 측정한다.
|
||||
|
||||
## 본문
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
## 단일 요청에서 보이지 않는 이유
|
||||
|
||||
worker 하나만 실행하면 “읽기 → 검사 → 증가” 사이에 다른 쓰기가 끼어들지 않는다. 따라서 기능 테스트는 정상 범위만 관측할 수 있다.
|
||||
|
||||
## CAS 구현과 비교한다
|
||||
|
||||
대조 구현은 현재 값을 읽은 뒤 기대 revision을 포함한 갱신을 시도한다. 경쟁자가 먼저 값을 바꾸면 update count가 0이 되고 다시 읽어 판정한다. 이 차이가 두 구현의 concurrency 보장 차이다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
실제 부하에서 경계를 넘기는 실행은 이번 검토에서 재현하지 않았다. 코드 구조상 race 가능성을 확인한 상태다.
|
||||
|
||||
<!-- body:end -->
|
||||
+5
-5
@@ -26,7 +26,7 @@ source:
|
||||
- **중복 장치를 찾으면 어느 쪽이 조립됐는지 먼저 확인한다**
|
||||
이 사례가 그 규칙을 요구하는 형태다.
|
||||
- **클라이언트가 준 엔드포인트가 SSRF 가드가 아니라 약한 private 사본을 지났다**
|
||||
같은 형태가 알림 어댑터에서 나타난 사례다.
|
||||
알림 어댑터에서도 앞 단계가 정한 정책을 뒤 단계가 다시 덮어써 실제 관측값이 달라졌다.
|
||||
- **sanitize가 아니라 reject가 기본이다**
|
||||
두 필터가 서로 다른 답을 내는 규칙이다.
|
||||
|
||||
@@ -38,7 +38,7 @@ source:
|
||||
|
||||
## 결론
|
||||
|
||||
관측 가능한 자리에 도달하는 값은 전부 뒤 필터의 것이다. 응답 헤더와 MDC 와 접근 로그가 그렇다.
|
||||
최종 응답 헤더·MDC·접근 로그에는 앞 필터가 만든 값이 아니라 뒤 필터가 다시 쓴 값이 기록된다.
|
||||
|
||||
앞 필터가 남긴 요청 속성을 쓰는 곳이 없다. 접근자는 있는데 부르는 곳이 0 이다.
|
||||
|
||||
@@ -76,9 +76,9 @@ Spring Boot : 4.0.8
|
||||
|
||||
앞 필터는 자동설정이 조립한다. 서블릿 웹 애플리케이션 조건이 붙어 있고, 자동설정 등록 파일에 이름이 올라가 있다.
|
||||
|
||||
자동설정은 설정값을 그대로 넘긴다. 그 설정 레코드의 압축 생성자가 널을 거짓으로 접으므로, 아무것도 설정하지 않은 배포는 신뢰가 꺼진 상태로 돈다. 그 자리 주석이 이유를 적는다 — 자기 요청 id 를 고를 수 있는 호출자는 서로 다른 두 요청이 하나의 신원을 공유하게 만들 수 있고, 그것이 지원 조사가 남의 교신을 읽게 되는 경로라는 것이다.
|
||||
자동설정은 설정값을 그대로 넘긴다. 설정 record의 compact constructor는 null을 false로 바꾸므로 아무것도 설정하지 않은 배포에서는 신뢰 기능이 꺼진다. 해당 주석은 호출자가 요청 id를 직접 고르게 두면 서로 다른 두 요청이 하나의 신원을 공유할 수 있고, 지원 조사에서 다른 요청의 교신을 같은 요청으로 묶을 수 있다고 설명한다.
|
||||
|
||||
같은 기본값을 넘기는 무인자 생성자도 있지만 부르는 쪽은 테스트뿐이다.
|
||||
같은 기본값을 넘기는 무인자 생성자는 테스트에서만 호출된다.
|
||||
|
||||
## 같은 헤더 이름이 두 파일에 따로 있다
|
||||
|
||||
@@ -98,7 +98,7 @@ Spring Boot : 4.0.8
|
||||
|
||||
순서를 선언하지 않은 필터 빈에 Spring Boot 가 매기는 기본 순서는 가장 낮은 우선순위다. 분석 문서가 뒤 필터의 순서를 그 이름으로 적는 근거가 이것이다. 그래서 뒤 필터가 나중에 돌고, 두 필터가 쓰는 응답 헤더 설정은 덮어쓰기다.
|
||||
|
||||
## 관측 가능한 자리에는 뒤 필터의 값만 도달한다
|
||||
## 응답 헤더·MDC·접근 로그에는 뒤 필터 값이 기록된다
|
||||
|
||||
앞 필터는 자기 값을 요청 속성과 응답 헤더에 쓴다. 뒤 필터는 정화한 클라이언트 값을 같은 응답 헤더와 MDC 에 쓴다.
|
||||
|
||||
|
||||
+4
-4
@@ -27,7 +27,7 @@ source:
|
||||
## 관계
|
||||
|
||||
- **재시도 단위는 statement가 아니라 유스케이스 전체다**
|
||||
코디네이터가 구현하는 결정이고, 그 결정이 도는 자리는 다른 구현이다.
|
||||
코디네이터가 재시도 결정을 구현하지만 실제 배선된 호출 경로에서는 다른 구현이 실행된다.
|
||||
- **중복 장치를 찾으면 어느 쪽이 조립됐는지 먼저 확인한다**
|
||||
이 사례가 그 확인 절차를 요구한 형태다.
|
||||
- **@Bean이 있다는 것은 조립 증거가 아니다**
|
||||
@@ -59,9 +59,9 @@ source:
|
||||
|
||||
지워진 인터셉터 이름을 건드린 커밋은 넷이다. 하나가 설계 문서에 이름을 넣었고, 하나가 인터셉터와 그 테스트를 더했고, 하루 뒤 909 파일을 건드린 커밋이 둘 다 지웠다. 지운 커밋의 제목에는 삭제가 드러나지 않는다.
|
||||
|
||||
어드바이스를 얹을 자리가 없어서가 아니다. 같은 트리의 배출기와 정리기가 이미 트랜잭션 애너테이션으로 프록시되고, 인바운드 쪽은 이 애플리케이션이 직접 쓴 어드바이저를 갖고 있다. 빠진 것은 경로가 아니라 코디네이터를 그 경로에 얹는 클래스 하나다.
|
||||
AOP를 적용할 기반이 없는 것은 아니다. 같은 트리의 배출기와 정리기는 이미 트랜잭션 애너테이션으로 프록시되고, 인바운드 쪽에는 이 애플리케이션이 직접 만든 어드바이저가 있다. 빠진 것은 retry coordinator를 실제 호출 경로에 연결하는 advisor 또는 wrapper다.
|
||||
|
||||
판정은 P1 이다. 안정 등급으로 선언한 능력의 구현이 도달 불가이고, 그 자리에서 실제로 도는 것은 다른 값과 좁은 정책을 가진 다른 구현이다.
|
||||
판정은 P1이다. 안정 등급으로 선언한 retry coordinator에는 확인한 프로덕션 호출 경로가 없고, 실제 wired path에서는 다른 설정값과 더 좁은 정책을 가진 구현이 실행된다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
@@ -86,7 +86,7 @@ OpenJDK : 21.0.12
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
선언적 재시도 애너테이션이 지워졌고, 그 자리에 이유를 적은 문단이 남아 있다.
|
||||
선언적 재시도 애너테이션은 삭제됐고, 인접한 javadoc에는 왜 애너테이션 기반 재시도를 쓰지 않는지 이유가 적혀 있다.
|
||||
|
||||
## 삭제는 사고가 아니었다
|
||||
|
||||
|
||||
+6
-6
@@ -31,7 +31,7 @@ source:
|
||||
- **sanitize가 아니라 reject가 기본이다**
|
||||
강한 쪽 함수가 따르는 규칙이다.
|
||||
- **요청 식별자를 클라이언트가 고를 수 없다는 정책이 뒤에 도는 필터에 뒤집혔다**
|
||||
같은 형태가 웹 어댑터에서 나타난 사례다.
|
||||
웹 어댑터에서도 더 강한 공용 검증기가 있었지만 실제 호출 경로는 더 약한 private 검증을 사용했다.
|
||||
|
||||
## 문제
|
||||
|
||||
@@ -47,7 +47,7 @@ source:
|
||||
|
||||
값 타입은 그 함수를 부르지 않는다. 같은 파일의 약한 공개 함수도 부르지 않는다. 약한 쪽과 같은 논리를 private 메서드로 다시 썼고, 루프백 호스트 집합까지 같다. 그래서 약한 함수 이름으로 검색해도 이 호출처는 나오지 않는다.
|
||||
|
||||
이웃 호출처는 같은 결함을 이미 고쳤다. 웹훅 쪽 주석이 고치면서 무엇이 남았는지까지 적는다. 강한 가드는 바로 그 호출처를 위해 쓰였고 한동안 아무 데서도 불리지 않았으며, 자기 테스트는 통과하고 있었다는 것이다.
|
||||
이웃 호출처는 같은 결함을 이미 고쳤다. 웹훅 쪽 주석에는 공용 guard가 그 호출처를 위해 추가됐지만 한동안 실제 호출되지 않았고 자체 테스트만 통과했다는 이력이 적혀 있다.
|
||||
|
||||
호출처 도달을 고정하려고 만든 테스트가 있는데, 그 클래스의 머리글이 대상을 웹훅과 SES 둘로 적는다. 가드 자신의 javadoc 이 적은 둘은 웹푸시와 웹훅이다. 두 목록이 어긋나 있고, 그 테스트 파일에 웹푸시를 언급하는 줄은 0 이다.
|
||||
|
||||
@@ -86,7 +86,7 @@ OpenJDK : 21.0.12
|
||||
|
||||
## 그 검사는 자기 목적에 대해서는 옳다
|
||||
|
||||
메서드 javadoc 에는 다루는 위협이 경로상의 도청이고 루프백 엔드포인트에는 그 경로가 없다고 적혀 있다. 예외를 루프백으로 좁힌 것은 계약 시험이 진짜 소켓을 쓸 수 있게 하려는 것이다.
|
||||
메서드 javadoc은 경로상의 도청을 막기 위해 HTTPS를 요구하고, loopback endpoint는 그 위협 모델에서 제외한다고 적는다. loopback만 예외로 둔 덕분에 계약 테스트는 실제 소켓을 사용할 수 있다.
|
||||
|
||||
전송 보안 규칙으로서 이 판단은 유지된다. 다만 같은 모듈의 이웃 파일이 그 논리로 남은 결과를 주석에 적어 뒀고, 그 내용은 뒤에서 본다.
|
||||
|
||||
@@ -105,14 +105,14 @@ a server-side request forgery primitive
|
||||
|
||||
값 타입은 두 공개 함수 중 어느 것도 부르지 않는다. 약한 쪽과 같은 논리를 private 으로 다시 썼고, 루프백 호스트 집합 `127.0.0.1`, `::1`, `localhost` 까지 같다. 약한 함수 이름으로 검색하면 이 호출처는 드러나지 않는다.
|
||||
|
||||
호출처 도달을 고정하려고 만든 테스트도 있다. 그 클래스의 머리글이 대상을 웹훅 대상과 SES 엔드포인트 둘로 적는다. 가드 javadoc 이 적은 둘과 한 자리가 다르고, 그 파일에 웹푸시를 언급하는 줄은 0 이다.
|
||||
호출 경로를 고정하려고 만든 테스트도 있다. 클래스 머리글은 대상을 웹훅 target과 SES endpoint로 적고, 공용 guard javadoc의 대상 목록과 하나가 다르다. 해당 테스트 파일에서는 web push를 언급하지 않는다.
|
||||
|
||||
## 내부망 주소를 넣으면 실제로 통과한다
|
||||
|
||||
:::evidence key="a-weaker-private-copy-on-the-wired-path-probe" alt="컴파일된 값 타입의 생성자에 목적지 여덟 개를 직접 넣어 통과와 거절을 출력한 결과, 이웃 호출처가 같은 결함을 고치며 남긴 주석, 그 엔드포인트가 POST 대상이 되는 지점, 값을 만드는 유일한 main 코드와 그 코드가 있는 복호 경로, 접수 유스케이스의 채널 거절, 그리고 플랫폼 마스터 스위치의 출하 기본값을 출력한 터미널 기록." caption="목적지 여덟 개 투입 결과 · 이웃 호출처의 주석 · POST 대상 지점 · 값 생성은 복호 경로 한 곳 · 마스터 스위치 기본값 false — 49줄 · exit 0" zoom="true"
|
||||
:::
|
||||
|
||||
컴파일된 값 타입에 여덟 개를 넣었다. https 인 여섯은 전부 통과한다. 클라우드 메타데이터 주소 둘, 사설 대역, 사설 IPv6, 그리고 `user:pw@` 를 단 주소까지 지난다. 거절된 둘은 http 이고, 메시지는 루프백 밖에서는 https 여야 한다는 것이다.
|
||||
컴파일된 값 타입에 여덟 입력을 넣었다. HTTPS인 여섯 입력은 모두 통과했고, 그 안에는 클라우드 메타데이터 주소 둘·사설 대역·사설 IPv6·`user:pw@`가 포함됐다. 거절된 둘은 HTTP였으며 메시지는 loopback 밖에서는 HTTPS를 요구했다.
|
||||
|
||||
이웃 호출처의 주석이 같은 결함을 고치며 무엇이 남았는지 적는다.
|
||||
|
||||
@@ -130,7 +130,7 @@ for kept the weaker check.
|
||||
|
||||
그래서 지금 성립하는 것은 계약이다. 이 레코드의 표준 생성자가 이 값의 유일한 검증 지점이고, 포크가 구독 등록 엔드포인트를 붙이는 순간 — 그것이 이 모듈의 존재 이유다 — 검증은 이미 통과되어 있다.
|
||||
|
||||
고칠 자리는 두 모듈 사이가 아니라 값 타입의 생성자 안이다. 컴포지션 루트 편의 표가 이 건에 배선 지점이 없다고 따로 적어 둔다.
|
||||
수정은 두 모듈의 wiring을 바꾸는 일이 아니라 값 타입 생성자가 공용 guard와 같은 검증을 사용하도록 만드는 쪽이다. 컴포지션 루트 편의 표도 이 문제에 별도 배선 지점이 없다고 적는다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
|
||||
+13
-13
@@ -1,7 +1,7 @@
|
||||
---
|
||||
kind: CASE
|
||||
slug: trust-policy-lives-in-nginx-not-in-the-code
|
||||
title: forwarded 헤더 신뢰 판정이 Nginx에만 있고 Java 정책 421 LOC은 배선되지 않았다
|
||||
title: 테스트 Nginx 설정은 forwarded 헤더를 교체하지만 운영 경로는 확인하지 않았다
|
||||
topic: duplicate-mechanisms
|
||||
project: clean-architecture-backend-template
|
||||
status: 게시 전
|
||||
@@ -17,16 +17,16 @@ source:
|
||||
- 원본 분석 절은 final/document.md#3-1 · final/document.md#a14 §32.2 이다.
|
||||
---
|
||||
|
||||
# forwarded 헤더 신뢰 판정이 Nginx에만 있고 Java 정책 421 LOC은 배선되지 않았다
|
||||
# 테스트 Nginx 설정은 forwarded 헤더를 교체하지만 운영 경로는 확인하지 않았다
|
||||
|
||||
웹 리프의 프록시 패키지는 421 줄로 신뢰 프록시 정책과 헤더 정화기와 정규화 타입을 갖는다. 실제 신뢰 판정은 Nginx 설정이 하고, 그 설정은 들어온 forwarded 헤더를 원격 주소로 교체한다.
|
||||
웹 리프의 프록시 패키지는 421줄로 신뢰 프록시 정책과 헤더 정화기와 정규화 타입을 갖는다. 이번 evidence에서 확인한 `nginxProxyTest` 설정은 들어온 forwarded 헤더를 authoritative value로 교체한다. 이 테스트 설정이 실제 운영 배포의 trust boundary인지까지는 확인하지 않았다.
|
||||
|
||||
## 관계
|
||||
|
||||
- **중복 장치를 찾으면 어느 쪽이 조립됐는지 먼저 확인한다**
|
||||
이 사례가 그 규칙의 인프라 판이다.
|
||||
- **요청 식별자를 클라이언트가 고를 수 없다는 정책이 다른 필터에서 뒤집힌다**
|
||||
같은 리프에서 같은 계열의 사례다.
|
||||
같은 웹 리프에서 Java 정책과 프록시 설정이 중복되어 실제 신뢰 경계를 어느 쪽이 결정하는지 다시 확인해야 했다.
|
||||
|
||||
## 문제
|
||||
|
||||
@@ -37,15 +37,15 @@ NormalizedForwardedHeaders 158 줄
|
||||
ForwardedHeaderSanitizer 72 줄
|
||||
UntrustedForwardedHeaderException 30 줄
|
||||
|
||||
이 코드가 하는 일은 어떤 프록시를 신뢰할지 정하고 forwarded 헤더를 정규화하는 것이다.
|
||||
이 코드는 신뢰할 프록시를 판정하고 forwarded 헤더를 정규화한다.
|
||||
|
||||
문제는 이것이 실제 판정 경로인가다.
|
||||
|
||||
## 결론
|
||||
|
||||
Nginx 설정이 그 판정을 대신한다.
|
||||
확인한 테스트용 Nginx 설정은 Java 앞단에서 forwarded 헤더를 교체한다.
|
||||
|
||||
프록시 헤더 설정 파일의 주석이 자기 지위를 명시한다. 이것이 권위 있는 forwarded 헤더이며 모든 location 에서 include 된다는 것이다.
|
||||
프록시 헤더 설정 파일의 주석은 이 파일을 forwarded 헤더의 authoritative 설정으로 두고 모든 location에서 include하도록 요구한다.
|
||||
|
||||
설정 내용은 교체다.
|
||||
|
||||
@@ -56,7 +56,7 @@ X-Forwarded-Host 를 이 배포의 공개 이름으로 설정
|
||||
|
||||
주석이 모든 줄이 SET 이고 ADD 가 아니라고 못 박는다. 클라이언트가 보낸 X-Forwarded-For 는 remote_addr 로 교체되고 X-Forwarded-Host 는 이 배포의 공개 이름으로 교체된다.
|
||||
|
||||
즉 애플리케이션에 도달하는 시점에 그 헤더들은 이미 신뢰할 수 있는 값이다. Java 정책이 판정할 것이 남아 있지 않다.
|
||||
이 테스트 구성에서는 애플리케이션에 도달하기 전에 forwarded 헤더가 교체된다. 하지만 운영 배포가 같은 설정을 사용한다는 evidence는 없으므로 실제 운영에서 Java 정책이 불필요하다고 단정하지 않는다.
|
||||
|
||||
Java 쪽 참조 수도 그것과 맞는다.
|
||||
|
||||
@@ -65,9 +65,9 @@ TrustedProxyPolicy : main 참조 1, test 참조 2
|
||||
UntrustedForwardedHeaderException : main 참조 1, test 참조 1
|
||||
NormalizedForwardedHeaders : main 참조 2
|
||||
|
||||
같은 설정 파일의 주석이 왜 include 방식인지도 적는다. Nginx 의 배열 지시어 상속 규칙이 병합이 아니라 교체이기 때문이다. location 안의 proxy_set_header 하나가 server 수준에서 상속된 모든 proxy_set_header 를 버린다. 보안 헤더를 server 수준에 두고 location 마다 하나씩 추가하는 설정은 보안 헤더를 하나도 보내지 않으며, 유일한 증상은 애플리케이션이 조용히 클라이언트를 다시 신뢰하는 것이다.
|
||||
같은 설정 파일의 주석은 Nginx 배열 지시어가 병합되지 않고 교체되기 때문에 include 방식을 쓴다고 설명한다. location 안에서 `proxy_set_header`를 하나라도 다시 선언하면 server 수준에서 상속받던 같은 계열 지시어를 잃을 수 있다. 따라서 forwarded 헤더 설정을 location마다 부분적으로 재정의하면 애플리케이션이 받는 신뢰 입력이 달라질 수 있다.
|
||||
|
||||
그 주석이 이 사례의 위험을 정확히 서술한다. 신뢰 판정이 인프라에 있으면 인프라 설정 실수가 애플리케이션의 신뢰 정책을 조용히 되돌린다. 그리고 그때 되돌아갈 Java 정책은 배선되어 있지 않다.
|
||||
이 테스트 설정이 운영에서도 trust boundary라면 인프라 설정 실수가 애플리케이션이 받는 forwarded 헤더 의미를 바꿀 수 있다. 다만 이번 searched direct-reference evidence만으로는 운영 시 fallback이 될 Java trust policy의 실제 framework/lifecycle wiring을 확정하지 못했다.
|
||||
|
||||
## 검증 환경
|
||||
|
||||
@@ -86,16 +86,16 @@ Nginx 설정 : 웹 리프의 nginxProxyTest 소스셋 아래 proxy_headers.conf
|
||||
|
||||
<!-- body:start -->
|
||||
|
||||
forwarded 헤더를 어디까지 믿을지 판정하는 Java 정책이 421 LOC 작성돼 있고 배선되지 않는다. 실제 판정은 Nginx 설정이 한다.
|
||||
forwarded 헤더를 다루는 Java 정책이 421 LOC 있고 searched direct reference 기준으로 사용 지점이 매우 적다. 별도로 `nginxProxyTest` 설정은 forwarded 헤더를 authoritative value로 교체한다. 두 사실을 확인했지만 이 테스트 설정이 운영 배포의 실제 trust boundary인지와 Java 정책의 모든 framework/lifecycle wiring 부재까지는 확인하지 않았다.
|
||||
|
||||
## 판정을 실제로 하는 곳
|
||||
|
||||
:::evidence key="trust-policy-lives-in-nginx-not-in-the-code" alt="분석 문서 final/document.md 에서 이 기록의 근거 절을 그대로 잘라낸 18줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="final/document.md 발췌 — 18줄" zoom="true"
|
||||
:::
|
||||
|
||||
## 리뷰가 닿지 않는 자리로 정책이 옮겨졌다
|
||||
## Java 코드와 프록시 설정을 함께 봐야 한다
|
||||
|
||||
두 곳이 어긋나면 코드 리뷰가 잡을 수 없고, Java 쪽을 고쳐도 동작이 바뀌지 않는다.
|
||||
운영 배포가 이 프록시 설정을 실제로 사용한다면 Java 코드만 검토해서는 forwarded 헤더 교체 정책을 검증할 수 없다. 반대로 운영 설정을 확인하지 않은 상태에서는 Java 변경이 동작에 영향을 주지 않는다고 단정할 수도 없다.
|
||||
|
||||
## 확인하지 못한 것
|
||||
|
||||
|
||||
Reference in New Issue
Block a user