developer
Base64는 암호화가 아니다: 인코딩, 해시, 암호화의 차이
Base64로 바꾼 비밀번호는 누구나 되돌릴 수 있습니다. Base64와 SHA-256 해시, 암호화가 각각 무엇을 위한 도구인지 한글 예시와 함께 정리합니다.
modu.tools Editorial · ·
developer
Base64로 바꾼 비밀번호는 누구나 되돌릴 수 있습니다. Base64와 SHA-256 해시, 암호화가 각각 무엇을 위한 도구인지 한글 예시와 함께 정리합니다.
modu.tools Editorial · ·
비밀번호나 API 키를 Base64로 바꿔 저장해 놓고 “암호화했다”고 적어 둔 코드를 가끔 봅니다. 결과가 알아볼 수 없는 문자열이라 안전해 보이지만, Base64는 누구나 같은 방법으로 즉시 되돌릴 수 있는 변환입니다. 이 글은 겉보기에 비슷한 인코딩, 해시, 암호화가 무엇이 다른지, 각각 언제 써야 하는지 정리합니다.
| 구분 | 되돌릴 수 있나 | 열쇠가 필요한가 | 목적 |
|---|---|---|---|
| 인코딩(Base64) | 예, 누구나 | 아니요 | 데이터를 다른 형식으로 옮기기 |
| 해시(SHA-256) | 아니요 | 아니요 | 같은 데이터인지 확인하기 |
| 암호화(AES 등) | 예, 열쇠가 있으면 | 예 | 허락된 사람만 읽게 하기 |
Base64는 RFC 4648에 정의된 인코딩으로, 임의의 바이트를 영문 대소문자, 숫자, +, /의 64개 문자로 표현합니다. 이메일 첨부 파일, 데이터 URL 속 이미지, JSON 안의 바이너리처럼 텍스트만 다룰 수 있는 곳에 바이너리를 싣기 위해 만들어졌습니다.
3바이트(24비트)를 6비트씩 네 덩어리로 나눠 각각을 한 문자로 바꾸기 때문에 결과는 원본보다 약 33% 커집니다. 길이가 3의 배수가 아니면 끝에 =를 붙여 맞춥니다.
modu → bW9kdQ== (4바이트라 = 두 개)한글 → 7ZWc6riA (UTF-8로 6바이트라 패딩 없음)’한글’은 두 글자지만 UTF-8에서 6바이트라 결과가 8글자입니다. 같은 ’한글’을 CP949 바이트로 바꾸면 전혀 다른 결과가 나오므로, 한글을 Base64로 바꿀 때는 어떤 인코딩으로 바이트를 만들었는지가 중요합니다. 자세한 차이는 한글 바이트 가이드에 있습니다.
표준 Base64의 +와 /는 URL에서 특별한 의미가 있습니다. 그래서 RFC 4648은 이 둘을 -와 _로 바꾼 base64url 변형도 정의합니다. JWT가 이 방식을 씁니다. 표준 Base64를 URL에 그대로 넣으면 +가 공백으로 해석될 수 있으니 주의하세요. URL 인코딩 가이드에서 이 문제를 더 다룹니다.
해시 함수는 어떤 길이의 데이터든 정해진 길이의 값으로 바꿉니다. NIST FIPS 180-4에 정의된 SHA-256은 결과가 항상 256비트, 16진수 64글자입니다.
modu → 4de5202b10845b93ab7b3c3782cbe719713d2b60113a0390b78ed73ebbed1de1modv → 81b4031909a37bcdc6b9ba2eacc6075ff4967c70ac681aa675fa49fa0f4a6340한 글자만 달라도 결과가 완전히 바뀌고, 결과에서 입력을 거꾸로 계산할 수 없습니다. 그래서 해시는 이런 데 씁니다.
MD5는 서로 다른 두 입력이 같은 해시를 갖게 만드는 충돌 공격이 실제로 가능합니다. RFC 6151은 디지털 서명처럼 충돌 저항성이 필요한 곳에 MD5를 쓰지 말라고 권고합니다. SHA-1도 같은 이유로 보안 용도에서 물러나는 추세입니다. 단순한 오류 검출에는 아직 쓰이지만, 위조를 막아야 하는 곳에서는 SHA-256 이상을 쓰세요.
SHA-256은 빠르도록 설계되었습니다. 공격자가 흔한 비밀번호 수십억 개의 해시를 미리 계산해 두면 저장된 해시와 비교만 해도 비밀번호를 알아낼 수 있습니다. 그래서 비밀번호는 사용자마다 다른 무작위 값(솔트)을 섞고, 일부러 느리게 만든 전용 함수인 bcrypt, scrypt, Argon2 같은 것으로 저장하는 것이 표준입니다.
암호화는 열쇠를 가진 사람만 원래 데이터를 되돌릴 수 있게 합니다. 같은 열쇠로 잠그고 여는 대칭키 방식(AES 등)과, 공개키로 잠그고 개인키로 여는 공개키 방식이 있습니다. API 키나 개인정보처럼 나중에 원래 값이 필요하지만 아무나 읽으면 안 되는 데이터에 씁니다.
암호화 결과는 바이너리라서 JSON이나 설정 파일에 넣을 때 다시 Base64로 인코딩하는 경우가 많습니다. “암호화 후 Base64”는 맞는 조합이고, “Base64만”은 암호화가 아닙니다.
| 하고 싶은 일 | 알맞은 도구 |
|---|---|
| 이미지를 JSON에 넣어 보내기 | Base64 |
| 다운로드 파일 변조 확인 | SHA-256 |
| 사용자 비밀번호 저장 | bcrypt, scrypt, Argon2 |
| API 키 보관 | 암호화 또는 비밀 관리 서비스 |
| URL에 짧은 토큰 넣기 | base64url |
설정 파일의 Base64 “비밀”. 쿠버네티스 시크릿처럼 값을 Base64로 적는 형식이 있습니다. 이건 YAML에 바이너리를 안전하게 담으려는 것이지 숨기려는 것이 아닙니다. 그 파일을 읽을 수 있는 사람은 값도 읽을 수 있다고 생각해야 합니다.
해시를 비교할 때 줄바꿈. 터미널에서 echo로 문자열을 넘기면 끝에 줄바꿈이 붙어서 해시가 달라집니다. 웹 도구와 터미널 결과가 다르면 먼저 줄바꿈을 의심하세요.
토큰 길이 계산. 32바이트 무작위 값을 Base64로 바꾸면 44글자(패딩 포함), 16진수로 바꾸면 64글자입니다. 데이터베이스 컬럼 길이를 정할 때 원본 바이트가 아니라 인코딩 후 길이를 기준으로 해야 합니다.
modu.tools의 데이터 워크벤치에서 Base64와 SHA-256을 브라우저 안에서 바로 확인할 수 있습니다. 입력값은 서버로 전송되지 않지만, 실제 비밀번호나 운영 키는 어떤 온라인 도구에도 붙여 넣지 않는 습관을 권합니다.
같은 문자열 modu를 세 방식으로 처리하면 성격 차이가 한눈에 드러납니다.
| 방식 | 결과(요약) | 되돌리기 | 길이 변화 |
|---|---|---|---|
| Base64 | bW9kdQ== |
가능(디코딩) | 3바이트 → 4글자(+패딩) |
| SHA-256(16진수) | 64글자 고정 | 불가능 | 입력이 짧아도 출력 길이 고정 |
| AES 같은 암호화 | 키에 따라 달라짐 | 키 있으면 가능 | 블록·IV에 따라 늘어남 |
Base64는 표현만 바꾼 것이고, 해시는 요약한 지문이며, 암호화는 비밀을 유지한 채 보관하는 도구입니다. ’난독화’라는 말로 셋을 섞어 부르면 설계 사고가 납니다.
입력이 (n)바이트일 때 패딩 포함 Base64 길이는 (4 \times \lceil n/3 \rceil)입니다.
| 원본 바이트 | Base64 길이 | 예 |
|---|---|---|
| 1 | 4 (xx==) |
YQ== ← a |
| 2 | 4 (xxx=) |
YWI= ← ab |
| 3 | 4 | YWJj ← abc |
| 16 | 24 | 짧은 API 토큰 원본 |
| 32 | 44 | 흔한 랜덤 시크릿 |
한글 모듈은 UTF-8로 6바이트라 Base64로 8글자(66y47J6s)가 됩니다. 데이터베이스 컬럼을 VARCHAR(32)로 잡았다면 원본 24바이트까지만 안전합니다.
echo는 기본적으로 LF를 붙이고, echo -n은 붙이지 않습니다.sha256:…처럼 접두를 두면 MD5 결과와 섞이지 않습니다.===보다 상수 시간 비교를 씁니다.브라우저 안 도구는 공개해도 되는 샘플, 문서화용 예시, 파일 무결성 확인용 해시 재현에 적합합니다. 운영 중인 비밀번호, 결제 키, 고객 개인정보가 들어간 문자열은 붙여 넣지 마세요. 로컬이라도 화면 공유·브라우저 확장·히스토리에 남을 수 있습니다.
파일 다운로드 페이지가 SHA-256을 공개했다면, 받은 파일을 로컬에서 해시한 뒤 공개 값과 비교하는 것이 정상 절차입니다. 값이 다르면 다시 받거나 미러를 바꾸세요.
Base64 끝의 =는 지워도 되나요?
RFC 4648은 패딩을 기본으로 하되, 길이를 따로 알 수 있으면 생략을 허용합니다. JWT는 패딩을 생략합니다.
해시가 대문자와 소문자로 다르게 나와요. 16진수 표기 차이일 뿐 같은 값입니다.
같은 문자열인데 해시가 달라요. 끝의 줄바꿈이나 공백, 또는 UTF-8과 CP949 같은 인코딩 차이가 원인인 경우가 대부분입니다.