← 전체 글

Base64는 암호화가 아니다: 개발자의 흔한 오해

Base64 인코딩된 문자열은 뒤죽박죽처럼 보이지만 누구나 함수 한 번이면 해독. Base64가 실제로 무엇이고, 왜 암호화와 혼동되며, 정말 데이터를 보호해야 할 때 무엇을 써야 하는지.

"Base64 로 인코딩하면 아무도 못 읽어" —— 이 문장은 코드 리뷰, Slack, Stack Overflow 답변에 필요 이상으로 자주 등장합니다. Base64 는 암호화가 아닙니다. 난독화도 아닙니다. 어떤 종류의 보안도 아닙니다. 디코딩은 한 번의 함수 호출이면 되고 키나 비밀 정보가 전혀 필요 없습니다. 본 글은 Base64 가 실제 무엇인지, 왜 오해가 지속되는지, 진짜로 데이터를 보호해야 할 때 무엇을 써야 하는지 설명합니다.

Base64 가 실제로 무엇인가

Base64 는 인코딩 방식 입니다 —— 이진 데이터와 64 개의 인쇄 가능 ASCII 문자(A–Z, a–z, 0–9, +, /, 패딩에 =) 사이의 가역 매핑입니다.

입력 3 바이트마다 Base64 문자 4 개가 됩니다. 알고리즘은 완전히 결정적이며 키가 필요 없습니다:

// 인코딩
btoa("hello")          // → "aGVsbG8="
btoa("secret password") // → "c2VjcmV0IHBhc3N3b3Jk"

// 디코딩 —— 한 번의 호출, 키 불필요
atob("c2VjcmV0IHBhc3N3b3Jk") // → "secret password"

그것이 전부입니다. 인코딩된 문자열에 접근 가능한 사람은 누구나 어떤 언어든 어떤 환경에서든 추가 정보 없이 즉시 디코딩할 수 있습니다.

왜 오해가 생기는가

Base64 인코딩 문자열은 암호화된 데이터처럼 보입니다. 대소문자, 숫자, 특수 문자가 섞여 있고 원본 입력과 시각적 유사성이 없습니다. 이 시각적 노이즈는 비기술 관찰자를 속이기에 충분하고 —— 놀랍게도 자주 기술 인력도 속입니다.

이 패턴은 근접성에 의해서도 강화됩니다. Base64 는 보안 관련 문맥에 지속적으로 등장합니다:

  • HTTPS 인증서는 Base64 인코딩(PEM 형식)
  • JWT 토큰의 header 와 payload 는 Base64url 사용
  • HTTP Basic 인증은 자격 증명을 Base64 로 전송
  • SSH 키는 Base64 로 저장
  • 암호화된 암호문도 종종 Base64 로 전송

이런 보안 인접 장소에서 Base64 를 반복적으로 보면 개발자는 인코딩과 보안을 혼동합니다. 인코딩은 단지 포장이고 보안은 그 아래의 암호 연산에서 옵니다.

인코딩 vs 암호화: 핵심 차이

차이는 단순하고 절대적입니다:

인코딩(Base64)암호화(AES, RSA…)
목적이진 데이터를 텍스트로 표현권한 없는 자에게서 데이터 숨김
키 필요?아니오
누구나 역으로?예 —— 한 번의 함수 호출올바른 키가 있을 때만
기밀성 제공?아니오예(올바르게 사용 시)
출력 크기입력보다 약 33% 더 큼알고리즘에 따라 다름

암호화는 키로 데이터를 변환해 키 없이 원본 복구가 계산적으로 실행 불가능하게 만듭니다. Base64 인코딩에는 그런 성질이 없습니다.

현실 세계의 보안 실수

소스 코드에 "인코딩된" 비밀 저장

// ❌ 이것은 비밀이 아님 —— 누구나 디코드 가능
const API_KEY = atob("c2stbGl2ZS1hYmMxMjM0NTY3ODk=");

