RSS

Base64는 암호화가 아니다: 브라우저 Vault의 v2 봉투 암호화

모바일 서명 파일이나 환경설정을 백업하다 보면 값 대부분이 Base64처럼 보일 때가 있다. 이때 가장 먼저 구분해야 할 사실이 있다.

Base64는 데이터를 텍스트로 포장하는 표현 방식이고, 암호화는 권한이 없는 사람이 내용을 읽지 못하게 만드는 보안 장치다.

파일을 Base64로 바꾼 뒤 암호화 저장소에 넣었다면 두 과정이 겹쳐 보일 뿐이다. 이 글에서는 브라우저에서 비밀값을 암호화하고 서버에는 암호문만 저장하는 Vault를 예로 들어, v2 암호문이 무엇이며 어떤 위협을 막고 어떤 위협은 막지 못하는지 설명한다.

먼저 결론

파일 기반 비밀값의 저장 흐름은 다음과 같다.

단계 예시 결과 보안 의미
원본 인증서, JSON, 환경설정 파일 실제 데이터
Base64 변환 영문자·숫자 중심의 긴 문자열 전송과 복원을 위한 텍스트 포장
v2 암호화 버전·IV·암호문으로 구성된 문자열 기밀성과 위변조 검증
DB 저장 v2 암호문 서버는 평문 대신 암호문 보관

원본이 이미 단순 문자열이라면 Base64 과정은 생략할 수 있다. 하지만 Vault 암호화는 생략하지 않는다.

v2는 알고리즘 이름이 아니다

v2는 “두 번째 AES” 같은 알고리즘 이름이 아니라 저장 형식의 버전 표식이다. 암호문은 개념적으로 다음처럼 직렬화된다.

v2.<base64url-IV>.<base64url-ciphertext-and-auth-tag>

각 조각의 역할은 다음과 같다.

  • v2: 파서가 어떤 형식으로 해석할지 결정하는 버전
  • IV: 암호화할 때마다 새로 생성하는 96비트 난수
  • ciphertext-and-auth-tag: AES-GCM이 만든 암호문과 인증 태그

인증 태그는 “복호화가 가능한가”뿐 아니라 암호문이 중간에 바뀌지 않았는지도 확인한다. 한 글자라도 조작되면 정상적인 복호화 결과를 내는 대신 검증에 실패한다.

버전을 앞에 붙여두면 나중에 KDF나 직렬화 방식을 바꿔도 기존 데이터를 구분해 점진적으로 마이그레이션할 수 있다. 중요한 점은 v2라는 이름 자체가 보안 강도를 보장하지는 않는다는 것이다. 실제 보안은 알고리즘 선택, 키 관리, 난수, 구현 경계가 함께 결정한다.

하나의 키를 그대로 저장하지 않는 이유

이 Vault는 봉투 암호화(envelope encryption) 구조를 사용한다.

패스프레이즈 ── KDF ──> KEK ─┐
                              ├─> DEK를 암호화해 보관
복구 코드 ── 복구 키 ────────┘

DEK + 무작위 IV + AAD ── AES-256-GCM ──> 각 비밀값의 v2 암호문

여기에는 두 종류의 키가 등장한다.

  • DEK(Data Encryption Key): 실제 비밀값을 암호화하는 무작위 256비트 키
  • KEK(Key Encryption Key): DEK를 다시 암호화해 봉인하는 키

사용자의 패스프레이즈는 PBKDF2-SHA256과 salt를 거쳐 KEK가 된다. 별도로 생성한 고엔트로피 복구 코드도 같은 DEK를 열 수 있는 두 번째 봉인을 만든다. 서버에는 KDF 파라미터, 봉인된 DEK, 비밀값 암호문이 저장되지만 패스프레이즈와 평문 DEK는 정상 동작 경로에서 전송하지 않는다.

이 구조의 장점은 패스프레이즈를 바꿀 때 모든 비밀값을 다시 암호화할 필요가 없다는 것이다. DEK를 새 KEK로 다시 봉인하면 된다. 반대로 DEK 자체가 노출됐다면 패스프레이즈만 바꾸는 것으로는 충분하지 않다. 이때는 새 DEK로 전체 비밀값을 재암호화해야 한다.

브라우저가 암호화 경계가 된다

암호화와 복호화는 브라우저의 Web Crypto API에서 수행한다. 잠금을 해제하면 DEK는 브라우저 메모리에만 존재하고, 저장소·쿠키·웹 스토리지에는 기록하지 않는다. 사용을 마치거나 자동 잠금이 동작하면 메모리에서 키 참조를 제거한다.

비밀값을 새로 저장할 때의 흐름은 다음과 같다.

  1. 브라우저가 비밀값 ID와 무작위 IV를 준비한다.
  2. 메모리에 있는 DEK로 값을 AES-256-GCM 암호화한다.
  3. 결과를 v2 문자열로 직렬화한다.
  4. API에는 평문이 아니라 v2 암호문만 전송한다.
  5. 서버는 암호문 형식이 아닌 값의 저장을 거부한다.

