이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • 로그에 남기면 안 되는 개인정보와 secret은 무엇인가?
  • 마스킹, tokenization, pseudonymous identifier는 각각 어떤 목적에 쓰는가?
  • 로그 보존 기간과 삭제, 감사 요구가 충돌할 때 어떤 설계를 고려해야 하는가?

개요

로그는 사고 조사에 필요하지만 그 자체가 큰 개인정보 저장소가 될 수 있다. 요청 body, token, email, 전화번호, 카드번호, 주소, 주민등록번호, IP, user agent가 한곳에 모이면 애플리케이션 DB보다 더 민감해진다.

마스킹은 사용자 화면만의 문제가 아니다. application log, access log, metric label, trace attribute, alert payload, SIEM field, error tracking attachment에도 적용되어야 한다.

동시에 모든 값을 지우면 사고 조사가 불가능하다. 그래서 원문 대신 masked value, stable internal id, keyed hash, pseudonymous identifier를 사용해 상관관계 분석 가능성과 개인정보 최소화를 함께 맞춘다.

이 문서의 핵심은 “로그를 남길 것인가 말 것인가”가 아니라 “무엇을 어떤 형태로 얼마나 오래 남길 것인가”다.

공격 시나리오

  • 공격자는 로그 조회 권한을 탈취해 token, password reset link, session id를 찾는다.
  • error tracking attachment에 들어간 request body에서 개인정보를 수집한다.
  • metric label에 email이 들어가 장기 보존 시스템에 개인정보가 복제된다.
  • alert payload가 메신저와 이메일로 전파되어 민감값이 통제 밖으로 나간다.
  • 로그 보존 기간이 무기한이라 과거 사용자의 개인정보가 계속 남는다.
  • 사고 대응 중 급히 export한 로그 파일이 암호화 없이 공유된다.

취약한 요청/응답 예시

level=ERROR event=payment_failed userEmail=joseph@example.com card=4111111111111111 bearer=eyJhbGciOi... address="Seoul ..." exception="..."
metric=http_requests_total{path="/api/users/joseph@example.com/orders",status="500"} 1

로그와 metric label은 장기 보존되고 여러 시스템으로 복제된다. 원문 개인정보와 token이 들어가면 삭제와 접근 제어가 매우 어려워진다.

주제별 핵심 판단

  • password, token, secret, private key, authorization code는 로그에 남기지 않는다.
  • 전체 카드번호, 주민등록번호, 계좌번호, 민감 식별자는 원문 저장을 피한다.
  • 이메일과 전화번호도 필요하면 masked value 또는 keyed hash로 남긴다.
  • metric label에는 high cardinality 개인정보를 넣지 않는다.
  • trace attribute와 error report attachment도 로그 정책의 대상이다.
  • 보존 기간은 로그 목적별로 다르게 정한다.
  • 삭제 요청과 감사 보존 요구가 충돌할 수 있으므로 raw PII와 audit event를 분리한다.

개선된 요청/응답 예시

level=WARN event=payment_failed actor=member:1004 emailHash=hmac:78d2 cardLast4=1111 paymentRef=pay_882 result=blocked reason=issuer_declined traceId=6a1e90c2
metric=http_requests_total{route="/api/users/{id}/orders",status="500"} 1

원문 이메일은 keyed hash로 바꾸고, 카드번호는 last4와 provider reference만 남긴다. metric label에는 실제 path parameter 대신 route template을 사용한다.

방어 설계

  • 로그 데이터 분류표를 만든다. 금지, 마스킹, hash, 원문 허용 필드를 분리한다.
  • request/response logging은 기본 비활성화하고 필요한 endpoint만 allowlist로 켠다.
  • log sanitizer를 공통 library 또는 appender/filter로 적용한다.
  • metric label과 trace attribute에는 사용자 입력을 직접 넣지 않는다.
  • alert payload는 외부 메신저와 이메일로 복제되므로 더 엄격하게 축소한다.
  • 보존 기간은 access log, security event, audit log, debug log별로 다르게 둔다.
  • 로그 조회와 export 권한을 분리하고 export 파일은 암호화와 만료를 둔다.
  • 개인정보 삭제 요청 처리 시 로그 보존 예외와 익명화 절차를 법무/개인정보 담당자와 정한다.

