애플리케이션 보안

시크릿 관리

meellon 2026. 6. 30. 06:00

학습 목표

시크릿(비밀번호·API 키·토큰·TLS key)을 코드·Git·로그에서 분리하는 이유를 설명할 수 있다.

환경변수 주입의 장단점과 K8s Secret 한계(base64·etcd)를 설명할 수 있다.

Vault·클라우드 Secret Manager·External Secrets Operator 패턴을 설명할 수 있다.

Rotation·least privilege·audit이 시크릿 수명주기에서 맡는 역할을 설명할 수 있다.

앞서 Platform ConfigMap·Secret과 Security 관점 연계를 설명할 수 있다.

문제 상황

  • const API_KEY = "sk-live-..." — GitHub public push 한 번에 키 폐기
  • .env커밋 — history에 영구 남음 (git filter-repo 필요)
  • K8s Secret YAML을 repo에 평문 — base64는 인코딩일 뿐
  • kubectl logsDB_PASSWORD=... 출력
  • dev·staging·prod 같은 DB root 비밀번호
  • 퇴사자 개인 API key — 회수·로테이션 프로세스 없음
  • CI에 kubeconfig 평문 variable — fork PR에서 탈취

Part 4 운영 보안 시작 — OWASP·접근 제어 다음은 자격증명이 새는 경로를 막는다.

본 글의 예제 값은 가짜이다. 유출된 키는 즉시 폐기·재발급한다.

1. 시크릿이란

시크릿 — 인증·암호화·API 호출에 쓰이는 비밀 (CWE-798 Hard-coded Credentials).

종류
Password DB·admin
API key / token Stripe, AWS, SaaS
Private key TLS, JWT signing, SSH
Connection string postgres://user:pass@...
Encryption key data at rest, pepper
시크릿 ≠ 설정
  LOG_LEVEL=info     → ConfigMap
  DB_PASSWORD=...    → Secret manager / K8s Secret
  • 앞서 least privilege — 시크릿도 범위·TTL 최소
  • 앞서 misconfiguration — default cred·debug가 시크릿 유출겹침

2. 하면 안 되는 것

# 취약 — code/hardcoded-secret-vulnerable.example
STRIPE_KEY = "sk_live_51AbCdEf..."
conn = psycopg2.connect("postgresql://admin:s3cr3t@db/prod")
안티패턴 위험
소스·이미지에 박기 repo·registry 영구
Git 평문 history·fork·CI log
Slack/티켓 붙여넣기 검색·백업 유출
클라이언트(SPA) 누구나 읽음
로그·에러 Observability 전파
동일 secret dev/prod dev 유출 → prod 침해
# code/gitignore-secrets.example
.env
.env.*
*.pem
secrets/
kubeconfig
  • pre-commit — gitleaks, truffleHog, detect-secrets
  • 유출 시 rotate first — "나중에" ❌

3. 저장 계층

계층 적합 한계
env / .env (local) 개발자 로컬 Git·백업 유출
CI secret variable pipeline scope·rotation 수동
K8s Secret Pod 주입 base64·etcd — 앞서 Platform
Vault / AWS SM / GCP SM 중앙·audit·rotation 운영 복잡도
HSM / Cloud KMS signing key 비용·latency
권장 흐름:
  Source of truth (Vault/SM)  →  sync  →  K8s Secret  →  env/volume  →  app
  • K8s Secret — 전달 수단; 마스터는 vault·SM (앞서 Platform ConfigMap·Secret 주입)
  • etcd encryption at rest — K8s 2차 방어

4. Vault 개요

HashiCorp Vault — 시크릿 저장·동적 발급·audit (개념은 AWS Secrets Manager·GCP Secret Manager와 유사).

기능 설명
Static secret KV — API key 버전
Dynamic secret DB 임시 user·TTL
Encryption as a service transit — 앱이 키 보관 안 함
Policy path·operation 최소
Audit log 누가 언제 read
# 개념 — code/vault-kv.example
vault kv put secret/myapp/db password='...' username='app'
vault kv get -field=password secret/myapp/db
# 앱 startup — short-lived token or sidecar (발췌)
# code/vault-app-init.example
import os
import hvac

