77 lines
4.6 KiB
Markdown
77 lines
4.6 KiB
Markdown
---
|
|
title: blog-topic / logback-layer1-secret-masking-json-vs-pattern-2026-06-14
|
|
source_type: blog-topic
|
|
status: raw
|
|
related_branches: [feature-log-management-contract]
|
|
related_projects: [ca-tmpl]
|
|
tags: [blog-topic, ca-tmpl, logback, logstash-encoder, masking, security, observability, redaction]
|
|
created: 2026-06-14
|
|
status_label: ready-for-canonical
|
|
target_audience: backend-engineer
|
|
inspiration_url:
|
|
archive_url:
|
|
---
|
|
|
|
# blog-topic: logback-layer1-secret-masking-json-vs-pattern-2026-06-14
|
|
|
|
> Layer: `raw/blog-topics/` — 글감 원석. canonical `wiki/blog` 정제 전.
|
|
|
|
## Parent / 부모
|
|
|
|
- [[raw/branch-notes/feature-log-management-contract]] — Redaction Layer 1(DRIFT-2) 구현에서 파생.
|
|
|
|
## 트리거 / Trigger
|
|
|
|
"ERROR/WARN 로그에 token/password/Authorization 헤더를 `****` 로 가린다"는 계약을 `%replace(%msg){...}` 한 줄로 끝내려다, 운영 포맷이 `LogstashEncoder`(JSON) 임을 깨달음. `%replace` 는 PatternLayout converter 인데 JSON encoder 는 PatternLayout 을 **우회**한다 → JSON 로그에는 마스킹이 안 걸린다. 정작 가려야 할 production(JSON) 경로가 무방비.
|
|
|
|
## 글감 / Topic seed
|
|
|
|
"Logback 에서 secret 마스킹을 제대로 하려면 — `%replace` 가 JSON 을 못 가리는 이유와 단일 정규식 SSOT 설계":
|
|
|
|
1. **두 갈래 인코딩 경로**: 사람이 읽는 `PatternLayout`(local/dev) vs 구조화 `LogstashEncoder`(staging/prod). `%replace` 는 전자에만 적용.
|
|
2. **JSON 경로의 올바른 도구**: `net.logstash.logback.mask.MaskingJsonGeneratorDecorator` + `ValueMasker` — JSON 생성 시점에 모든 string value(message/MDC/stack trace)를 정규식으로 치환. `<jsonGeneratorDecorator>` 로 encoder 에 장착.
|
|
3. **pattern 경로**: `MessageConverter` 를 상속한 custom converter(`%maskedMsg`)로 같은 정규식 적용.
|
|
4. **단일 SSOT**: 두 경로가 **같은** `LogMaskingPatterns`(컴파일된 `List<Pattern>` + capture-group replacement)를 공유 → profile/포맷 전환이 마스킹 대상을 바꾸지 못함(D10 "가독성 전환이 secret 노출로 이어지지 않게").
|
|
5. **정규식 설계**: keyword(group1)+separator(group2)+value(group3) 로 캡처 후 `$1$2****` 치환 → 키는 남기고 값만 가림. 부정 문자클래스(`[^\s"',&}]+`)로 catastrophic backtracking 회피. Authorization/Bearer 별도 룰.
|
|
6. **한계 명시**: 정규식 기반은 obfuscated 인코딩(Base64URL blob, prefix 없는 토큰)을 놓칠 수 있음 → 1차 방어는 여전히 "logger 가 payload/body 인자를 안 받음(by construction)". 마스킹은 defence-in-depth.
|
|
|
|
## 핵심 주장 후보 / Claim candidates
|
|
|
|
- `%replace` 만으로 "로그 마스킹 했다"는 JSON 운영에서 거짓 안심이다 — encoder 가 PatternLayout 을 우회하면 무력.
|
|
- 마스킹 정규식은 **인코딩 경로마다 중복하지 말고** 단일 Java SSOT 로 두고 decorator/converter 두 어댑터가 참조해야 한다 — 그래야 profile 전환이 보안 동작을 못 바꾼다.
|
|
- value 캡처 그룹 + `$n` 역참조 치환으로 "키는 보존, 값만 마스킹" → 진단 가능성과 보안의 균형.
|
|
|
|
## 관련 / Related
|
|
|
|
- [[raw/branch-notes/feature-log-management-contract]]
|
|
- 동반 면접 노트(드롭 메트릭 결정론 테스트 + 마스킹 Q): [[raw/interviews/deterministic-logback-asyncappender-drop-metric-test-2026-06-14]]
|
|
|
|
## Outline seed
|
|
|
|
1. PatternLayout `%replace`가 JSON encoder 경로를 우회하는 문제를 설명한다.
|
|
2. `MaskingJsonGeneratorDecorator`와 custom `%maskedMsg`가 같은 SSOT regex를 공유하게 한다.
|
|
3. regex masking의 한계와 logger signature의 by-construction 방어를 분리한다.
|
|
|
|
## Canonical 전환 후보 / Canonical extraction candidates
|
|
|
|
- `wiki/projects/ca-tmpl/observability-log-metric-trace-runbook.md` 후보:
|
|
- Logback JSON vs pattern masking Layer 1 글감.
|
|
- 필요한 추가 검증:
|
|
- 현재 `LogMaskingPatterns`, JSON decorator, pattern converter 구현 여부.
|
|
|
|
## Sources / 근거 후보
|
|
|
|
- [[raw/branch-notes/feature-log-management-contract]]
|
|
- [[raw/interviews/deterministic-logback-asyncappender-drop-metric-test-2026-06-14]]
|
|
|
|
## 미해결 / Unknown
|
|
|
|
- 아직 확인해야 할 사실: 현재 ca-tmpl 코드에 JSON masking decorator와 pattern converter가 존재하는지.
|
|
- 과장하면 안 되는 부분: regex masking이 모든 secret 형태를 잡는다고 쓰지 않는다.
|
|
|
|
## Decision / 처리 결정
|
|
|
|
- 액션: `promote-to-canonical`
|
|
- 이유: `wiki/projects/ca-tmpl/observability-log-metric-trace-runbook.md` 에 Logback JSON/pattern masking 글감으로 반영한다.
|
|
- 다음 단계: blogify 전 masking 구현 등급과 payload logging 금지 rule을 분리한다.
|