본문 바로가기
클라우드/opentelemetry

당신의 서버 로그, 개인정보 유출의 시한폭탄일 수 있습니다 💣 | OpenTelemetry 민감정보 안전하게 처리하는 법

by gasbugs 2025. 11. 8.

안녕하세요! 오늘은 개발자라면 누구나 한 번쯤 고민해봤을 주제, 바로 로그와 개인정보에 대해 이야기해보려고 합니다.

혹시 서비스 장애 분석이나 사용자 행동 추적을 위해 OpenTelemetry Span에 user_id 같은 정보를 그대로 저장하고 계신가요? 🕵️‍♂️

 

만약 그렇다면, 지금 당장 멈추셔야 합니다! 🚨 이는 GDPR, CCPA와 같은 전 세계적인 개인정보 보호 규정을 위반할 소지가 매우 높은, 위험한 관행입니다. 사용자의 민감한 정보가 유출될 경우, 기업의 신뢰도는 물론 법적인 문제까지 발생할 수 있습니다.

그렇다고 사용자 관련 정보를 아예 추적하지 않을 수도 없는 노릇이죠. 특정 사용자에게서만 발생하는 버그를 찾거나, 사용자별 경험을 분석해야 할 때도 있으니까요.

 

그래서 준비했습니다. OpenTelemetry 환경에서 user_id와 같은 민감 정보를 안전하고 스마트하게 다루는 방법을 A부터 Z까지 알려드릴게요!


전체적인 그림 먼저 보기 🗺️

문제를 해결하는 개별 방법들을 살펴보기 전에, 전체적인 흐름을 이해하는 것이 중요합니다. 우리는 두 가지 선택지 앞에서 고민하게 됩니다.

  1. 애플리케이션에서 직접 처리하기: 각 서비스(애플리케이션) 코드 레벨에서 민감 정보를 암호화하거나 변환해서 Span에 기록하는 방식입니다.
  2. 중앙에서 일괄 처리하기: 애플리케이션은 민감 정보를 그대로 보내고, 데이터 수집기(OpenTelemetry Collector)에서 정책에 따라 일괄적으로 처리하는 방식입니다.

어떤 방식이 더 좋을까요? 정답부터 말씀드리면, 장기적으로는 중앙 처리 방식이 훨씬 더 효율적이고 안전합니다. 이제 구체적인 방법들을 하나씩 살펴보며 그 이유를 알아보겠습니다.


해결 방안 1: 가장 쉽고 빠른 해결책, 해싱 (Hashing) 🛡️

가장 먼저 떠올릴 수 있고, 즉시 적용 가능한 방법은 해싱(Hashing)입니다. user_id를 SHA-256 같은 단방향 해시 함수로 변환해서 저장하는 거죠.

👍 장점:

  • 보안: 해시값만으로는 원래의 user_id를 역산할 수 없어 안전합니다.
  • 추적 가능: 동일한 user_id는 항상 동일한 해시값을 생성합니다. 따라서 특정 사용자와 관련된 모든 Span들을 검색하고 추적하는 데 문제가 없습니다.

👎 단점:

  • 모든 애플리케이션 코드에 해싱 로직을 추가해야 합니다.
  • 나중에 해싱 정책이 변경되면(예: SHA-256 -> SHA-512), 모든 서비스의 코드를 수정하고 재배포해야 하는 번거로움이 있습니다.

💻 적용 방법 (Python 예시):

애플리케이션 코드에서 Span의 속성(attribute)을 설정하기 전에, user_id를 직접 해싱 처리합니다.

import hashlib
from opentelemetry import trace

tracer = trace.get_tracer(__name__)

def process_user_request(user_id: str, item_id: str):
    with tracer.start_as_current_span("process_request") as span:
        # ❌ 절대 이렇게 사용하면 안 됩니다!
        # span.set_attribute("user.id", user_id)

        # ✅ user_id를 해싱하여 안전하게 저장합니다.
        hashed_user_id = hashlib.sha256(user_id.encode()).hexdigest()

        # 속성 이름에 .hashed를 붙여 해시된 값임을 명시해주면 더 좋습니다.
        span.set_attribute("user.id.hashed", hashed_user_id) 

        # 민감하지 않은 정보는 그대로 저장해도 괜찮습니다.
        span.set_attribute("item.id", item_id)

        # ... 비즈니스 로직 처리 ...

해결 방안 2: 궁극의 해결책 (강력 추천), OpenTelemetry Collector 👑

더욱 세련되고 체계적인 방법은 OpenTelemetry Collector를 활용하는 것입니다. 이 아키텍처는 MSA(마이크로서비스 아키텍처) 환경에서 빛을 발합니다.

 

🤔 이게 어떻게 동작하나요?

  1. 애플리케이션은 user_id를 평문 그대로 담아서 Collector로 전송합니다. (코드 수정 불필요!)
  2. Collector는 중앙에서 이 데이터를 받은 후, 최종 목적지(Jaeger, Prometheus 등)로 보내기 전에 미리 정의된 규칙에 따라 데이터를 가공합니다.
  3. 이 과정에서 user.id 속성을 찾아 해싱하거나, user.email처럼 더 민감한 정보는 아예 삭제해버릴 수 있습니다.

