🕵️ 친구와 암호 대화
한국어

Base64는 암호화가 아니다: 인코딩의 원리와 쓰임

알 수 없는 글자들이 길게 늘어선 문자열을 보면 암호화된 것 같다는 느낌이 듭니다. 그중 SGVsbG8= 처럼 영문과 숫자로 이루어지고 끝에 등호가 붙은 문자열은 Base64라는 방식으로 적은 것일 가능성이 높습니다. 하지만 Base64는 암호화가 아닙니다. 이 글에서는 Base64가 바이트를 글자로 바꾸는 과정을 직접 따라 해 보고, URL용 변형과 흔한 오해, 그리고 이 서비스에서 쓰이는 방식을 설명합니다.

Base64는 왜 필요한가요

컴퓨터가 다루는 데이터는 결국 바이트의 나열입니다. 그런데 이메일 본문이나 주소창, 설정 파일, JSON 같은 곳은 사람이 읽는 글자만 안전하게 다루도록 만들어진 경우가 많습니다. 임의의 바이트를 그대로 끼워 넣으면 제어 문자나 줄바꿈으로 해석되어 내용이 깨지거나 잘려 나갈 수 있습니다.

그래서 어떤 바이트든 64가지 안전한 글자만으로 표현할 수 있도록 한 약속이 Base64입니다. 사용하는 글자는 영문 대문자 26개, 소문자 26개, 숫자 10개, 그리고 기호 두 개(+ 와 /)입니다. 암호를 숨기려는 목적이 아니라 데이터를 안전하게 운반하기 위한 변환이라는 점이 중요합니다.

변환 원리

핵심은 6비트씩 끊어 읽는 것입니다. 원래 데이터는 8비트(1바이트) 단위인데, 64가지 글자는 정확히 6비트로 표현되는 값의 개수와 같습니다(2의 6제곱은 64). 그래서 3바이트(24비트)를 6비트씩 4덩어리로 나누고, 각 덩어리를 글자 하나로 바꿉니다. 3바이트가 4글자가 되므로 크기는 약 3분의 4배, 즉 약 33% 늘어납니다.

글자 표는 다음과 같습니다. 값 0부터 25까지는 A부터 Z, 26부터 51까지는 a부터 z, 52부터 61까지는 숫자 0부터 9, 62는 +, 63은 / 입니다.

따라 해 보기: Man 이 TWFu 가 되는 과정

영문 세 글자 Man을 변환해 보겠습니다. 각 글자의 ASCII 값과 2진수는 다음과 같습니다.

세 바이트를 이어 붙이면 24비트가 됩니다.

01001101 01100001 01101110
-> 010011 010110 000101 101110

6비트씩 끊은 네 덩어리를 10진수로 읽으면 19, 22, 5, 46 입니다. 글자 표에서 19번째는 T(A가 0번이므로 T는 19번), 22는 W, 5는 F, 46은 소문자 구간에서 20번째라서 u 입니다. 따라서 결과는 TWFu 가 됩니다. 반대로 TWFu를 각각 6비트로 되돌려 8비트씩 다시 끊으면 Man이 나옵니다. 열쇠가 필요한 과정은 어디에도 없습니다.

패딩: 끝의 등호는 무엇인가요

데이터 길이가 3의 배수가 아니면 마지막 덩어리가 모자랍니다. 이때 모자란 비트는 0으로 채워 글자를 만들고, 4글자 단위를 맞추기 위해 등호를 붙입니다. 이것을 패딩이라고 합니다. 예를 들어 한 글자 M은 TQ==, 두 글자 Ma는 TWE= 가 됩니다. 앞서 본 SGVsbG8= 는 Hello를 변환한 것으로, 5바이트라서 끝에 등호가 하나 붙습니다.

등호는 길이를 알려 주는 표시일 뿐 정보가 아닙니다. 그래서 길이를 다른 방법으로 알 수 있는 환경에서는 등호를 생략하기도 합니다.