// 이 코드를 읽은 사람은 실행:
atob("c2stbGl2ZS1hYmMxMjM0NTY3ODk=") // → "sk-live-abc123456789"

자동 시크릿 스캐닝 도구(GitHub 시크릿 스캐닝, GitGuardian, truffleHog)는 탐지의 일부로 Base64 문자열을 디코드합니다. Base64 가 그것들을 막을 거라 기대하면 못 막습니다.

HTTP Basic 인증

// Authorization 헤더는 이렇게 생김:
Authorization: Basic dXNlcjpwYXNzd29yZA==

// 그 Base64 는 자명하게 디코드:
atob("dXNlcjpwYXNzd29yZA==") // → "user:password"

HTTP Basic Auth 는 HTTPS 위에서만 안전합니다 —— 실제 암호화는 TLS 계층이 제공합니다. 헤더의 Base64 는 순전히 username:password 쌍을 단일 텍스트 값으로 인코딩하기 위한 것이지 숨기기 위한 것이 아닙니다. 평문 HTTP 에서는 트래픽을 가로채는 누구든 즉시 자격을 읽을 수 있습니다.

JWT "토큰" 을 비밀로 취급

// JWT 는 점으로 연결된 세 Base64url 섹션처럼 보임:
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjQyfQ.abc123

// 중간(payload)은 자명하게 디코드:
atob("eyJ1c2VySWQiOjQyfQ") // → '{"userId":42}'

JWT 페이로드는 암호화되지 않습니다 —— Base64url 인코딩되고 서명 됩니다. 토큰을 가진 누구나 페이로드를 읽을 수 있습니다. 서명은 변조를 막지만 기밀성을 제공하지 않습니다. JWE(JSON Web Encryption)를 쓰지 않는 한 민감 데이터(비밀번호, 신용카드 번호, 개인정보)를 JWT 페이로드에 넣지 마세요 —— JWE 는 별개의 훨씬 덜 흔한 표준입니다.

URL 에서 데이터 난독화

// 레거시 앱의 일반 패턴
/profile?data=eyJ1c2VySWQiOjQyfQ==

// 어떤 디코더에든 붙여 넣기:
atob("eyJ1c2VySWQiOjQyfQ==") // → '{"userId":42}'

URL 의 Base64 는 보안 이득이 없습니다. 또한 쉽게 깨집니다 —— 표준 Base64 의 +/ 문자는 URL 안전하지 않아 백분율 인코딩이 필요합니다. 그래서 Base64url 변종(+-, /_, 패딩 없음)이 존재합니다.

Base64 가 실제로 무엇을 위한 것인가

Base64 는 정당하고 중요한 용도가 있습니다 —— 보안용은 아닙니다:

  • 텍스트 형식에 이진 데이터 임베드 —— 이미지, 폰트, 파일을 data: URI 로 HTML/CSS 에 임베드, 또는 JSON API 응답에 첨부
  • 이메일 첨부 —— MIME 인코딩은 7 비트 ASCII 만 다루는 프로토콜로 이진 파일을 Base64 로 전송
  • 전송 안전성 —— 일부 채널은 이진(널 바이트, 제어 문자)을 망가뜨립니다; Base64 는 내용이 무결히 통과함을 보장
  • 불투명 식별자 —— ID 나 커서를 Base64 로 인코딩하면 API 소비자에게 "파싱하지 마라" 를 알리지만 보안은 제공하지 않음

정말 보안이 필요할 때 무엇을 쓸까

목표가 권한 없는 데이터 접근 차단이라면 진짜 암호 도구를 쓰세요:

목표사용
저장 데이터 암호화AES-256-GCM(대칭 암호화)
전송 데이터 암호화TLS 1.3(HTTPS)
비밀번호 해싱bcrypt, scrypt, Argon2 —— SHA-256 단독 금지
토큰 서명HMAC-SHA256(JWT HS256) 또는 RS256
비밀 저장환경 변수, 시크릿 매니저(Vault, AWS Secrets Manager)
엔드투엔드 토큰 암호화JWE(JSON Web Encryption) —— 일반 JWT 아님