client = hvac.Client(url=os.environ["VAULT_ADDR"], token=os.environ["VAULT_TOKEN"])
secret = client.secrets.kv.v2.read_secret_version(path="myapp/db")
os.environ["DB_PASSWORD"] = secret["data"]["data"]["password"]
패턴  
Init container / sidecar Vault Agent — token 갱신
AppRole / K8s auth Pod identity → Vault role
Never root token in prod policy 분리
  • 앞서 Cloud IAM·workload identity — Pod → Vault 인증 (시리즈 후반 확장)
  • dynamic DB cred — SQL 장기 password 불필요

5. Kubernetes 연계

Platform에서 ConfigMap·Secret env/volume 주입을 봤다. Security는 Git·repo에 Secret 없게.

# External Secrets Operator — code/external-secret.example
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: api-db
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: ClusterSecretStore
  target:
    name: api-db
  data:
    - secretKey: DB_PASSWORD
      remoteRef:
        key: secret/myapp/db
        property: password
도구 역할
External Secrets Operator Vault/SM → K8s Secret 동기화
Sealed Secrets 공개 Git에 암호화 Secret
SOPS YAML age/KMS 암호화
cert-manager TLS Secret 자동 (Platform Ingress)
# SealedSecret — cluster public key로 seal, Git OK
# kubeseal --format yaml < secret.yaml > sealed-secret.yaml
규칙  
Git Sealed/External/SOPS — 평문 Secret 금지
RBAC secrets get/list 최소
namespace prod Secret dev 복사 ❌
rotation ESO refreshInterval + rollout
  • CI/CD kubeconfig — OIDC·short-lived token (Platform CI/CD)
  • projected volume — SA token + Secret 한 mount

6. Rotation과 폐기

단계 내용
Inventory 어떤 key가 어디 쓰이는지
Dual active 구·신 key 동시 유효 기간
Deploy 새 Secret → rollout
Revoke old Vault/SM disable version
Break-glass emergency root — 감사·즉시 rotate
Rotation trigger:
  scheduled (90d) · employee offboard · suspected leak · CVE on key material
  • 앞서 JWT — signing key rotate → kid·JWKS
  • DB password — connection pool drain 후 old user drop
  • audit — Secret read 이벤트 (로깅·감사 편)

7. CI/CD·로컬

위치 권장
GitHub Actions secrets.*masked, environment approval
GitLab protected variables, environment scope
로컬 .env gitignore, 1Password·direnv
Docker build ARG secret mount — BuildKit secret, ENV에 남기지 않음
# GitHub Actions — masked secret (발췌)
# code/ci-secret-usage.example
env:
  KUBECONFIG_DATA: ${{ secrets.KUBECONFIG_DATA }}
# never echo secrets; fork PR — secrets unavailable by default
  • PR from fork — secret 없음 — workflow 설계 주의
  • artifact·test report에 env dump 금지

8. 체크리스트

항목 확인
Source Vault/SM single source of truth
Git gitleaks CI · no .env commit
K8s External Secrets / Sealed · etcd encrypt
App no log of secrets · no client-side keys
Scope dev/stage/prod 분리
Rotation SLA·offboard runbook
Access RBAC·policy read audit
안티패턴  
base64 = secure 디코드 trivial
ConfigMap에 password Secret 계열
shared team password 개인·service account
infinite-lived API key TTL·rotate

9. 정리

  • 시크릿 — 코드·Git·로그·클라이언트에서 분리
  • K8s Secret — 주입; 마스터는 Vault/SM + GitOps 암호화
  • Rotation·least privilege·audit — 유출 가정 설계
  • Platform ConfigMap·Secret 주입 + Security 저장·유출 방지

다음에 다룰 것

  • API 보안
  • rate limit, input validation, CORS

해당 내용은 OWASP Secrets Management Cheat Sheet, HashiCorp Vault Documentation, Kubernetes Documentation (Secrets), CWE-798 (Use of Hard-coded Credentials) 의 내용을 기반으로 합니다.

'애플리케이션 보안' 카테고리의 다른 글

로깅과 감사  (0) 2026.07.02
API 보안  (0) 2026.07.01
OWASP Top 10  (0) 2026.06.29
Broken Access Control  (0) 2026.06.28
XSS와 CSRF  (0) 2026.06.27