Files
document-haness/docs/clean-architecture-backend-template/tech-log-studio/fileserver-state-and-fencing/case/case-scriptable-detection-bypassed-by-a-bom.md
T
DongHyeonkaandClaude Fable 5.1 b25357c48a docs(clean-architecture-backend-template): fold analysis into final and re-select one topic
- 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>
2026-09-07 12:39:20 +09:00

99 lines
4.6 KiB
Markdown

---
kind: CASE
slug: scriptable-detection-bypassed-by-a-bom
title: scriptable 콘텐츠 탐지가 BOM·NUL·주석으로 우회된다
topic: fileserver-state-and-fencing
project: clean-architecture-backend-template
status: 게시 전
sourceRevision: 21234e38cdb9a926cbc92bb97a2aee2e4a7d2916
rootTreeNode: case:scriptable-detection-bypassed-by-a-bom
evidenceCapturedOn: 2026-09-01
assets:
- key: scriptable-detection-bypassed-by-a-bom
file: ../../../final/evidence/rendered/scriptable-detection-bypassed-by-a-bom.svg
evidence:
- ../../../final/evidence/raw/scriptable-detection-bypassed-by-a-bom.txt
source:
- 원본 분석 절은 final/document.md#4-4 · final/document.md#a08 §40 이다.
---
# scriptable 콘텐츠 탐지가 BOM·NUL·주석으로 우회된다
브라우저가 실행할 수 있는 콘텐츠를 탐지하는 정책이 접두사 시작 매칭을 쓴다. 마커 앞에 바이트가 하나라도 있으면 탐지되지 않고, 브라우저는 그런 파일도 실행한다.
## 관계
- **접두사 시작 매칭은 시그니처에는 맞고 스니핑 패턴에는 맞지 않는다**
이 사례에서 끌어낸 규칙이다.
- **sanitize가 아니라 reject가 기본이다**
이 정책이 따르는 기본 방침이다.
## 문제
정책의 의도는 명확하고 옳다. javadoc 이 그것을 적는다.
브라우저가 인라인으로 제공될 경우 실행할 콘텐츠를 막는다. 탐지는 주장된 타입이나 확장자가 아니라 콘텐츠에 대해 한다. 둘 다 공격자가 통제하기 때문이다. 명시적 안전 프로파일이 켜져 있지 않으면 실행 가능 콘텐츠는 게시되지 않고 격리된다.
마커 목록도 합리적이다. HTML 선언과 여는 태그들과 XML 선언과 엔티티 선언이다.
## 결론
매칭 방식이 접두사 시작이다.
앞의 1024 바이트를 읽고 그 안에서 마커를 찾는데, 마커가 콘텐츠의 시작에 있어야 한다.
브라우저는 그렇게 엄격하지 않다. 앞에 바이트가 있어도 콘텐츠를 스니핑해 실행한다.
그래서 우회가 여럿이다.
바이트 순서 표시를 앞에 붙이면 마커가 시작이 아니다
널 바이트를 앞에 넣어도 같다
주석이나 공백을 앞에 두어도 같다
실행 탐침이 이 우회들을 확인했다.
시그니처 검사와 스니핑 패턴 검사는 다른 문제다. 시그니처는 파일 형식이 정의상 특정 바이트로 시작하므로 시작 매칭이 맞다. 브라우저 스니핑은 형식 정의가 아니라 관용적 해석이므로 시작 매칭이 맞지 않는다.
같은 함수가 두 목적에 쓰이면 한쪽이 틀린다.
## 검증 환경
OpenJDK : 21.0.12
확인 방식 : 실행 탐침으로 우회 입력 확인
소스 수정 : x
## 재현 조건
원문은 final/evidence/raw/146-fileserver-verification-security-audit-probes.txt 의 실행 탐침 블록에 있다.
1. 정책 클래스의 마커 목록과 접두사 길이를 확인한다.
2. 매칭이 시작 기준인지 포함 기준인지 확인한다.
3. 마커 앞에 바이트를 붙인 입력으로 탐지 결과를 확인한다.
## 본문
<!-- body:start -->
javadoc이 목적을 "Detection is on content, not on the claimed type or the extension, because both are attacker controlled"로 적는데, 구현은 1,024바이트 접두사를 소문자화·`stripLeading()`한 뒤 여섯 마커로 **시작하는지**만 본다.
## javadoc 의 목적과 구현의 판정
:::evidence key="scriptable-detection-bypassed-by-a-bom" alt="분석 문서 final/document.md 에서 이 기록의 근거 절을 그대로 잘라낸 18줄. 코드베이스를 측정한 것이 아니라 원본 판정이 무엇을 적었는지를 보여 준다." caption="final/document.md 발췌 — 18줄" zoom="true"
:::
## hermetic probe 가 통과시킨 셋
UTF-8 BOM + `<html>` · 선행 HTML 주석 후 `<script>` · NUL 바이트 후 `<html>`. `String.stripLeading()``Character.isWhitespace`만 제거하므로 BOM(U+FEFF)도 NUL도 지우지 않는다. 셋 다 브라우저는 HTML로 렌더링하고, BOM 접두 HTML은 여러 편집기의 기본 출력이다.
## 형제 검증기와의 대비가 판정을 굳힌다
`MediaTypeVerifier`의 매직바이트 선두 매칭은 시그니처의 정의가 파일 선두이므로 옳지만, scriptable 마커는 시그니처가 아니라 브라우저가 스니핑하는 패턴이다.
## 확인하지 못한 것
실제 브라우저가 각 우회 입력을 실행하는지 확인하지 않았다. 브라우저의 스니핑 동작은 명세와 구현이 모두 관여하므로 별도 확인이 필요하다.
안전 프로파일이 켜진 배포에서의 동작을 확인하지 않았다.
<!-- body:end -->