Skip to content

ADR-13: NaCl SecretBox 암호화

🇺🇸 English Version

날짜작성자리포지토리
2025-12-23@KubrickCodecore, web, collector

배경

문제 상황

데이터베이스에 저장되는 OAuth 토큰은 암호화가 필요함. 여러 서비스에서 토큰 암복호화가 발생함:

  1. Web 서비스: OAuth 자격증명 저장 시 암호화
  2. Collector 서비스: GitHub API 접근 시 복호화

요구사항

요구사항설명
인증된 암호화변조 방지 및 데이터 무결성 보장
대칭키동일 키로 암복호화
스레드 안전멀티 고루틴 환경에서 동시 암호화 가능
서비스 간 공유web/collector에서 동일 코드베이스 사용
키 로테이션 지원데이터 손실 없이 키 교체 가능

결정

NaCl SecretBox (XSalsa20 + Poly1305)를 대칭 인증 암호화에 사용함.

pkg/crypto 패키지 제공 사항:

  • 암복호화를 위한 Encryptor 인터페이스
  • Base64 인코딩 출력 형식: Base64(nonce || ciphertext)
  • 타입 안전한 에러 처리를 위한 센티널 에러
  • 스레드 안전 구현

검토한 옵션

Option A: NaCl SecretBox (선택됨)

XSalsa20 스트림 암호 + Poly1305 MAC.

장점:

  • 오용 방지 설계: 192비트 nonce로 충돌 위험 사실상 제거
  • 인증됨: Poly1305 MAC으로 변조 감지
  • 단순한 API: 단일 함수, 오용하기 어려움
  • 검증됨: libsodium 구현, 광범위한 보안 감사 완료
  • IV 관리 불필요: 암호화마다 랜덤 nonce 자동 생성

단점:

  • FIPS-140 비준수 (규정 준수 필요 시)
  • 엔터프라이즈 환경에서 AES보다 덜 일반적

Option B: AES-GCM

Galois/Counter Mode AES.

장점:

  • FIPS-140 준수
  • 하드웨어 가속 (AES-NI)
  • 업계 표준

단점:

  • 96비트 nonce: 충돌 위험 높음, nonce 추적 필요
  • nonce 재사용 치명적: 재사용 시 인증 키 노출
  • 복잡한 API, 오용 가능성

Option C: AES-CBC + HMAC

암호화용 AES-CBC, 인증용 별도 HMAC.

장점:

  • FIPS-140 준수
  • 잘 알려진 방식

단점:

  • Encrypt-then-MAC 순서 중요: 잘못된 순서는 보안 취약
  • IV 관리 필요: 유일성 보장 필수
  • 패딩 오라클 공격: 신중한 구현 필요
  • 코드량 증가, 오류 가능성 증가

결과

긍정적

  1. 단순성

    • 단일 Seal/Open 함수 쌍
    • 랜덤 nonce 자동 생성
    • 모드 선택이나 파라미터 조정 불필요
  2. 보안 마진

    • 256비트 키, 192비트 nonce
    • XSalsa20은 원본 Salsa20 대비 nonce 공간 확장
    • 알려진 실용적 공격 없음
  3. 이식성

    • golang.org/x/crypto의 순수 Go 구현
    • CGO 의존성 없음
    • 크로스 플랫폼 빌드 가능

부정적

  1. 규정 준수 제한

    • FIPS-140 인증되지 않음
    • 완화: 현재 사용 사례(OAuth 토큰)에는 허용 가능
  2. 알고리즘 종속

    • 알고리즘 변경 시 전체 데이터 재암호화 필요
    • 완화: 인터페이스 추상화로 구현 교체 가능

구현 세부사항

출력 형식:

Base64(nonce || ciphertext)
       24 bytes   plaintext + 16 bytes overhead

키 관리:

bash
# 32바이트 키 생성
openssl rand -base64 32

인터페이스 설계:

go
type Encryptor interface {
    Encrypt(plaintext string) (string, error)
    Decrypt(ciphertext string) (string, error)
    Close() error  // 메모리에서 키 제거
}

참고 자료

Open-source test coverage insights