👍 장점:

  • 코드 청결성: 애플리케이션 코드에 민감 정보 처리 로직이 섞이지 않아, 개발자는 비즈니스 로직에만 집중할 수 있습니다.
  • 중앙 관리: 모든 서비스의 민감 정보 처리 정책을 Collector 설정 파일 하나로 관리할 수 있어 일관성 유지가 매우 쉽습니다.
  • 유연성: 정책이 바뀌어도 애플리케이션을 재배포할 필요 없이 Collector 설정만 변경하면 됩니다.

💻 적용 방법 (Collector 설정 예시):

OpenTelemetry Collector의 config.yaml 파일에 attributes 프로세서를 추가하여 특정 속성을 해시하거나 삭제하도록 설정합니다.

# config.yaml

receivers:
  otlp:
    protocols:
      grpc:

processors:
  batch:
  # attributes 프로세서를 사용하여 속성들을 가공합니다.
  attributes:
    actions:
      # key가 "user.id"인 속성을 찾아 SHA256으로 해싱합니다.
      - key: user.id
        action: hash
      # "user.email"과 같이 더 민감한 정보는 아예 삭제합니다.
      - key: user.email
        action: delete

exporters:
  otlp:
    endpoint: "otel-backend:4317"

service:
  pipelines:
    traces:
      receivers: [otlp]
      # receivers와 exporters 사이에 processor를 끼워넣습니다.
      processors: [batch, attributes] 
      exporters: [otlp]

이제 애플리케이션에서는 마음 편히 아래와 같이 코드를 작성해도 됩니다.

# 애플리케이션에서는 그냥 원본 값을 넣습니다.
span.set_attribute("user.id", "some_user_123")
span.set_attribute("user.email", "sensitive@email.com")

 

위 코드를 실행해도, Collector를 거치면서 user.id는 해시값으로 바뀌고 user.email은 삭제된 채로 최종 백엔드 시스템에 저장됩니다. 정말 깔끔하죠? ✨


그 외 다른 방법들은 없을까? 🤔

물론 다른 방법들도 존재합니다.

  • 가명화 (Pseudonymization) / 토큰화 (Tokenization): user_id를 전혀 의미 없는 다른 값(토큰)으로 대체하는 방법입니다. 해싱과 비슷하지만, 별도의 보안 시스템을 통해 (엄격한 권한 하에) 원래 ID를 다시 조회할 수 있다는 차이점이 있습니다. 더 복잡한 시스템과 높은 수준의 규정 준수가 필요할 때 사용됩니다.
  • 포함하지 않기 (Exclusion): 가장 간단하고 확실한 방법입니다. 디버깅이나 분석에 user_id가 굳이 필요 없다면, 처음부터 Span에 포함하지 않는 것이 최선입니다. 대신 비즈니스 트랜잭션 ID나 요청 ID처럼 민감하지 않은 고유 식별자를 활용하는 것을 고려해볼 수 있습니다.

❌ 절대! 원본 값을 그냥 저장하면 안 되는 이유

"우리 회사는 규모가 작아서 괜찮아", "내부 시스템에서만 보는 건데 뭐 어때"라고 생각하실 수도 있습니다. 하지만 이런 안일한 생각이 큰 사고로 이어질 수 있습니다.

  • 보안 사고: 만약 모니터링 시스템의 접근 권한이 탈취당하거나, 로그 데이터베이스가 유출된다면 어떻게 될까요? 고객들의 개인정보가 그대로 노출되는 끔찍한 상황이 발생합니다.
  • 법적 책임: GDPR과 같은 규정은 매우 엄격합니다. 위반 시 상상 이상의 과징금이 부과될 수 있으며, 이는 회사에 치명적인 타격을 줄 수 있습니다.
  • 신뢰 하락: 데이터 유출 사고는 사용자들의 신뢰를 한순간에 잃게 만듭니다.

민감 정보를 다루는 것은 선택이 아닌 필수입니다.


핵심 요약 & 최종 권장 🚀

자, 이제 총정리를 해볼까요?

  1. 🥇 Best Practice (가장 좋은 방법): OpenTelemetry Collector를 도입하세요. attributes 프로세서를 사용해 민감 정보를 중앙에서 해싱(hash)하거나 삭제(delete)하는 것이 확장성과 유지보수 측면에서 가장 뛰어난 아키텍처입니다.
  2. 🥈 차선책 (대안): Collector를 도입하기 어려운 환경이라면, 애플리케이션 코드 내에서 민감 정보를 직접 해싱하여 속성으로 설정하세요. 이때 user.id.hashed와 같이 속성 이름을 명확히 하여 다른 개발자들이 혼동하지 않도록 하는 것이 좋습니다.
  3. ⛔️ 절대 피해야 할 것: 어떤 경우에도 원본 user_id를 그대로 Span 속성에 저장하지 마세요. 이것은 더 이상 논의할 가치가 없는 문제입니다.

여러분의 서비스와 고객 정보를 안전하게 지키는 첫걸음, 오늘 바로 시작해보시는 건 어떨까요?