모든 게시글보안

Base64와 보안: 흔한 오해와 진실

Base64는 암호화가 아닙니다. 보안 관점에서 Base64의 올바른 사용법을 알아봅니다.

22분 읽기

Base64는 암호화가 아닙니다

많은 개발자들이 Base64를 암호화로 오해합니다. 하지만 Base64는 단순히 인코딩 방식일 뿐, 어떠한 보안도 제공하지 않습니다.

인코딩 vs 암호화

구분인코딩 (Base64)암호화 (AES, RSA)
목적데이터 형식 변환데이터 보호
복원누구나 가능키가 있어야 가능
보안성없음높음
용도데이터 전송민감 정보 보호

흔한 실수들

1. 비밀번호를 Base64로 저장

절대 하지 마세요!

// ❌ 잘못된 예 const password = btoa('myPassword123'); saveToDatabase(password); // → 누구나 atob()로 쉽게 복원 가능! // ✅ 올바른 예 - 해시 함수 사용 const bcrypt = require('bcrypt'); const hashedPassword = await bcrypt.hash('myPassword123', 10); saveToDatabase(hashedPassword);

2. API 키를 Base64로 "숨기기"

// ❌ 클라이언트 코드에서 API 키를 Base64로 인코딩 const apiKey = atob('c2VjcmV0S2V5MTIz'); // → 브라우저 개발자 도구에서 바로 확인 가능! // ✅ 환경 변수나 서버사이드에서 관리 // .env 파일 API_KEY=secretKey123 // 서버 코드 const apiKey = process.env.API_KEY;

3. 민감한 데이터를 URL에 Base64로 포함

// ❌ 민감한 정보를 URL 파라미터로 전달 const userInfo = btoa(JSON.stringify({ email: 'user@example.com', creditCard: '1234-5678-9012-3456' })); window.location = `/checkout?data=${userInfo}`; // → URL 로그, 브라우저 히스토리에 그대로 남음! // ✅ 서버 세션이나 암호화된 토큰 사용 // 서버에 세션 저장 후 세션 ID만 전달 window.location = `/checkout?session=${sessionId}`;

Base64를 사용해야 할 때

Base64는 다음과 같은 비보안 목적으로 사용해야 합니다:

1. 이메일 첨부파일

// MIME 인코딩 - 바이너리 데이터를 텍스트로 변환 const attachment = btoa(fileContent);

2. Data URI - 이미지 임베딩

<!-- HTTP 요청을 줄이기 위한 이미지 임베딩 --> <img src="data:image/png;base64,iVBORw0KG..." alt="로고">

3. JWT 토큰 페이로드

JWT의 페이로드는 Base64로 인코딩되지만, **서명(Signature)**으로 보호됩니다.

// JWT 구조: header.payload.signature // payload는 Base64 인코딩 (누구나 읽을 수 있음) // signature로 변조 방지 const jwt = 'eyJhbGci...';

Base64를 사용하면 안 될 때

❌ 비밀번호 저장

// 절대 안됨 const password = btoa('password123');

대신 사용: bcrypt, scrypt, Argon2

❌ 민감한 데이터 보호

// 절대 안됨 const creditCard = btoa('1234-5678-9012-3456');

대신 사용: AES-256, RSA 암호화

❌ API 키 숨기기

// 절대 안됨 - 클라이언트 코드 const apiKey = atob('encoded_key');

대신 사용: 서버사이드 환경 변수, AWS Secrets Manager

실제 암호화가 필요할 때

Web Crypto API 사용 (브라우저)

async function encrypt(text, password) { const encoder = new TextEncoder(); const data = encoder.encode(text); // 비밀번호에서 키 생성 const keyMaterial = await crypto.subtle.importKey( 'raw', encoder.encode(password), 'PBKDF2', false, ['deriveBits', 'deriveKey'] ); const key = await crypto.subtle.deriveKey( { name: 'PBKDF2', salt: crypto.getRandomValues(new Uint8Array(16)), iterations: 100000, hash: 'SHA-256' }, keyMaterial, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt'] ); // 데이터 암호화 const iv = crypto.getRandomValues(new Uint8Array(12)); const encrypted = await crypto.subtle.encrypt( { name: 'AES-GCM', iv }, key, data ); return { encrypted, iv }; }

Node.js에서 암호화

const crypto = require('crypto'); function encrypt(text, password) { const algorithm = 'aes-256-cbc'; const key = crypto.scryptSync(password, 'salt', 32); const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv(algorithm, key, iv); let encrypted = cipher.update(text, 'utf8', 'hex'); encrypted += cipher.final('hex'); return { encrypted, iv: iv.toString('hex') }; } function decrypt(encrypted, iv, password) { const algorithm = 'aes-256-cbc'; const key = crypto.scryptSync(password, 'salt', 32); const decipher = crypto.createDecipheriv( algorithm, key, Buffer.from(iv, 'hex') ); let decrypted = decipher.update(encrypted, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted; }

보안 체크리스트

프로젝트에서 Base64를 사용할 때 다음 사항을 확인하세요:

  • Base64를 암호화 대신 사용하고 있지 않은가?
  • 비밀번호나 API 키를 Base64로 인코딩하고 있지 않은가?
  • 민감한 데이터를 URL에 Base64로 포함하지 않는가?
  • 클라이언트 코드에서 Base64로 "보안"을 구현하려 하지 않는가?
  • 실제 보안이 필요한 곳에서는 적절한 암호화를 사용하는가?

마치며

Base64는 인코딩이지 암호화가 아닙니다. 데이터 형식 변환에는 유용하지만, 보안 목적으로는 절대 사용해서는 안 됩니다.

민감한 데이터는 반드시 적절한 암호화 알고리즘(AES, RSA 등)과 해시 함수(bcrypt, scrypt 등)를 사용하여 보호하세요.

안전한 Base64 변환이 필요하다면 Base64.kr 변환기를 사용해보세요!