64개의 문자
Base64는 입력을 3바이트(24비트)씩 읽어서 6비트 숫자 4개로 나눕니다. 0부터 63까지의 각 숫자는 이 표의 인덱스입니다. 순서는 RFC 4648이 정해 두었습니다. 025는 대문자, 2651은 소문자, 52~61은 숫자, 그다음이 +와 /입니다. 마지막 열은 해당 문자의 ASCII 코드로, 실제로 저장되거나 전송되는 값이 바로 이것입니다.
| 인덱스 | 6비트 | Base64 | Base64URL | ASCII 코드 |
|---|---|---|---|---|
| 0 | 000000 | A | A | 0x41 |
| 1 | 000001 | B | B | 0x42 |
| 2 | 000010 | C | C | 0x43 |
| 3 | 000011 | D | D | 0x44 |
| 4 | 000100 | E | E | 0x45 |
| 5 | 000101 | F | F | 0x46 |
| 6 | 000110 | G | G | 0x47 |
| 7 | 000111 | H | H | 0x48 |
| 8 | 001000 | I | I | 0x49 |
| 9 | 001001 | J | J | 0x4A |
| 10 | 001010 | K | K | 0x4B |
| 11 | 001011 | L | L | 0x4C |
| 12 | 001100 | M | M | 0x4D |
| 13 | 001101 | N | N | 0x4E |
| 14 | 001110 | O | O | 0x4F |
| 15 | 001111 | P | P | 0x50 |
| 16 | 010000 | Q | Q | 0x51 |
| 17 | 010001 | R | R | 0x52 |
| 18 | 010010 | S | S | 0x53 |
| 19 | 010011 | T | T | 0x54 |
| 20 | 010100 | U | U | 0x55 |
| 21 | 010101 | V | V | 0x56 |
| 22 | 010110 | W | W | 0x57 |
| 23 | 010111 | X | X | 0x58 |
| 24 | 011000 | Y | Y | 0x59 |
| 25 | 011001 | Z | Z | 0x5A |
| 26 | 011010 | a | a | 0x61 |
| 27 | 011011 | b | b | 0x62 |
| 28 | 011100 | c | c | 0x63 |
| 29 | 011101 | d | d | 0x64 |
| 30 | 011110 | e | e | 0x65 |
| 31 | 011111 | f | f | 0x66 |
| 32 | 100000 | g | g | 0x67 |
| 33 | 100001 | h | h | 0x68 |
| 34 | 100010 | i | i | 0x69 |
| 35 | 100011 | j | j | 0x6A |
| 36 | 100100 | k | k | 0x6B |
| 37 | 100101 | l | l | 0x6C |
| 38 | 100110 | m | m | 0x6D |
| 39 | 100111 | n | n | 0x6E |
| 40 | 101000 | o | o | 0x6F |
| 41 | 101001 | p | p | 0x70 |
| 42 | 101010 | q | q | 0x71 |
| 43 | 101011 | r | r | 0x72 |
| 44 | 101100 | s | s | 0x73 |
| 45 | 101101 | t | t | 0x74 |
| 46 | 101110 | u | u | 0x75 |
| 47 | 101111 | v | v | 0x76 |
| 48 | 110000 | w | w | 0x77 |
| 49 | 110001 | x | x | 0x78 |
| 50 | 110010 | y | y | 0x79 |
| 51 | 110011 | z | z | 0x7A |
| 52 | 110100 | 0 | 0 | 0x30 |
| 53 | 110101 | 1 | 1 | 0x31 |
| 54 | 110110 | 2 | 2 | 0x32 |
| 55 | 110111 | 3 | 3 | 0x33 |
| 56 | 111000 | 4 | 4 | 0x34 |
| 57 | 111001 | 5 | 5 | 0x35 |
| 58 | 111010 | 6 | 6 | 0x36 |
| 59 | 111011 | 7 | 7 | 0x37 |
| 60 | 111100 | 8 | 8 | 0x38 |
| 61 | 111101 | 9 | 9 | 0x39 |
| 62 | 111110 | + | - | 0x2B |
| 63 | 111111 | / | _ | 0x2F |
| 패딩 | – | = | 보통 생략됨 | 0x3D |
외워 둘 만한 범위가 두 개 있습니다. A부터 P(015)까지의 문자는 모두 비트 63)까지의 문자는 모두 00으로 시작하고, w부터 /(4811로 시작합니다. 접두사를 손으로 확인할 때 문자 하나를 머릿속으로 디코딩하기에는 이 정도면 충분합니다. 비트를 나누는 과정을 따라가 보는 예시는 Base64의 작동 원리에 있습니다.
알파벳 순서는 ASCII 순서와 다릅니다. ASCII에서는 +와 /가 먼저 오고, 그다음 숫자, 대문자, 소문자 순입니다. 따라서 Base64 문자열을 정렬해도 원래 바이트 순서로 정렬되지 않습니다. +A==(바이트 F8)가 AA==(바이트 00)보다 앞에 정렬됩니다. 인코딩된 값이 원시 바이트와 같은 순서로 정렬되어야 한다면 RFC 4648의 base32hex나 일반 hex를 사용하세요.
URL-safe 알파벳
+와 /는 URL에서 특별한 의미를 가집니다. +는 쿼리 문자열과 폼 데이터에서 공백으로 디코딩되고, /는 경로 구간을 나눕니다. RFC 4648 5절에서 정의한 Base64URL은 인덱스 0~61은 그대로 두고 마지막 두 문자만 바꿉니다.
| 인덱스 | Base64 | Base64URL |
|---|---|---|
| 62 | + | - |
| 63 | / | _ |
비트 배치를 포함해 나머지는 모두 같으므로, 둘 사이의 변환은 문자 치환에 패딩 추가나 제거를 더한 것뿐입니다. JWT, 많은 API 토큰, 파일 이름에 안전한 식별자는 Base64URL을 쓰며 대개 =가 없습니다. Base64URL 인코더로 바로 만들 수 있습니다. 자세한 비교는 Base64와 Base64URL 비교에 있습니다.
패딩 문자
=(ASCII 0x3D)는 64개 문자 알파벳에 속하지 않으며 데이터를 인코딩하지 않습니다. 입력 길이가 3의 배수가 아닐 때 마지막 4문자 그룹을 채우는 역할만 합니다.
- 1바이트가 남으면 데이터 문자 2개에
==가 붙습니다. - 2바이트가 남으면 데이터 문자 3개에
=가 붙습니다. - 남는 바이트가 없으면 패딩도 없습니다.
그래서 패딩된 문자열의 길이는 항상 4의 배수이고, ===는 절대 나오지 않습니다. 패딩이 없으면 길이를 4로 나눈 나머지는 0, 2, 3 중 하나이며 1은 나오지 않습니다. =를 생략해도 되는 경우와 복원하는 방법은 패딩 가이드에서 다룹니다.
어떤 문자가 어디에 나타나는가
몇 가지 패턴은 비트 배치에서 곧바로 나옵니다. 아래 내용은 해당하는 모든 바이트 값을 전수 조사해 확인했습니다.
== 앞에는 A, Q, g, w만 옵니다. 1바이트가 남으면 두 번째 문자는 데이터 비트 2개 뒤에 0비트 4개를 담으므로 인덱스가 0, 16, 32, 48 중 하나입니다. = 하나 앞에서는 세 번째 문자의 끝 2비트가 0이므로 A E I M Q U Y c g k o s w 0 4 8의 16개 문자 중 하나가 됩니다. 그 자리에 다른 문자가 있으면 정규(canonical)가 아닌 인코딩이며, 엄격한 디코더는 이를 거부합니다.
무작위 바이트는 64개 문자를 고르게 사용합니다. 무작위 데이터의 각 6비트 그룹은 균등하게 분포하므로, 암호화된 데이터, 키, 압축 파일은 문자 분포가 평평합니다. 분포가 한쪽으로 치우쳐 있다면 데이터가 텍스트이거나 구조를 가지고 있다는 신호입니다.
ASCII 텍스트는 소문자 g~z, 숫자, +, /를 그룹 첫 자리에 두지 않습니다. 각 4문자 그룹의 첫 문자는 바이트의 상위 6비트를 담습니다. 7비트 ASCII는 최상위 비트가 0이므로 이 문자는 항상 A와 f 사이(인덱스 0~31)입니다. SGVsbG8gd29ybGQh(Hello world!)에서 그룹의 첫 문자는 S, b, d, b입니다.
ASCII 텍스트의 Base64에서 +와 /는 드뭅니다. 0x80 미만의 바이트에서 +는 그룹의 네 번째 문자로만, 그것도 세 번째 바이트가 >나 ~일 때만 나타날 수 있습니다. /는 그 바이트가 ?이거나 DEL 제어 문자일 때만 그 자리에 나타납니다. 그래서 Hi?>는 SGk/Pg==로, <b>는 PGI+로 인코딩됩니다.
A가 이어지면 0 바이트, /가 이어지면 0xFF입니다. AAAA는 00 바이트 3개이고, ////는 FF 바이트 3개입니다. A가 길게 이어지는 모습은 빈 곳이 많은 바이너리 형식, UTF-16 텍스트(Hello는 SABlAGwAbABvAA==), SSH 키 앞부분의 길이 필드에서 흔히 보입니다. 알아보기 쉬운 패턴은 흔한 Base64 접두사에 더 정리해 두었습니다.
다른 Base64 알파벳
6비트당 64개 문자라는 아이디어는 유지하되 다른 문자를 고른 시스템이 여럿 있습니다.
| 변형 | 인덱스 순서의 알파벳 | 쓰이는 곳 |
|---|---|---|
| 표준 | A-Z a-z 0-9 + / | RFC 4648, MIME, PEM, 대부분의 API |
| URL-safe | A-Z a-z 0-9 - _ | RFC 4648 5절, JWT |
| IMAP 메일함 이름 | A-Z a-z 0-9 + , | RFC 3501 수정 UTF-7 |
| bcrypt | . / A-Z a-z 0-9 | $2a$, $2b$, $2y$ 비밀번호 해시 |
| crypt(3) | . / 0-9 A-Z a-z | MD5-crypt $1$, SHA-crypt $5$와 $6$, DES crypt |
| xxencode | + - 0-9 A-Z a-z | 옛 Unix 메일과 Usenet |
| uuencode | ASCII 32~95(공백부터 _까지), 0에는 흔히 ` 사용 | 옛 Unix 메일과 Usenet |
- IMAP은
/가 메일함 이름에서 흔히 계층 구분자로 쓰이기 때문에/를,로 바꿉니다.Entwürfe라는 폴더는Entw&APw-rfe로 저장됩니다. - bcrypt는
.와/를 앞으로 옮기고 문자를 숫자보다 앞에 둡니다. 솔트와 해시는 이 알파벳의 패딩 없는 Base64이므로, 표준 디코더로 디코딩하면 잘못된 바이트가 나옵니다. - crypt(3) 는 ASCII 순서인
./0-9A-Za-z를 사용합니다. 또한 최하위 비트 쪽부터 비트를 채우므로, 알파벳을 변환하더라도 표준 디코더로는 해시 바이트를 재현할 수 없습니다. - uuencode와 xxencode는 MIME보다 먼저 나왔습니다. uuencode와 xxencode 도구로 두 형식을 모두 읽고 쓸 수 있습니다.
Base32, Base58, Base85처럼 알파벳 크기가 다른 인코딩은 Base64와 hex, Base32, Base58, Base85 비교를 참고하세요. 여기에 나온 용어의 정의는 용어집에 있습니다.
자주 묻는 질문
Base64 알파벳은 몇 개의 문자로 이루어져 있나요?
데이터용 문자는 64개입니다. 대문자 26개, 소문자 26개, 숫자 10개, 그리고 +와 /입니다. 패딩 기호 =는 65번째 기호로, 데이터를 담지 않습니다.
문자 A의 Base64 값은 무엇인가요?
A는 인덱스 0, 2진수로 000000입니다. B는 1, Z는 25, a는 26, z는 51, 0은 52, 9는 61, +는 62, /는 63입니다.
Base64URL은 어떤 문자를 바꾸나요?
두 개뿐입니다. +(인덱스 62)는 -로, /(인덱스 63)는 _로 바뀝니다. 보통 패딩도 함께 생략합니다. Base64를 Base64URL로 변환에서 바꿀 수 있습니다.