Files
project-auth-server/docs/standards/observability/pii-masking.md
T

306 lines
14 KiB
Markdown

# PII Masking 기준
## 1. 목적
이 문서는 로그, 트레이스, 운영 이벤트 기록에서 개인정보(PII)와 민감정보를 어떻게 마스킹하거나 제거할지 정의한다.
이 문서의 목표는 다음과 같다.
- 운영에 필요한 관측 가능성을 유지하면서 민감정보 노출을 막는다
- 로그 레벨과 무관하게 보호해야 하는 데이터를 명확히 한다
- 마스킹 책임을 개별 개발자 습관이 아니라 공통 규칙으로 만든다
- request/response, exception, external API payload, structured logging 모두에 같은 기준을 적용한다
## 2. 근거 수준
- Official: OWASP, Spring Boot 공식 문서에서 직접 확인되는 내용
- Official + Practice: 공식 가이드 위에 일반적인 운영/보안 관행을 결합한 내용
- Project Recommendation: 이 프로젝트 구조와 운영 방식에 맞춘 규칙
## 3. 기본 원칙
### 3.1 민감정보 보호는 예외가 아니라 기본값이다
OWASP는 세션 식별값, 액세스 토큰, 민감한 개인정보, 비밀번호, DB 연결 문자열, 암호화 키, 결제 데이터 등은 로그에 직접 기록하지 말고 제거·마스킹·정제·해시·암호화하라고 권고합니다. 따라서 이 프로젝트에서는 “필요할 때만 숨긴다”가 아니라 기본적으로 숨기고, 꼭 필요한 최소 정보만 남긴다를 원칙으로 둔다.
프로젝트 규칙:
- 민감정보는 기본 비기록(non-log) 이 원칙
- 필요하면 원문 대신 마스킹/해시/요약값을 남긴다
- “디버깅 중이라서”, “DEBUG니까”, “내부망이라서” 같은 이유로 예외를 만들지 않는다
### 3.2 로그 레벨이 낮다고 민감정보를 찍어도 되는 것은 아니다
OWASP는 민감정보 자체를 직접 기록하지 말라고 하며, 이는 특정 로그 레벨에 한정된 예외를 두지 않습니다. 이 프로젝트도 DEBUG/TRACE를 보안 예외 구간으로 취급하지 않는다.
프로젝트 규칙:
- INFO, DEBUG, TRACE 모두 같은 마스킹 규칙을 따른다
- 낮은 레벨은 더 자세할 수 있을 뿐, 더 위험한 데이터를 허용하는 레벨이 아니다
### 3.3 민감정보 마스킹은 로깅 호출부와 공통 로깅 파이프라인이 함께 책임진다
Spring Boot는 MDC를 로그 패턴과 structured logging JSON에 포함할 수 있고, JSON 필드의 include/exclude/rename/add와 customizer도 지원합니다. 따라서 민감정보 보호는 “개발자가 매번 조심해서 안 찍는 것”만으로 끝내지 않고, 공통 로깅 구성을 통한 2차 방어선도 둘 수 있다.
프로젝트 규칙:
- 1차 방어: 애플리케이션 코드에서 원문을 로그 인자로 넘기지 않는다
- 2차 방어: 공통 로깅 설정/structured logging customizer/appender 등에서 추가 마스킹을 검토한다
- 둘 중 하나만 믿지 않는다
## 4. 데이터 분류 규칙
### 4.1 절대 원문 기록 금지 데이터
OWASP 기준으로 직접 기록하지 말아야 할 대표 데이터는 다음과 같습니다. 세션 식별값, 액세스 토큰, 민감한 개인정보와 일부 PII, 인증 비밀번호, DB 연결 문자열, 암호화 키 및 주요 비밀값, 은행 계좌/카드 정보 등이 여기에 해당합니다.
프로젝트 규칙:
다음은 원문 로그 금지 다.
- 비밀번호
- access token / refresh token / bearer token
- session id / cookie session value / JWT raw token
- DB connection string
- encryption key / signing key / secret key / API secret
- 카드번호 / 계좌번호 / 결제 식별정보 원문
- 주민등록번호/여권번호/정부 식별번호류
- 건강정보, 법적으로 민감한 개인정보
- 외부 시스템 인증 헤더 원문
- source code, stack dump 속 비밀 설정값
### 4.2 기본적으로 직접 기록하지 않는 데이터
OWASP는 민감한 개인정보 외에도 personal names, telephone numbers, email addresses, internal network names/addresses, file paths 등은 특별한 취급이 필요할 수 있다고 설명합니다. 이 프로젝트에서는 다음 데이터도 원문 기록을 기본값으로 두지 않는다.
프로젝트 규칙:
다음은 원문 비권장 이며, 필요 시 축약/부분 마스킹/내부 식별자 대체를 우선한다.
- 이메일 전체값
- 전화번호 전체값
- 개인 이름 전체값
- 내부 IP/호스트명/내부 네트워크 이름
- 로컬 파일 경로
- 상세 주소
- 주민등록번호 일부를 유추할 수 있는 조합값
### 4.3 운영에 필요한 식별자는 내부 식별자를 우선한다
OWASP는 direct/indirect identifier에 대해 de-identification을 검토하라고 권고합니다. 이 프로젝트에서는 사람이 직접 식별되는 값보다 내부 식별자를 우선 남기는 방향을 기본으로 한다.
프로젝트 규칙:
- email 대신 userId
- accountNumber 대신 accountId
- phoneNumber 대신 customerId
- 꼭 외부 식별자가 필요하면 일부만 남긴다
## 5. 처리 방식 규칙
### 5.1 제거, 마스킹, 해시, 암호화의 기본 선택
OWASP는 민감값을 제거, 마스킹, 정제, 해시, 암호화 중 적절한 방식으로 처리하라고 설명합니다. 이 프로젝트는 다음 우선순위를 권장한다.
프로젝트 규칙:
- 가장 먼저 검토: 아예 기록하지 않기
- 운영 식별이 필요: 부분 마스킹
- 동일 값 상관관계만 필요: 해시/토큰화
- 정말 복구 가능한 보관이 필요: 별도 보호 저장소를 검토하고 일반 애플리케이션 로그에는 두지 않는다
### 5.2 부분 마스킹 규칙
프로젝트 권장 예:
- 이메일: do***@example.com
- 전화번호: 010-****-1234
- 카드번호: ************1234
- 계좌번호: ******7890
- 주민번호류: 기본 로그 금지, 부분 마스킹도 최소화
- 토큰/세션 ID: 원문 금지, 필요하면 앞/뒤 일부 + 해시 일부
### 5.3 해시 사용 규칙
프로젝트 규칙:
- 같은 값의 반복 여부만 추적하면 될 때 해시 사용 가능
- salt/secret이 필요한 경우 보안 정책을 따른다
- 해시값도 장기 식별자로 과도하게 남용하지 않는다
- 원문 복구가 필요한 요구를 해시로 해결하려 하지 않는다
## 6. 위치별 규칙
### 6.1 request logging
OWASP는 HTTP request body, response body, headers 같은 확장 상세 정보는 민감할 수 있으므로 특별한 주의가 필요하다고 설명합니다. 이 프로젝트에서는 request logging에서 다음을 기본 금지한다.
프로젝트 규칙:
- request body 전문 로그 금지
- Authorization 헤더 원문 금지
- Cookie 헤더 원문 금지
- query string 전체 로그 금지
- multipart 파일명/본문 원문 금지
허용 가능한 예:
- requestPath
- method
- contentType
- payloadBytes
- allowlist된 소수의 비민감 query field
### 6.2 response logging
프로젝트 규칙:
- response body 전문 로그 금지
- token 발급 응답, 인증 응답, 결제 응답 원문 금지
- 상태코드, duration, 외부 시스템 상태, 응답 크기 같은 메타데이터만 우선 기록한다
### 6.3 exception logging
OWASP는 stack trace, system error messages, debug information, request/response body 등이 민감해질 수 있다고 설명합니다. 따라서 예외 로그는 stack trace를 남기더라도 민감 데이터를 포함한 message/context를 같이 남기지 않도록 조심해야 한다.
프로젝트 규칙:
- 예외 메시지에 포함된 민감정보 원문을 그대로 로그 제목에 사용하지 않는다
- log.error(ex.getMessage(), ex)를 기본값으로 두지 않는다
- 사건 설명 + 안전한 식별자 + 예외 객체 순으로 기록한다
- stack trace에 비밀값이 포함될 위험이 있는 경로는 별도 검토한다
### 6.4 external API / integration logging
프로젝트 규칙:
- 외부 요청/응답 payload 전문 로그 금지
- 외부 API key, bearer token, cookie 원문 금지
- provider 이름, endpoint path, status, duration, request id 같은 메타데이터만 우선 기록
- third-party error body도 민감정보를 포함할 수 있으므로 원문 그대로 남기지 않는다
### 6.5 audit/security logging
OWASP는 보안/감사 로그도 중요하지만, 그렇다고 민감값을 그대로 기록하라는 뜻은 아니라고 설명합니다. “무엇이 일어났는지”는 남기되, 값 원문은 보호해야 한다.
프로젝트 규칙:
- “비밀번호 변경 시도”, “토큰 재발급 요청”, “민감 데이터 조회” 같은 사건 자체는 기록
- 하지만 실제 비밀번호, 토큰, 데이터 원문은 기록하지 않는다
- audit log가 필요하면 일반 운영 로그와 목적을 구분한다
## 7. structured logging / MDC 규칙
### 7.1 MDC에 넣는 값도 같은 기준으로 마스킹한다
Spring Boot는 MDC 값을 로그 패턴과 structured JSON에 포함할 수 있고, ECS/GELF/Logstash 포맷에서도 MDC key-value가 JSON에 들어갑니다. 따라서 MDC는 안전한 공통 필드만 넣는 용도로 써야 한다.
프로젝트 규칙:
- MDC에는 traceId, requestPath, actorId, operation, clientIp(필요 시) 정도의 안전한 메타데이터만 둔다
- MDC에 token, email 전체값, session id, raw principal object를 넣지 않는다
- “MDC니까 괜찮다”는 예외를 두지 않는다
### 7.2 structured logging JSON 필드도 마스킹 정책 대상이다
Spring Boot는 structured JSON에서 include/exclude/rename/add, customizer를 지원합니다. 이는 JSON 로그가 plain text보다 안전하다는 뜻이 아니라, 같은 보호 규칙을 중앙에서 적용할 수 있다는 뜻에 가깝다.
프로젝트 규칙:
- structured logging을 쓰면 JSON field 수준 마스킹/제외 규칙을 검토한다
- 수집 시스템에 맞춘 필드 rename은 허용하지만, 민감 필드 유입 자체를 방지하는 것이 우선이다
- ingestion 단계에서 추가 마스킹이 가능해도 애플리케이션 단계의 원문 출력 금지를 대체하지 않는다
## 8. 로그 인젝션 방지 규칙
### 8.1 외부 입력은 로깅 전에 sanitization을 고려한다
OWASP는 다른 trust zone에서 들어오는 이벤트 데이터는 형식 검증을 하고, CR/LF 및 delimiter 같은 문자를 정제해 로그 인젝션을 막으라고 권고합니다.
프로젝트 규칙:
- 사용자 입력, 외부 시스템 응답, 헤더, query parameter를 로그에 넣기 전 길이/형식 검토
- CR/LF, 탭, 구분자, 제어문자 정제
- multi-line injection이 가능한 원문을 그대로 로그 메시지에 넣지 않는다
### 8.2 예외 메시지와 외부 오류 메시지도 신뢰하지 않는다
프로젝트 규칙:
- 외부 시스템의 에러 메시지를 그대로 로그 제목에 사용하지 않는다
- 예외 메시지를 한 줄 요약/정제 후 보조 정보로만 사용한다
- 로그 제목은 애플리케이션이 통제하는 문장으로 쓴다
## 9. 구현 규칙
### 9.1 공통 마스킹 유틸/컴포넌트를 둔다
프로젝트 규칙:
- 이메일, 전화번호, 토큰, 계좌/카드 마스킹은 공통 유틸이나 formatter로 제공한다
- 서비스마다 제각각 다른 마스킹 패턴을 쓰지 않는다
- 로그 메시지 안에서 직접 substring으로 잘라 쓰는 임시 구현을 줄인다
### 9.2 logger 호출 전에 안전한 값으로 변환한다
프로젝트 규칙:
- log.info("...", rawToken) 금지
- log.info("...", maskedToken(rawToken)) 형태로만 허용
- 가능하면 값을 마스킹한 뒤 변수에 담아 의미 있는 이름으로 사용한다
### 9.3 가능한 경우 중앙 로깅 구성에서 2차 필터링을 둔다
Spring Boot는 structured logging JSON customizer와 MDC 기반 필드 구성을 지원합니다. 이를 이용해 민감 필드가 특정 이름으로 유입될 경우 제거/대체하는 2차 방어선을 둘 수 있다.
프로젝트 규칙:
- authorization, accessToken, password, sessionId 같은 공통 금지 키는 중앙 필터링을 검토한다
- 다만 중앙 필터링이 있으니 코드에서 원문을 찍어도 된다는 뜻은 아니다
## 10. 테스트/검증 규칙
### 10.1 로그에 금지 데이터가 실제로 남지 않는지 검증한다
OWASP는 로깅 메커니즘의 설계/구현/검증을 강조합니다. 이 프로젝트도 마스킹 정책을 문서만 두지 않고 테스트/리뷰 대상으로 본다.
프로젝트 규칙:
- 주요 인증/결제/개인정보 흐름은 로그 검증 테스트를 둘 수 있다
- 보안 리뷰 체크리스트에 “민감정보 로그 노출 여부”를 포함한다
- 샘플 로그/운영 로그 점검으로 정책 위반을 찾아낸다
### 10.2 규칙 위반은 기능 버그가 아니라 보안 버그로 취급한다
프로젝트 규칙:
- 토큰/비밀번호/세션 ID/PII 원문 노출은 보안 결함으로 분류
- “로그일 뿐”이라고 축소하지 않는다
- 수정 우선순위를 높게 둔다
## 11. 금지 규칙
다음은 기본 금지다.
- 비밀번호 원문 로그
- access token / refresh token / session id / cookie 값 원문 로그
- Authorization 헤더 원문 로그
- request/response body 전문 로그
- 이메일/전화번호/이름 등 개인식별자 전체값 무비판적 로그
- 외부 API secret, DB connection string, key material 로그
- DEBUG/TRACE라는 이유로 민감정보 예외 허용
- MDC/structured JSON에 민감정보 적재
- CR/LF 등 제어문자 포함 원문을 그대로 로그에 기록
## 12. 체크리스트
다음 질문에 “예”로 답할 수 있어야 한다.
- 이 값은 정말 로그에 남겨야 하는가?
- 원문 대신 내부 식별자/마스킹/해시로 충분하지 않은가?
- 토큰, 세션 ID, 비밀번호, key material, 결제정보가 원문으로 남지 않는가?
- request/response 전문을 기본 로그에 넣고 있지 않은가?
- MDC와 structured logging 필드에도 같은 보호 규칙이 적용되는가?
- 외부 입력을 로그에 넣기 전에 sanitization을 검토했는가?
- 공통 마스킹 유틸/구성과 코드 레벨 금지 규칙이 함께 적용되는가?