URL에 쓰는 변형: base64url

일반 Base64의 + 와 / 는 주소(URL)에서 특별한 의미를 갖는 기호입니다. 주소에 그대로 넣으면 공백이나 경로 구분자로 읽힐 수 있어서, 이 두 글자를 - 와 _ 로 바꾼 변형이 쓰입니다. 이것이 base64url입니다. 보통 끝의 등호 패딩도 빼고 씁니다. 나머지 원리는 일반 Base64와 같습니다.

이 서비스에서는 어디에 쓰이나요

이 서비스에서도 base64url이 두 곳에 쓰입니다. 첫째, 번역기 주소 끝의 # 뒤에 붙는 번역기 키입니다. 32바이트짜리 무작위 값을 base64url로 적은 것이어서 주소에 안전하게 들어갑니다. # 뒤 조각이 서버로 가지 않는 이유는 URL의 # 뒤 내용은 왜 서버로 전송되지 않을까에서 설명했습니다.

둘째, 비밀어를 영문숫자 모양으로 표시할 때입니다. 이 서비스의 비밀어는 한글 비밀어, 깨진 한글, 영문숫자의 세 가지 모양으로 적을 수 있는데, 영문숫자 모양은 암호화된 바이트 열 전체를 base64url로 적은 것입니다. 그 바이트 열은 버전 1바이트, 플래그 1바이트, IV 12바이트, 그리고 암호문과 인증 태그가 이어진 구조입니다. 첫 바이트가 버전 값 1이므로 영문숫자 비밀어는 항상 AQ 로 시작합니다.

중요한 점은 영문숫자로 적었다고 해서 그 글자 자체가 안전을 만들지 않는다는 것입니다. 안전은 앞단의 AES-GCM 암호화가 만들고, base64url은 그 결과물을 글자로 옮겨 적는 포장일 뿐입니다. 암호화 과정은 브라우저 Web Crypto API로 암호화하기에서 볼 수 있습니다.

거꾸로 풀어 보기

디코딩은 인코딩의 정반대 과정입니다. 앞에서 본 SGVsbG8= 를 풀어 보겠습니다. 먼저 등호를 제외한 일곱 글자 S, G, V, s, b, G, 8을 표에서 값으로 바꿉니다. S는 18, G는 6, V는 21, s는 소문자 구간에서 18번째라 44, b는 27, G는 6, 8은 숫자 구간에서 8번째라 60입니다. 각 값을 6비트로 적어 이어 붙인 뒤 8비트씩 끊으면 01001000 01100101 01101100 01101100 01101111 이 나오고, 이는 차례로 72, 101, 108, 108, 111, 곧 Hello 입니다. 등호는 마지막 덩어리가 모자랐다는 것을 알려 줄 뿐이므로 계산에는 들어가지 않습니다.

이 과정에서 비밀 값이 하나도 쓰이지 않았다는 점을 눈여겨보시기 바랍니다. 표만 있으면 누구나 같은 결과를 얻습니다. 컴퓨터는 이 계산을 눈 깜짝할 사이에 하므로, 사람이 보기에 복잡해 보이는 문자열이라도 Base64라면 비밀이 전혀 지켜지지 않습니다.

16진수 표기와 비교하면

바이트를 글자로 적는 또 다른 흔한 방법으로 16진수(hex)가 있습니다. 한 바이트를 두 글자로 적기 때문에 크기가 두 배가 됩니다. Base64는 같은 데이터를 약 1.33배로 적을 수 있어 더 짧습니다. 반면 16진수는 바이트 경계가 눈에 잘 보여서 디버깅에는 편리합니다. 둘 다 같은 뜻에서 인코딩일 뿐이고 어느 쪽도 암호화가 아닙니다. 용도에 따라 짧은 쪽이 필요하면 Base64를, 읽고 확인하기 쉬운 쪽이 필요하면 16진수를 고르는 식으로 쓰입니다.

한글도 Base64가 되나요