정말로 암호화된 토큰이 필요할 때: JWE 와 JOSE

표준 JWT 는 서명되었지(JWS) 암호화되지 않았습니다 —— 페이로드는 읽힙니다. 클레임의 기밀성이 정말로 필요하면 JWE(JSON Web Encryption, RFC 7516)를 쓰세요. JWE 는 JOSE 표준군의 일부:

  • JOSE 알고리즘 —— 콘텐츠 암호화(예: A256GCM)와 키 관리(예: RSA-OAEP-256, ECDH-ES, A256KW). "alg" 는 콘텐츠 암호화 키가 어떻게 래핑되는지, "enc" 는 페이로드 자체가 어떻게 암호화되는지 선택.
  • 키 래핑 —— JWE 는 메시지마다 새 콘텐츠 암호화 키를 만들고 수신자의 키(비대칭 또는 대칭) 아래 래핑. 래핑된 키는 토큰 헤더에 실립니다. 같은 평문을 본문 재암호화 없이 다른 수신자에게 암호화할 수 있습니다.
  • JWE 콤팩트 형식 —— 점으로 연결된 다섯 Base64url 섹션(header.key.iv.ciphertext.tag), 시각적으로 JWT 같지만 세 조각이 아니라 네 조각.

검증된 라이브러리(jose, node-jose, python-jose)를 고르고 JWE 를 직접 만들지 마세요 —— 알고리즘과 키 형식 조합은 미묘하며 약한 알고리즘으로의 조용한 폴백은 고전적인 JOSE 버그입니다.

자신의 코드에 대한 빠른 테스트

코드베이스에서 btoa(, atob(, Buffer.from(…, 'base64') 를 검색하세요. 각 등장에 대해 물어보세요:

  • 텍스트를 필드에 맞추거나 전송에 살아남게 하려고 하는가? → 괜찮음.
  • 누군가에게서 데이터를 숨기려 하는가? → 진짜 암호화나 접근 제어로 교체.

자주 묻는 질문

Base64 는 암호화인가요?

아니요. Base64 는 키 없는 가역 인코딩 입니다 —— 누구나 한 번의 함수 호출(atob())로 디코드 가능. 암호화는 키가 필요하며 키 없이 원본 복구를 실행 불가능하게 만듭니다.

Base64 로 비밀번호나 API 키를 저장해도 안전한가요?

아니요. 디코딩은 자명하고 시크릿 스캐너는 Base64 를 자동 디코드합니다. 시크릿 매니저나 환경 변수를 쓰고 비밀번호는 bcrypt, scrypt, Argon2 로 해시하세요.

JWT 페이로드는 누구나 읽을 수 있나요?

예. JWT 페이로드는 Base64url 인코딩되고 서명 되지 암호화되지 않습니다 —— 토큰을 가진 누구나 모든 클레임을 읽습니다. JWE 를 쓰지 않는 한 비밀을 JWT 에 넣지 마세요. 토큰을 온라인 도구에 붙여 넣는 것도 위험; 왜 민감한 JSON 을 온라인에 붙여 넣지 말아야 하는지.

Base64 는 정말 무엇을 위한 것인가요?

이진 데이터를 텍스트로 안전하게 표현 —— data: URI, 이메일(MIME) 첨부, ASCII 만 다루는 채널에서의 전송. 포장이지 보안 수단이 아닙니다.

브라우저에서 인코딩 & 디코딩

지금 Base64 문자열을 검사해야 하나요? fixjson.org 의 Base64 인코드 & 디코드 는 임의 문자열을 즉시 인코드 또는 디코드합니다 —— 완전한 Unicode 와 UTF-8 포함 —— 설치 없이. JWT 페이로드 디버깅, API 응답 검사, data: URI 안 내용 확인에 유용합니다.