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

99 lines
5.0 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·주석으로 우회된다
scriptable 콘텐츠를 탐지하려는 정책이 접두사 시작 매칭을 쓴다. BOM·NUL·주석 같은 prefix를 앞에 두면 detector가 마커를 놓치는 것은 hermetic probe로 확인했다. 실제 대상 브라우저가 각 입력을 실행 가능한 콘텐츠로 해석하는지는 이번 evidence에서 확인하지 않았다.
## 관계
- **접두사 시작 매칭은 시그니처에는 맞고 스니핑 패턴에는 맞지 않는다**
이 사례에서 끌어낸 규칙이다.
- **sanitize가 아니라 reject가 기본이다**
이 정책이 따르는 기본 방침이다.
## 문제
정책의 의도는 명확하고 옳다. javadoc 이 그것을 적는다.
브라우저가 인라인으로 제공될 경우 실행할 콘텐츠를 막는다. 탐지는 주장된 타입이나 확장자가 아니라 콘텐츠에 대해 한다. 둘 다 공격자가 통제하기 때문이다. 명시적 안전 프로파일이 켜져 있지 않으면 실행 가능 콘텐츠는 게시되지 않고 격리된다.
마커 목록도 합리적이다. HTML 선언과 여는 태그들과 XML 선언과 엔티티 선언이다.
## 결론
매칭 방식이 접두사 시작이다.
앞의 1024 바이트를 읽고 그 안에서 마커를 찾는데, 마커가 콘텐츠의 시작에 있어야 한다.
이 probe만으로 실제 브라우저의 스니핑·실행 동작까지 증명할 수는 없다. 확인된 것은 prefix가 붙은 입력이 detector를 우회한다는 사실이다.
그래서 우회가 여럿이다.
바이트 순서 표시를 앞에 붙이면 마커가 시작이 아니다
널 바이트를 앞에 넣어도 같다
주석이나 공백을 앞에 두어도 같다
hermetic detector probe가 이 우회 입력들이 탐지를 통과한다는 사실을 확인했다.
시그니처 검사와 스니핑 패턴 검사는 다른 문제다. 시그니처는 파일 형식이 정의상 특정 바이트로 시작하므로 시작 매칭이 맞다. 브라우저 스니핑은 형식 정의가 아니라 관용적 해석이므로 시작 매칭이 맞지 않는다.
같은 함수가 두 목적에 쓰이면 한쪽이 틀린다.
## 검증 환경
OpenJDK : 21.0.12
확인 방식 : hermetic detector probe로 탐지 우회 입력 확인
소스 수정 : 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도 지우지 않는다. 세 입력 모두 detector를 통과했다. 실제 대상 브라우저가 이 입력을 HTML 또는 scriptable content로 해석하는지는 이번 evidence에서 확인하지 않았다.
## 형제 검증기와의 대비가 판정을 굳힌다
`MediaTypeVerifier`의 매직바이트 선두 매칭은 시그니처의 정의가 파일 선두이므로 적합하다. 반면 이 scriptable detector는 prefix가 붙은 입력을 놓쳤고, 그 입력을 실제 브라우저가 어떻게 해석하는지는 별도 브라우저 검증이 필요하다.
## 확인하지 못한 것
실제 대상 브라우저가 각 우회 입력을 실행 가능한 콘텐츠로 해석하는지 확인하지 않았다. 이번 evidence가 증명하는 범위는 detector bypass까지다.
안전 프로파일이 켜진 배포에서의 동작을 확인하지 않았다.
<!-- body:end -->