Spring/HTTP 예시

public final class SensitiveValueMasker {
    public String maskEmail(String email) {
        int at = email.indexOf('@');
        if (at <= 1) {
            return "***";
        }
        return email.charAt(0) + "***" + email.substring(at);
    }
 
    public String hashForCorrelation(String value) {
        return hmacSha256(correlationKey, value);
    }
}
log.warn("event=payment_failed actor={} emailHash={} cardLast4={} paymentRef={} reason={} traceId={}",
    actor,
    masker.hashForCorrelation(email),
    cardLast4,
    paymentRef,
    reason,
    traceId);

hash는 단순 SHA-256보다 keyed HMAC을 고려한다. 공격자가 가능한 이메일 목록으로 dictionary를 만들 수 있기 때문이다.

운영 로그와 감사 지점

level=WARN event=log_sanitizer_blocked result=blocked field=authorization reason=bearer_token_detected endpoint=POST /api/payments traceId=6a1e90c2
level=INFO event=log_retention_applied result=allowed logType=debug retention=7d storage=hot-index traceId=6a1e90c2
  • sanitizer가 실제로 값을 제거했는지 별도 event로 관찰한다.
  • 로그 보존 정책 적용 결과를 storage tier별로 확인한다.
  • 로그 export 이벤트에는 actor, query, time range, reason, file expiration을 남긴다.
  • alert 전송 실패와 alert payload 축소도 운영 이벤트로 본다.
  • 개인정보 검출 스캔을 정기적으로 수행한다.

실전 판단 기준

  • token을 일부만 잘라 남겨도 replay 가능한 경우가 있다.
  • email hash가 무조건 안전한 것은 아니다. 작은 후보군이면 dictionary 공격이 가능하다.
  • metric label에 user id나 email을 넣으면 비용과 개인정보 문제가 동시에 생긴다.
  • debug log는 짧은 보존과 강한 접근 제어가 필요하다.
  • 사고 대응용 log export는 가장 유출되기 쉬운 2차 산출물이다.
  • 마스킹 정책은 코드 리뷰와 테스트로 강제해야 한다.

테스트 포인트

  • Authorization header와 Cookie가 로그에 남지 않는지 확인한다.
  • request body logging이 password, token, card number를 제거하는지 확인한다.
  • metric label에 이메일, 전화번호, user id 원문이 없는지 확인한다.
  • error tracking attachment에 request body가 붙지 않거나 sanitize되는지 확인한다.
  • 로그 보존 기간이 logType별로 다르게 적용되는지 확인한다.
  • 로그 export가 감사 이벤트로 남고 파일 만료가 적용되는지 확인한다.

위험 신호!

  • 전체 request/response body logging이 운영에서 켜져 있다.
  • metric label에 실제 URL path parameter가 들어간다.
  • alert가 token이나 email 원문을 메신저로 보낸다.
  • debug log 보존 기간이 audit log와 같다.
  • 로그 export 파일이 암호화 없이 공유된다.
  • 개인정보 검출 스캔이 로그 저장소에는 적용되지 않는다.

확인 질문

확인 질문

  • 로그 마스킹은 어디까지 적용해야 하는가?
    • application log, access log, metric label, trace attribute, alert payload, SIEM field, error report 모두에 적용해야 한다.
  • pseudonymous identifier는 왜 필요한가?
    • 원문 개인정보 없이도 같은 사용자나 장치의 반복 패턴을 분석하기 위해 필요하다.
  • 로그 보존 정책은 왜 logType별로 달라야 하는가?
    • debug log, access log, security event, audit log는 목적과 민감도, 필요한 보존 기간이 다르기 때문이다.

참고 문서