- analysis/·source-index·state.json 을 final/document.md 제2부·제3부로 접었다. SSOT 는 하나다 - 파일럿 — commit-ambiguity-as-a-result 를 새 기준으로 재선별. 후보 14 → 글감 5 (PROMOTE 5 · MERGE_INTO 3 · KEEP_IN_SSOT 4 · 보류 2). 기록 5건을 다시 썼고 그림 1개를 techviz 로 만들었다 - 재선별이 잡은 것: 제1부 §6.2·§11.1 이 자기 §13.2 와 어긋나 있었다(레인을 안 돌렸다 vs 돌렸다) — 정정. 이미 답이 나와 있던 Question 을 HEAD 재실행 질문으로 다시 세웠다. Concept 이 인용한 코드가 SSOT 에 없어 뺐다 - candidateScope·sourceRepository 기록. 나머지 43개 주제는 재선별 대기(PENDING 905) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
131 lines
6.1 KiB
Markdown
131 lines
6.1 KiB
Markdown
---
|
|
kind: CASE
|
|
slug: analysis-finding-a14-f004
|
|
title: 프레임워크 자유 신원 모델과 교차 테넌트 가드가 프로덕션에서 한 번도 참조되지 않는다
|
|
topic: web-inbound-and-http-surface
|
|
project: clean-architecture-backend-template
|
|
status: 게시 전
|
|
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
|
|
rootTreeNode: case:analysis-finding-a14-f004
|
|
evidenceCapturedOn: 2026-09-01
|
|
body: case-analysis-finding-a14-f004.body.md
|
|
assets:
|
|
- key: analysis-finding-a14-f004
|
|
file: ../../../final/evidence/rendered/analysis-finding-a14-f004.svg
|
|
evidence:
|
|
- ../../../final/evidence/raw/analysis-finding-a14-f004.txt
|
|
source:
|
|
- 원본 분석 절은 final/document.md#a14#L562 이다.
|
|
---
|
|
|
|
# 프레임워크 자유 신원 모델과 교차 테넌트 가드가 프로덕션에서 한 번도 참조되지 않는다
|
|
|
|
보안 패키지 열한 파일이 서로만 참조하고 바깥에서 들어오는 화살표가 없다. 그 안에 클라이언트가 소속을 제안하는 헤더를 거부하는 가드가 있는데, 도달 경로의 호출자가 테스트뿐이다. 지금 이 플랫폼은 그 헤더를 거부도 무시도 하지 않는다.
|
|
|
|
## 관계
|
|
|
|
- **플랫폼 요청 컨텍스트가 서블릿에는 생산자가 없고 리액티브에는 익명 액터로 고정되어 있다**
|
|
이 가드를 고칠 때 함께 연결해야 하는 사례다.
|
|
- **용량 보호 계층 전체가 자기 테스트 픽스처 안에서만 실행된다**
|
|
같은 리프의 같은 형태다.
|
|
- **노출이 없는 것과 가드가 작동하는 것은 다르다**
|
|
이 사례가 그 규칙의 형태다.
|
|
|
|
## 문제
|
|
|
|
이 리프에 프레임워크에 의존하지 않는 신원 모델이 있다.
|
|
|
|
그 패키지가 바깥에서 쓰이는지 확인했다.
|
|
|
|
## 결론
|
|
|
|
쓰이지 않는다.
|
|
|
|
보안 패키지 열한 파일이 서로만 참조하고 바깥에서 들어오는 화살표가 없다.
|
|
|
|
인증 조회 값을 만드는 production 코드가 없으므로 보안 문맥 다리의 해석 메서드가 호출될 수 있는 상태 자체가 없다.
|
|
|
|
가장 값이 큰 부분이 그 안에 있다.
|
|
|
|
소속 문맥 해석기가 클라이언트가 소속을 제안하는 헤더 이름 넷을 알고 있고, 그 이름이 요청 입력에 있으면 거부 예외를 던진다.
|
|
|
|
도달 경로는 보안 문맥 다리의 두 인자 오버로드 하나뿐이고, 그 오버로드의 호출자는 테스트뿐이다.
|
|
|
|
그래서 지금 이 플랫폼은 그 헤더에 대해 거부도 무시도 하지 않는다. 그 헤더를 보는 코드가 아예 없다.
|
|
|
|
노출은 아니다.
|
|
|
|
소속을 소비하는 유일한 지점이 요청 컨텍스트의 소속을 읽는데, 그 값은 다른 사례 때문에 항상 비어 있다.
|
|
|
|
헤더가 소속이 되는 경로가 없으므로 교차 소속 읽기도 없다.
|
|
|
|
그러나 그것은 가드가 작동해서가 아니라 소속 기능 전체가 배선되지 않아서다.
|
|
|
|
요청 컨텍스트를 고치면서 이 가드를 함께 연결하지 않으면, 그때 노출이 생긴다.
|
|
|
|
이 패키지에는 교차 출처 정책 검증기와 위조 방지 정책 해석기도 있고 둘 다 production 참조가 0 이다.
|
|
|
|
실제 두 결정은 보안 설정이 프레임워크 API 로 직접 내린다.
|
|
|
|
같은 질문에 대한 두 번째 구현이 검증만 되고 쓰이지 않는다.
|
|
|
|
판정은 P2 다.
|
|
|
|
## 검증 환경
|
|
|
|
Spring Boot : 4.0.8
|
|
확인 방식 : 패키지 간 참조 방향 확인과 호출자 전수 검색
|
|
소스 수정 : x
|
|
|
|
## 재현 조건
|
|
|
|
원문은 final/evidence/raw/176 계열에 있다.
|
|
|
|
1. 보안 패키지의 파일 목록을 만든다.
|
|
2. 각 타입을 참조하는 바깥 코드를 검색한다.
|
|
3. 소속 해석기의 거부 메서드 도달 경로를 확인한다.
|
|
4. 그 경로의 호출자를 센다.
|
|
5. 소속을 소비하는 지점이 읽는 값이 무엇인지 확인한다.
|
|
6. 교차 출처와 위조 방지 결정이 실제로 어디서 내려지는지 확인한다.
|
|
|
|
## 본문
|
|
|
|
<!-- body:start -->
|
|
|
|
`security` 패키지 11개 파일이 서로만 참조하고 바깥에서 들어오는 화살표가 없다(§11.1).
|
|
|
|
## AuthenticationView 참조 위치
|
|
|
|
:::evidence key="analysis-finding-a14-f004" alt="코드베이스에서 AuthenticationView 를 검색한 출력 5줄. 이 기록이 세는 참조가 그 출력에 그대로 보인다." caption="AuthenticationView 코드베이스 검색 — 5줄 · exit 0" zoom="true"
|
|
:::
|
|
|
|
## 호출될 수 있는 상태 자체가 없다
|
|
|
|
`AuthenticationView`를 만드는 프로덕션 코드가 없으므로 `WebSecurityContextBridge.resolve(...)`가 호출될 수 있는 상태 자체가 없다.
|
|
|
|
## 가장 값이 큰 부분이 그 안에 있다
|
|
|
|
```java
|
|
// WebTenantContextResolver.java
|
|
private static final Set<String> TENANT_INPUT_NAMES =
|
|
Set.of("x-tenant-id", "tenant-id", "tenantid", "tenant");
|
|
|
|
public void rejectTenantInput(Map<String, String> requestInput) { ... throw new UntrustedTenantInputException(); }
|
|
```
|
|
|
|
클라이언트가 테넌트를 제안하는 헤더를 거부하는 가드다. 도달 경로는 `WebSecurityContextBridge.resolve(authentication, requestInput)` 오버로드 하나뿐이고, 그 오버로드의 호출자는 테스트뿐이다. 그래서 지금 이 플랫폼은 `X-Tenant-Id` 헤더에 대해 **거부도 무시도 하지 않는다** — 그 헤더를 보는 코드가 아예 없다.
|
|
|
|
## 노출은 아니지만 가드가 작동해서가 아니다
|
|
|
|
테넌트를 소비하는 유일한 지점(`OperationAccessPolicy:37-40`)이 `context.tenant()`를 읽고, 그 값은 §12.1 때문에 항상 비어 있다. 헤더가 테넌트가 되는 경로가 없으므로 교차 테넌트 읽기도 없다. 그러나 그것은 **테넌트 기능 전체가 배선되지 않아서**다. §12.1을 고치면서 이 가드를 함께 연결하지 않으면 그때 노출이 생긴다.
|
|
|
|
## 같은 질문에 대한 두 번째 구현
|
|
|
|
이 패키지에는 `WebCorsPolicyValidator`(96줄)와 `WebCsrfPolicyResolver`(43줄)도 있고 둘 다 프로덕션 참조 0이다. 실제 CORS·CSRF 결정은 `SecurityConfig`가 Spring Security API로 직접 내린다. P2.
|
|
|
|
## 확인하지 못한 것
|
|
|
|
소속 제안 헤더를 붙여 요청을 보내 아무 일도 일어나지 않는 것을 재현하지 않았다. 참조 부재상 그 결과가 나온다.
|
|
|
|
<!-- body:end -->
|