v0.9.267
Pod 로그 마스크가 놓친 자격증명 여덟 가지
analyzer.MaskSensitive 는 Pod 로그·exec stdout/stderr·터미널 스트림·Pod env 지문·Manifest Viewer 주석 값이 화면과 데이터베이스로 나가기 전 통과하는 유일한 마스크입니다. 바로 앞 틱(v0.9.266)에서 주석 값을 이 함수로 넘기며 근거로 적은 것이 "주석 값에는 토큰이 붙은 webhook URL·DSN·bearer 헤더가 일상적으로 들어간다" 였는데, 실측해 보니 그 셋 중 둘을 이 마스크가 잡지 못했습니다. 실제로 들어오는 형태를 하나씩 넣어 확인한 결과 여덟 가지가 그대로 나왔습니다.
① DSN 의 비밀번호가 평문이었습니다
postgres://appuser:S3cr3tPass@db:5432/app 은 연결 실패 로그가 가장 흔히 출력하는 줄인데 어떤 규칙에도 걸리지 않았습니다 — key=value 규칙은 DATABASE_URL 같은 키 이름을 모르기 때문입니다. 이제 비밀번호만 가리고 host/database 는 남겨 무엇이 실패했는지는 그대로 읽힙니다(같은 판정을 env 드리프트 경로의 looksSensitiveEnvValue 가 이미 쓰고 있었습니다).
② Authorization: Basic 이 통째로 남았습니다
base64 를 디코드하면 user:password 인 바로 그 헤더인데 Bearer 만 규칙이 있었습니다 — internal/audit 의 리댁터는 처음부터 둘 다 잡고 있었으므로 로그 경로만 구멍이었습니다.
③ JSON 으로 찍힌 헤더는 이름이 매치되지 않았습니다
서비스가 요청 헤더를 덤프하면 {"Authorization": "Bearer …"} 형태인데, 헤더 이름과 콜론 사이에 닫는 따옴표가 있어서 규칙이 걸리지 않았습니다. internal/audit 이 자기 쪽에서 같은 수정을 이미 했고(주석에 근거가 적혀 있습니다) 같은 처리로 맞췄습니다.
④ Bearer 토큰을 앞부분만 가렸습니다
토큰 문자 집합에 +/= 가 없어서 base64 토큰은 첫 + 에서 멈췄습니다 — Bearer ***REDACTED***+Z/gh== 처럼 가렸다고 표시하면서 뒷부분을 그대로 노출했습니다.
⑤ SECRET_KEY= 와 PRIVATE_KEY= 가 통과했습니다
구분자가 키 이름 바로 뒤에 와야 하므로 secret 항목은 SECRET_KEY= 를 덮지 못합니다 — Django/Flask 앱이 시작 로그에 찍는 바로 그 이름입니다. passphrase·credentials 와 함께 추가했습니다.
⑥ 키 이름 없이 값만 찍힌 토큰이 통과했습니다
using key sk-…, clone with token ghp_… 처럼 앵커할 = 가 없는 줄인데, 기존에는 AWS AKIA… 하나만 이 형태를 다뤘습니다. sk-·gh[pousr]_·xox[abprs]-·glpat-·AIza 를 추가했습니다.
⑦ PEM 개인키 블록이 그대로 나왔습니다
운영자가 터미널에서 cat 한 키가 exec 결과에 그대로 저장됩니다.
정상 로그는 한 글자도 바뀌지 않습니다
마스킹은 계속 표적 규칙만 씁니다 — 정상 로그를 먹는 넓은 base64 휴리스틱은 쓰지 않으며, 그 사실을 회귀 테스트로 고정했습니다(이미지 digest·헬스체크 URL·disk-0123… 같은 줄이 한 글자도 바뀌지 않아야 합니다). 규칙 목록을 audit 리댁터와 같은 {정규식, 치환} 표로 바꿔, 인덱스로 치환 방식을 고르던 switch 를 없앴습니다 — 가운데에 규칙을 넣으면 뒤쪽 규칙의 치환이 어긋나던 구조였습니다.
초록불을 믿지 않았습니다
신규 테스트 10개(+오탐 회귀 1개)를 고치기 전 코드에 되돌려 붙여 확인했습니다 — 열 개가 각 결함을 정확히 지목하며 실패했습니다.
검증: go build ./... · go vet ./... · go test ./... 전부 통과(21 패키지).
- 이미지:
clustara:v0.9.267 - 배포 압축본:
clustara-v0.9.267.tar.gz