서버의 형식 검사는 평문이 실수로 저장되는 것을 막는 방어선이다. 다만 정규식으로 v2 모양을 확인하는 것만으로 암호문의 진위를 증명할 수는 없다. 진짜 무결성 검증은 복호화 시 AES-GCM 인증 태그가 담당한다.

AAD로 암호문과 항목 ID를 묶는다

AES-GCM은 암호화하지 않지만 인증에는 포함하는 AAD(Additional Authenticated Data)를 지원한다. 각 비밀값을 암호화할 때 항목 ID를 AAD에 포함하면 암호문이 그 ID에 결박된다.

이 결박이 없으면 DB 수정 권한을 얻은 공격자가 A 항목의 암호문을 B 항목으로 옮겨 관리자를 혼란스럽게 만들 수 있다. AAD를 사용하면 다른 ID로 옮긴 암호문은 인증 태그 검증에 실패한다.

AAD는 모든 문제를 해결하지는 않는다. 같은 ID에 과거 암호문을 되돌려 넣는 롤백 공격까지 탐지하려면 별도의 버전 번호, 변경 이력 또는 외부 무결성 검증이 필요하다.

Base64와 v2가 함께 보이는 이유

인증서나 프로비저닝 프로파일 같은 바이너리 파일은 GitHub Actions 같은 텍스트 기반 비밀값 저장소에 직접 넣기 불편하다. 그래서 먼저 Base64로 바꾼다.

원본 파일
  └─ Base64 인코딩
       └─ Vault v2 암호화
            └─ DB 저장

복원 순서는 반대다.

DB의 v2 암호문
  └─ Vault 복호화
       └─ Base64 문자열
            └─ Base64 디코딩
                 └─ 원본 파일

따라서 DB에서 보이는 v2 문자열은 암호화된 값이고, Vault를 연 뒤 나타나는 Base64 문자열은 아직 파일로 되돌리기 전의 포장된 값이다. Base64 문자열을 발견했다고 해서 암호화됐다고 판단해서는 안 된다.

이 설계가 잘 막는 것

  • DB 덤프나 백업 파일만 단독으로 유출된 상황에서 비밀값 평문 노출
  • 서버 로그나 스냅샷에 평문 비밀값이 우연히 남는 문제
  • 암호문 일부를 조작하거나 다른 항목으로 바꾸는 공격
  • 패스프레이즈 변경 때 대량 재암호화가 필요한 운영 부담

단, 이는 암호문과 복호화 수단이 함께 유출되지 않았다는 전제에서 성립한다.

이 설계만으로 막지 못하는 것

  • 잠금 해제 시점의 XSS나 악성 브라우저 확장 프로그램
  • 변조된 프런트엔드 코드가 패스프레이즈나 복호화 결과를 훔치는 공격
  • 짧거나 재사용된 패스프레이즈에 대한 오프라인 추측
  • 패스프레이즈와 동등한 권한을 가진 복구 코드의 유출
  • 화면에 표시된 평문이나 클립보드 내용의 노출
  • 항목명·메모·수정 시각처럼 별도로 암호화하지 않은 메타데이터의 노출
  • DB와 봉인 설정을 잃어버리는 데이터 손실

암호화는 백업을 대신하지 않는다. 암호문, 봉인된 DEK, KDF 설정을 함께 백업해야 복구할 수 있다.

운영 체크리스트

  1. 패스프레이즈는 길고 고유하게 만들고 다른 서비스와 재사용하지 않는다.
  2. 복구 코드는 패스프레이즈와 분리해 오프라인으로 보관한다.
  3. HTTPS, CSP, 의존성 검증, XSS 방어를 암호 알고리즘만큼 중요하게 다룬다.
  4. DEK는 브라우저 메모리에만 두고 무활동 자동 잠금을 적용한다.
  5. 평문 클립보드는 짧은 시간 후 지우고 다른 앱의 클립보드 접근을 주의한다.
  6. 백업에는 암호문 DB와 Vault 봉인 설정이 모두 포함되는지 실제 복원으로 검증한다.
  7. 패스프레이즈 유출과 DEK 유출을 구분해 회전 범위를 결정한다.
  8. v3 같은 새 형식을 도입할 때는 구버전 복호화와 마이그레이션 테스트를 먼저 준비한다.

듀오랩스가 보는 관점

좋은 비밀값 저장소는 단순히 “강한 알고리즘을 썼다”에서 끝나지 않는다. 평문이 어느 경계를 통과하는지, 서버가 무엇을 볼 수 있는지, 키가 언제 메모리에 존재하는지, 복구 수단을 어떻게 분리하는지까지 함께 설계해야 한다.

Base64는 파일을 다루기 쉽게 만들고, v2 암호화는 내용을 읽기 어렵게 만들며, 봉투 암호화는 키 교체와 복구를 운영 가능하게 만든다. 서로 다른 문제를 해결하는 세 도구를 구분할 때 비로소 안전하면서도 복구 가능한 백업 구조를 만들 수 있다.