Base64는 글자가 아니라 바이트를 다루므로, 한글을 변환하려면 먼저 문자를 바이트로 바꾸는 약속이 필요합니다. 오늘날 가장 널리 쓰이는 UTF-8에서는 한글 한 글자가 3바이트입니다. 마침 Base64가 3바이트를 한 묶음으로 처리하기 때문에 한글 한 글자가 정확히 4글자의 Base64가 됩니다. 예를 들어 가 한 글자는 UTF-8로 EA B0 80 세 바이트이고, Base64로는 6rCA 가 됩니다. 영문 한 글자가 1바이트인 것과 비교하면 한글을 담은 Base64는 눈에 띄게 길어집니다.

이런 변환 과정에서 문자 인코딩을 서로 다르게 가정하면 한글이 깨져 보일 수 있습니다. 이는 Base64의 문제가 아니라 바이트를 다시 글자로 읽을 때 쓰는 약속이 맞지 않아서 생기는 문제입니다.

어디서 자주 만나게 되나요

Base64는 생각보다 일상 가까이에 있습니다. 이메일에 파일을 첨부하면 내부적으로 Base64로 바뀌어 본문 속에 실리고, 웹페이지에 작은 이미지를 직접 넣는 데이터 주소 방식도 Base64를 씁니다. 로그인 토큰으로 쓰이는 JWT의 앞부분 역시 base64url로 적힌 JSON입니다. 그래서 토큰 속 내용은 서명이 되어 있어도 누구나 읽을 수 있습니다. 이런 문자열에 비밀번호 같은 값을 그대로 담으면 안 되는 이유가 이것입니다.

어떤 문자열이 Base64로 보일 때 확인할 만한 단서는 세 가지입니다. 알파벳과 숫자, 기호 두 개만 쓰였는지, 길이가 4의 배수이거나 끝에 등호가 있는지, 풀었을 때 읽을 수 있는 글이나 알려진 파일 형식이 나오는지입니다. 하지만 이는 어디까지나 추측이며, 풀어서 무의미한 바이트가 나왔다면 그것이 암호문일 수도 있습니다.

흔한 오해

인코딩, 해시, 암호화가 어떻게 다른지는 암호화·해시·인코딩의 차이에서 더 정리했습니다. 간단히 말하면 인코딩은 형태만 바꾸고 누구나 되돌릴 수 있으며, 암호화는 키가 있어야 되돌릴 수 있습니다.

직접 확인해 보는 방법

브라우저 개발자 도구의 콘솔에서 아래 두 줄을 실행하면 변환을 바로 확인할 수 있습니다. 첫 줄은 인코딩, 둘째 줄은 디코딩입니다.

btoa('Man')        // 'TWFu'
atob('TWFu')       // 'Man'

다만 이 두 함수는 한글처럼 한 글자가 여러 바이트인 문자를 바로 처리하지 못합니다. 한글을 변환하려면 먼저 UTF-8 바이트로 바꾼 뒤 그 바이트를 변환해야 하고, 서비스 내부도 이런 방식으로 바이트를 다룹니다. 어떤 문자열을 만났을 때 Base64 같다는 생각이 든다면, 그것을 풀어 보는 것은 아무 위험도 없는 일이지만 풀린 내용이 비밀이 아닌 것도 아니라는 점을 기억하시면 좋겠습니다. 이 서비스에서 만든 비밀어는 디코딩하더라도 암호문 바이트만 나올 뿐이므로 내용은 보이지 않습니다. 직접 만들어 보시려면 번역기 열기에서 시작하시면 됩니다. 정리하면 Base64는 운반용 포장이고, 자물쇠 역할은 하지 않습니다.

잘못된 내용이나 보완할 점을 발견하셨다면 소개·문의의 이메일로 알려 주세요. 확인 후 글을 고치고 수정일을 갱신합니다.

함께 읽으면 좋은 글

다음 글: HTTPS와 TLS는 무엇을 지켜 주고 무엇은 못 지킬까 → 번역기 열기