Base64 형식을 다루어야 하나요? 그러면 여러분에게 이 웹사이트가 딱 맞네요! 저희 웹사이트의 아주 편리한 온라인 도구를 사용하여 데이터를 인코딩하거나 디코딩해보세요.

Bash에서의 Base64 인코딩: 완전한 가이드

바이트를 갖고 있고, 문자열이 필요합니다. JSON 본문 안에 살아가야 하는 텍스트 파일. 설정 한 줄에 앉아 있어야 하는 이미지. URL, 환경 변수, 또는 HTTP 헤더를 지나 여행해야 하는 토큰. 인증서 저장소에 둬야 마땅한 개인 키. 이것이 셸에서 Base64 인코딩의 일상적인 일이고, 셸의 답은 단 하나, 작고, 놀라울 정도로 이식 가능한 명령입니다.

그 거래를 한 번의 숨으로 말하면: Base64는 원시 데이터의 바이트 3개씩을 64글자 알파벳(A-Z, a-z, 0-9, 그리고 +와 /)에서 고른 문자 4개로 다시 쓰고, 바이트 수가 3의 배수가 아니면 꼬리에 = 기호 하나 또는 두 개로 채웁니다. 이 사이트의 홈 페이지가 포맷을 완결성 있게 설명합니다. 여기서는 텍스트를 만드는 일, 목적지에 맞는 방언을 고르는 일, 그리고 크기의 대가를 눈 뜬 채로 지불하는 일에 시간을 씁니다. 주머니에 넣어 두어야 할 숫자 하나: 인코딩된 형태는 보통 원본보다 1/3 크고, 바이트 3개당 문자 4개이며, 이 숫자는 계속 되돌아 옵니다.

등장 인물은 적습니다. coreutils의 base64 명령(GNU이거나 더 새로운 Rust 기반 uutils 계열), URL안전 방언을 위한 같은 계열의 basenc, coreutils가 없는 머신을 위한 openssl base64, 임베디드 시스템용 BusyBox 애플릿, 그리고 macOS의 BSD 풍미. 도구 5개, 일은 하나, 알 만한 플래그 몇 개.

인코더를 고르라

이들 모두 표준 입력 또는 파일에서 바이트를 읽고 텍스트를 표준 출력에 쓰므로, 전부 동일한 파이프라인에 끼어 듭니다. 다름은 기본 줄 바꿈과 사용할 수 있는 방언에 있습니다:

도구 어디에 있는가 기본 줄 바꿈 언제 손이 가는가
base64 (coreutils) Linux, 그리고 Homebrew를 통한 macOS 76자 기본 선택지. 한 줄은 -w 0 추가
basenc (GNU coreutils) coreutils를 가진 Linux 76자 --base64url, base32, base16, 또는 그 친구들이 필요할 때
openssl base64 OpenSSL이 설치된 모든 곳 64자 coreutils가 없을 때. 한 줄은 -A
busybox base64 Alpine, 임베디드 Linux 76자 최소 시스템. 작은 몸에 같은 플래그
base64 (BSD/macOS) macOS, BSD 계열 없음 (긴 줄 하나) 네이티브 macOS 작업. -b가 너비를 설정

그 줄 바꿈 열을 두 번 읽어 보세요. 그것이 바로 계열 사이의 조용한 차이입니다. Coreutils와 BusyBox는 기본적으로 76에서, OpenSSL은 64에서 줄을 바꿈하고, BSD 도구는 아예 줄을 바꾸지 않습니다. 누구도 틀린 것은 없습니다. 그저 서로 다른 관례를 물려받은 것뿐입니다(MIME은 76, PEM은 64, 그리고 BSD 도구는 줄 바꿈 습관보다 단순히 오래됐으니까요). 소비자가 신경 쓴다면, 너비를 명시적으로 설정하고 기본값에 기대지 마세요.

텍스트부터: 보이지 않는 줄바꿈

셸에서 텍스트를 인코딩하는 일은 하나의 함정으로 시작합니다: echo가 줄바꿈을 붙여 준다는 것이지요. 그 다섯 글자 "hello"가 echo를 지나가는 순간, 그것들은 6바이트가 되고, 그 여섯 번째 바이트는 보이지도, 사라지지도 않은 채 출력에 동승합니다:

echo "hello" | base64

이것은 aGVsbG8K를 출력하고, 마지막 문자가 줄바꿈을 인코딩합니다. 바이트 수가 중요한 텍스트라면, 치료법은 꾸밈 없이 포맷만 붙인 printf입니다:

printf '%s' "hello" | base64

이제 출력은 aGVsbG8=, 정확히 5바이트어치이며, 마지막 문자는 살아 있는 바이트가 아니라 패딩 기호입니다. 여기스트링에도 같은 규칙이 적용됩니다. echo처럼 끝에 줄바꿈을 붙여 주니까요: base64 <<< "hello"는 다시 aGVsbG8K 버전을 만들어 줍니다. 의문이 들면, 인코딩하기 전에 마지막 바이트가 무엇인지 스스로에게 물으세요.

한 줄로 만들고 싶은 것은 전부 -w 0(또는 아래 나올 -w 0의 친척들)를 붙이세요. 명령이 그렇지 않으면 출력할 마지막 줄바꿈도 함께 제거해 줍니다:

printf '%s' "hello world and more" | base64 -w 0

그것은 깔끔하고, 끊기지 않은 한 줄, 끝에 줄바꿈이 없습니다. URL, JSON 값, 설정 파일 어디에나 추가 의식 없이 그대로 떨어 뜨릴 준비가 된 상태이지요.

파일과 줄 바꿈 너비

파일이 흔한 경우이고, 모든 구현이 FILE 인자를 받습니다. 바이트를 셸의 따옴표 처리 장치로부터 완전히 벗어나게 하는 것이죠:

base64 -w 0 report.pdf > report.b64

-w 0가 없으면, 출력이 76자마다 줄이 바뀐 채로 도착합니다. 바로 MIME 소비자가 원하는 모양이지요:

base64 report.pdf > report.mime.b64

너비는 소비자를 위해 당신이 돌리는 다이얼입니다. 76은 RFC 2045의 MIME 관례이고, 64는 인증서와 키가 쓰는 PEM 관례이며, 0은 URL과 API를 위한 끊기지 않은 한 줄을 뜻합니다:

base64 -w 64 key.bin | head -2

소비자가 Windows에 살고 CRLF 줄 끝을 기대한다면, 줄 바꿈 전에 변환하지 말고, 줄 바꿈을 한 뒤에 변환하세요:

base64 -w 76 attachment.bin | sed 's/$/\r/' > attachment.crlf.b64

OpenSSL 경로에서는, 한 줄 모드와 동일한 것이 -A 플래그이며, 이것도 끝의 줄바꿈을 억제해 줍니다:

openssl base64 -A < report.pdf

줄이 바뀐 파일을 보내기 전에, 크기 건전성 점검은 아무 대가도 없으며 놀라운 만큼의 실수를 잡아 냅니다(두 번 인코딩된 파일, 잘못된 입력으로 인코딩된 파일):

wc -c report.pdf
base64 report.pdf | wc -c

두 번째 숫자는 첫 번째의 약 4/3이어야 하고, 줄바꿈 때문에 줄이 바뀐 줄마다 1바이트씩 더합니다. 크게 다르다면 멈추고, 인코더에게 실제로 무엇을 먹였는지 살펴 보세요.

URL 안전 Base64: 성가신 두 문자를 바꿔치기하기

표준 알파벳의 두 문자, +와 /가 문제 아동입니다. URL 쿼리 문자열의 +는 공백을 뜻하고, /는 경로 구분자처럼 보일 수 있으며, 둘 다 문자열이 URL, 쿠키, 또는 파일명에 들어가는 순간 퍼센트 인코딩을 강제합니다. RFC 4648 5절은 이 문제를 정확히 그 두 문자를 -와 _로 바꾸고 패딩은 없애 버리는 방언으로 해결합니다. URL이 정확한 바이트 수를 알릴 필요가 거의 없기 때문이죠.

셸 레시피는 교환에 다듬기, 알파벳을 위한 tr 한 번과 패딩 제거를 위한 한 번입니다:

printf '\376\117\202' | base64 -w 0 | tr '+/' '-_' | tr -d '='

그 세 바이트는 보통 /k+C로 인코딩되며, URL에서는 보기 흉할 텐데, 파이프라인이 그것을 어디로든 여행할 수 있는 4문자 _k-C로 만들어 줍니다. 교환은 자리에 근거하므로 방향을 쉽게 섞어 버립니다: 인코딩은 tr '+/' '-_'(플러스가 대시로, 슬래시가 언더스코어로), 그리고 디코딩에 속하는 반대 방향은 tr '_-' '/+'입니다. 방향이 섞여도 에러는 나지 않고, 그저 다른 바이트를 만들어 줄 뿐인데, 이것은 보내기 가장 싫은 종류의 버그입니다.

문자열이 셸의 통제를 벗어날 때마다 그 방언이 중요합니다: JWT 세그먼트, 쿼리 문자열의 토큰, 쿠키나 파일명의 값, 그리고 다른 시스템이 URL의 일부로 읽을 어떤 식별자가 그렇습니다. GNU의 basenc는 패딩이 아직 제자리에 있는 채로 그 방언을 네이티브로 만들어 냅니다:

printf '%s' "hello" | basenc --base64url

대부분의 소비자처럼 패딩이 제거된 형태를 원한다면, tr -d '='로 패딩을 벗기세요.

셸에서 JWT 발행하기

JSON Web Token은 API 세계에서 URL 안전 Base64의 가장 눈에 띄는 소비자입니다. RFC 7515에 따르면, 컴팩트 JWT는 점으로 이어 붙인 base64url 세그먼트 3개입니다: 헤더, 페이로드, 그리고 서명. 앞의 두 개는 순수한 JSON이며, 서명은 점으로 이어 붙인 앞의 두 세그먼트에 대한 바이너리 다이제스트이고, 이 일은 정확히 openssl가 잘하는 분야의 것이지요.

key="supersecretkey"
h=$(printf '%s' '{"alg":"HS256","typ":"JWT"}' | base64 -w 0 | tr '+/' '-_' | tr -d '=')
p=$(printf '%s' '{"sub":"42","name":"homer"}' | base64 -w 0 | tr '+/' '-_' | tr -d '=')
s=$(printf '%s' "$h.$p" | openssl dgst -sha256 -hmac "$key" -binary | base64 -w 0 | tr '+/' '-_' | tr -d '=')
printf '%s.%s.%s\n' "$h" "$p" "$s"

이것은 어떤 플랫폼의 어떤 표준 라이브러리가든 받아들일 컴팩트 HS256 JWT를 출력합니다. 역할 분담을 알아차리세요: Base64 부분은 알파벳이고, openssl dgst -sha256 -hmac 부분은 암호학이며, 점으로 이어 붙이는 것은 포맷입니다. 머릿속에서 세 일을 따로 두면, 파이프라인은 명확한 상태를 유지합니다.

현장을 위한 경고 세 가지. 첫째, MAC은 점까지 포함한 앞의 두 세그먼트의 ASCII 텍스트 위에서 계산되므로, 서명할 때 세그먼트는 이미 최종 base64url 형태여야 합니다. 서명한 뒤 줄을 다시 바꾸거나 패딩을 다시 채우면 토큰이 깨집니다. 둘째, 키는 토큰 밖에 머물러야 합니다: 서명은 누가 서명했는지 증명하고, 키는 비밀을 비밀로 유지합니다. 셋째, 셸 스크립트에서의 발행은 테스트와 자동화 도구일 뿐, 실제로 이 토큰들을 발급하고 검증할 서버의 대체재가 아니며, alg: none로 발행된 토큰은 아무것도 증명하지 못합니다.

data URI: 문자열 안에 태운 파일

RFC 2397은 data: URL 스킴을 정의하며, 그 Base64 형태는 파일이 URL 안에 살게 해 줍니다: data:, 이어 선택 가능한 미디어 타입, 페이로드가 Base64로 인코딩된 경우 ;base64, 이어 쉼표, 그리고 데이터. 미디어 타입을 생략하면 기본값은 text/plain;charset=US-ASCII인데, 알 가치가 있는 함정입니다. 대부분의 사람은 ASCII 텍스트가 아니라 이미지나 JSON 문서를 뜻하기 때문이죠.

printf 'data:text/plain;base64,%s\n' "$(printf '%s' "hi there" | base64 -w 0)"

이것은 data:text/plain;base64,aGkgdGhlcmU=를 출력합니다. 완성되고, 자기 완결적인 URL로, 브라우저가 기꺼이 표시해 줄 것입니다. 이미지의 경우, 진짜 미디어 타입을 가진 같은 모양입니다:

printf 'data:image/png;base64,%s\n' "$(base64 -w 0 icon.png)" > icon.uri

결과를 HTML img 태그의 src나 CSS background에 붙여 넣으면, 이미지가 문서와 함께 실려 갑니다. 두 번째 HTTP 요청 없이요. 함정은 전부 크기에 관한 것입니다: RFC 자체가 그 스킴은 짧은 값에만 유용하다고 말하고, 브라우저는 자기만의 URL 길이 제한을 거는 데, 인라인화된 바이트마다 이미지 자체 크기에 33퍼센트 오버헤드를 더 치르게 하고, data URI로 가득 찬 페이지는 그 이미지들에 대한 캐싱 이야기가 없는 페이지입니다. 작은 아이콘과 일회용 임베드 그래픽에는 즐거움이지만, 사진 라이브러리에는 세금입니다.

시크릿, 설정, 그리고 환경 변수

Base64가 설정과 시크릿 작업에 등장하는 이유는 하나, 그리고 꽤 구체적입니다: 공백, 따옴표, 줄바꿈을 포함한 아무 바이트든, 따옴표 곡예 없이 export나 설정 한 줄, JSON 필드를 살아 통과하는 문자열로 바꿔 주니까요. 가장 눈에 띄는 예는 Kubernetes입니다: 시크릿의 .data 아래 모든 필드가 Base64이므로, 셸에서 시크릿을 만드는 일은 인코딩일 뿐입니다:

kubectl create secret generic app --from-literal=password='s3cret'

API 서버는 비밀번호를 .data 아래에 czNjcmV0로 저장하고, 시크릿에 접근 권한이 있는 어떤 노드도 디코딩 한 번으로 다시 읽어 올 수 있습니다. 같은 수법은 당신 자신의 설정 파일에도 통합니다:

export API_TOKEN_B64=$(printf '%s' "$API_TOKEN" | base64 -w 0)

또는, 애플리케이션이 시작할 때 읽는 파일이라면:

printf 'token=%s\n' "$(printf '%s' "$API_TOKEN" | base64 -w 0)" >> app.conf

여기 벽에 붙여 둘 자격이 있는 경고가 옵니다: Base64는 인코딩이지, 암호화가 아닙니다. RFC 4648의 보안 섹션은 이것에 대해 직설적입니다. 이 인코딩은 "패스워드처럼 원래 쉽게 알아볼 수 있는 정보를 시각적으로 숨겨 주지만, 어떤 계산적 비밀성도 제공하지 않는다"고 지적하고, 바로 이 오해가 누군가가 "보호된" 프로토콜 교환을 버그 리포트에 붙여 넣어 실수로 자격 증명을 드러내는 실제 보안 사고들을 만들어 냈다고 말합니다. 값이 비밀이어야 한다면, 암호화하세요(그리고 저장하려고 암문을 Base64로 인코딩합니다). Base64만이 전부라면, 인코딩된 값을 화면을 벗어날 순간부터 평문으로 대하세요.

유니코드, 캐릭터셋, 그리고 그 아래의 바이트

인코더는 문자가 아니라 바이트를 읽고, 셸은 로케일과 명령이 만든 어떤 바이트든 그것에게 넘겨 줍니다. UTF-8 텍스트라면, 보통은 정확히 당신이 원하는 것입니다: héllo의 é는 이미 두 바이트 c3 a9이고, 인코딩은 그것들을 그저 실어 나를 뿐입니다:

printf 'h\xc3\xa9llo' | base64

이것은 aMOpbGxv를 출력하고, 건너편의 UTF-8 소비자는 바이트 그대로 héllo를 되찾습니다. 문제는 출처가 UTF-8이 아닐 때 시작됩니다. 같은 단어를 가진 Latin-1 파일은 é를 단 하나의 바이트 e9로 갖고 있고, 그 바이트를 그대로 인코딩하면 Latin-1 소비자가 아니면 다시 읽을 수 없는 텍스트가 나옵니다. 먼저 변환하고, 그 다음에 인코딩하세요:

iconv -f ISO-8859-1 -t UTF-8 note.txt | base64 -w 0

바이트 수준의 사실이 두 가지 더 있습니다. UTF-8 BOM, 파일 맨 앞의 세 바이트는 77u/로 인코딩되며, 먼저 벗기지 않는 한 당신의 디코딩된 출력 맨 앞에 영원히 앉아 있을 것입니다:

sed '1s/^\xef\xbb\xbf//' file.txt | base64 -w 0

그리고 로케일은 인코딩 자체를 결코 바꾸지 않습니다. 인코더는 바이트 기계이기 때문이죠. 로케일이 바꾸는 것은 당신이 입력한 것뿐입니다. 출력이 잘못 보인다면, 당신이 실행한 인코딩이 아니라 먹인 바이트를 확인하세요.

이메일, API, 그리고 업로드

이메일이 Base64가 예의를 배운 곳이고, 그 예의가 아직도 관례입니다. SMTP는 역사적으로 7비트 ASCII만 실었으므로, RFC 2045에 따라 첨부파일은 CRLF 줄 끝으로 76자마다 줄이 바뀐 Base64로 여행합니다. MIME 파트에 그 정확한 모양을 만들어 내는 일은 줄 바꿈에 줄 끝 변환입니다:

base64 -w 76 attachment.bin | sed 's/$/\r/' > attachment.mime

오래된 수호자는 임베디드 시스템에서 아직도 복무 중입니다: BusyBox의 uuencode에 -m 플래그를 붙이면 친숙한 begin-base64 프레임으로 감긴 MIME Base64가 만들어지고, 그 형제인 uudecode가 다시 읽어 옵니다:

busybox uuencode -m photo.jpg < photo.jpg > photo.uu

API와 업로드는 같은 아이디어에 JSON 옷을 입힙니다: 바이너리가 JSON 필드 안의 Base64 문자열이 되고, curl이 그것을 실어 나릅니다. 바디를 셸 변수에서 만들어 두면 따옴표가 정직합니다:

body="{\"file\":\"$(base64 -w 0 upload.bin)\"}"
curl -fsS -X POST https://httpbin.org/post -H "Content-Type: application/json" -d "$body"

상호 운용 가능성 함정이 둘이 여기에 살고 있습니다. 첫째, API가 어떤 알파벳을 원하는지 확인하세요: 어떤 API는 표준 Base64를, 어떤 API는 URL 안전 방언을 기대합니다. + 문자가 있는 문자열을 URL 안전 엔드포인트에 보내면(또는 그 반대로), 검증에 실패하거나, 더 나쁜 것은, 잘못된 바이트로 디코딩됩니다. 둘째, 이중 인코딩에 눈을 맞추세요. 스크립트가 값을 인코딩해 놓고 서버가 다시 인코딩하는 고전적인 버그로, 왕복을 풀려면 디코딩 두 번이 필요합니다.

페이로드가 커질 때

인코더도 디코더처럼 스트리밍 기계입니다: 청크 단위로 읽고 청크 단위로 쓰므로, 10 GB 타르볼에 13 GB 램이 필요하지 않고, 큰 입력에서도 메모리 사용량은 평탄한 채로 기꺼이 몇 분 동안 실행됩니다. 크기 계산이 당신이 필요한 유일한 계획 도구입니다: 출력은 입력 바이트 3개당 문자 4개에, 줄이 바뀐 줄마다 1바이트가 더하고, 그래서 300 MB 파일은 대략 400 MB의 텍스트가 됩니다. 아무 파일에 대한 빠른 현실 점검:

base64 -w 0 big.bin | wc -c

텍스트 자체가 크기 제한이 있는 채널(이메일 첨부 상한, 티켓 시스템, IM 메시지)을 통과해야 한다면, 원시 바이너리가 아니라 인코딩된 형태를 분할하세요. 그러면 모든 청크가 여전히 붙여 넣기, 압축, 재전송이 가능한 평범한 텍스트일 테니까요:

base64 -w 0 big.bin | split -b 4000 - part_

이것은 4000자 부분들의 연속을 만들어 냅니다. 수신자는 순서대로 cat으로 다시 이어 붙이고 한 번 디코딩합니다. 그리고 페이로드가 압축 가능하다면, 인코딩 전에 압축하세요. Base64는 데이터가 이미 갖고 있는 것 위에 중복성을 더하기 때문이죠: 프로젝트 디렉터리의 타르볼은 보통 33퍼센트 Base64 부가가료가 붙기 전에 gzip으로 몇 배로 줄어 듭니다:

tar czf - project/ | base64 -w 0 > project.b64

속도는 당신의 제약이 되지 않을 것입니다. 이 인코더들은 최신 머신에서 기가바이트를 단 1초도 못 걸려 밀어 내고, 200 MB 파일은 coreutils와 OpenSSL 구현으로 약 0.1초가 들고, 흔한 것 중 가장 느린 BusyBox조차도 여전히 1초의 일부 만에 끝납니다(한 최신 머신에서 200 MB를 대략 0.25초로 측정, coreutils보다 몇 배 느리지만 병목과는 거리가 멉니다). 실제 파이프라인의 병목은 거의 언제나 네트워크이지, 인코딩이 아닙니다.

물어 오는 작은 것들

인코딩 쪽의 함정은 디코딩 쪽보다 작습니다. 그것이 마땅할 정도입니다:

함정 무슨 일이 일어나는가 치료법
echo로 인코더에게 먹이기 끝의 줄바꿈이 출력에 동승하고, 마지막 문자가 그것을 인코딩함 바이트 수가 중요한 텍스트에는 printf '%s'
기본 줄 바꿈에 기대기 도구에 따라 76, 64, 또는 0. 한 줄 소비자는 줄이 바뀐 입력 앞에서 막힘 -w 0(또는 소비자가 원하는 너비)를 명시적으로 설정
출력 안의 끝 줄바꿈 줄 바꿈 모드는 줄바꿈으로 끝나며, 그것이 캡처되면 URL과 JSON을 오염시킴 한 줄에는 -w 0, 아니면 그것이 제거해 주는 $(...)로 캡처
URL 안의 + 또는 / 플러스는 쿼리 문자열에서 공백으로 읽힘. 둘 다 퍼센트 인코딩을 강제 URL에 들어가는 것은 전부 URL 안전 방언 사용
섞인 tr 방향 교환이 유효하지만 잘못된 바이트를 만들고, 어디에도 에러 없음 인코딩은 tr '+/' '-_'. 디코딩은 tr '_-' '/+'
이미 인코딩된 값을 인코딩하기 풀려면 디코딩 두 번이 필요한 이중 인코딩 인코딩 전에 출처가 이미 Base64인지 확인
입력 안의 UTF-8 BOM 모든 디코딩된 출력 맨 앞의 여분 3바이트 BOM을 먼저 벗기기: sed '1s/^\xef\xbb\xbf//'
진짜 시크릿을 Base64로 저장하기 명령 하나로 되돌려 놓음. RFC에는 자격 증명 유출이라는 실제 사건들이 기록되어 있음 비밀은 암호화로, Base64는 전송 모양에만
소비자의 알파벳을 가정하기 표준 대 URL 안전 불일치가 검증 실패 또는 잘못된 디코딩 API 문서를 읽고, 소비자가 요청하는 방언으로 인코딩

당신을 안전하게 지켜 주는 습관들

  • 바이트 수를 명명하라. 텍스트에는 printf '%s', 파일에는 FILE 인자, 그리고 크기가 중요한 것을 보내기 전에 wc -c 건전성 점검.
  • 줄 바꿈을 명시적으로 설정하라. URL과 JSON에는 -w 0, MIME에는 -w 76, PEM에는 -w 64. 너비를 도구의 기본값에 맡기지 마라.
  • 목적지를 위해 알파벳을 고르라. 이메일과 파일에는 표준, 토큰과 URL에는 URL 안전. 그리고 인코딩 전에 소비자의 문서를 확인하라.
  • 인코딩 전에 압축하라. 압축 가능한 어떤 페이로드든, 먼저 gzip 또는 tar czf. 33퍼센트 부가가료는 인코더에게 넘기는 데이터에 적용됩니다.
  • 바이너리가 아니라 텍스트를 분할하라. 크기 제한이 길에 있으면, 모든 청크가 붙여 넣기 안전하도록 인코딩된 형태를 split하고, 단일 디코딩 전에 순서대로 다시 조립하라.
  • JWT 세 일을 따로 두라. 알파벳, 암호학, 포맷: 세그먼트를 인코딩하고, 이어 붙인 세그먼트의 ASCII 텍스트에 서명하고, 나서 출력하라. 순서를 바꾸면 토큰이 깨진다.
  • Base64가 암호화를 대변하게 하지 마라. 값이 비밀이면, 암호화한 뒤 암문을 인코딩하라. 비밀이 아니라면, 그렇게 말하고 걱정할 일을 그만 두라.

셸에서의 인코딩, 짧은 역사

  • 1980, 버클리. Mary Ann Horton가 캘리포니아 대학교 버클리에서 Unix 시스템 사이를 이메일로 바이너리 파일을 실어 나르기 위해 uuencode와 uudecode를 작성합니다. 이름, "Unix에서 Unix로의 인코딩"은 이 포맷의 출생 증명서이고, 이후 약 10년 동안 셸 사용자가 인코딩에 쓰는 것은 바로 이것입니다.
  • 다이얼업 시대. UNIX의 uuencode와 TRS-80, Apple II의 BinHex(Macintosh는 한 발 늦게)는 서로 다른 알파벳으로 같은 문제를 해결하며, 각각 자기 터미널이 출력할 수 있는 문자만 믿습니다.
  • 1993. MIME이 RFC 1521(이후 RFC 2045)에서 이메일을 위한 Base64를 표준화합니다. 오늘도 coreutils의 기본값이 그대로 짊어지고 있는 76자 줄 바꿈과 함께이지요.
  • 2006년 이전의 Linux. base64 명령은 없습니다. 셸 스크립트는 openssl base64, uuencode -m, Perl, 또는 Python에 손을 뻗고, OpenSSL 습관은 너무 뿌리깊어, 현장에 떠도는 오래된 원라이너의 절반이 아직도 그것으로 시작합니다.
  • 2006년 8월 15일. coreutils 6.0이 base64 명령을 내놓습니다. NEWS 파일은 그것을 "base64 encoding and decoding (RFC 3548) functionality"로 올려 주며, 한 명령의 시대가 시작됩니다. 그리고 몇 달 뒤, 2006년 10월, RFC 4648이 이 글이 계속 손이 가는 URL 안전 방언을 포함한 알파벳 가족을 공식화합니다.
  • OS X 10.7. macOS는 기본 줄 바꿈이 없는 BSD 풍미의 자기만의 base64를 내놓습니다. 그래서 "그냥 base64 실행"은 이식 가능한 스크립트에서 플랫폼 확인을 필요로 하지요.
  • 2024. coreutils 9.5가 디코더가 패딩 없고 비정형인 입력을 다루는 방식을 바꿨고, 실질적으로는 인코더가 무죄 처분을 받게 된 것입니다. 오래된 GNU 버전이 거부했을 출력이 이제 깨끗하게 디코딩되니까요. 포맷의 인코딩 쪽은 안정적인 쪽이고, 움직인 것은 디코더 쪽입니다.
  • 2025. coreutils의 Rust 재작성 버전(uutils)이 최신 Ubuntu 릴리스의 기본이 됩니다. 같은 명령, 같은 플래그, 새 엔진, 그리고 C 버전에서 물려받은 같은 76자 기본값.

작은 경이들

  • 포맷의 이름은 모든 머신에서 참이다. printf 'base64' | base64는 GNU, uutils, BusyBox, OpenSSL, macOS 어디서나 YmFzZTY0를 줍니다. 2006년부터 참이었고, 언제나 참일 것입니다.
  • 아무것도 없는 파일은 A의 벽으로 인코딩된다. NUL 바이트 3개를 먹이면 출력이 AAAA입니다. 값 0인 세 바이트가 알파벳에서 인덱스 0인 네 자리에 대응하기 때문이고, 그것들은 각기 A로 표현되니까요. A의 긴 행렬로 시작하는 .b64 파일은 보통 수수께끼가 아니라, 원본 파일의 제로 패딩(NUL 바이트)입니다.
  • 33퍼센트 세금에는 할인이 없다. 바이트 3개당 문자 4개, 압축도, 두 번째 기회도 없습니다. 유일한 탈출구는 데이터를 먼저 압축하는 것이고, 그래서 tar czf가 큰 페이로드 파이프라인의 진짜 영웅입니다.
  • 두 문자가 모든 URL 트러블의 원인이다. +와 /는 대체자가 필요했던 유일한 알파벳 구성원이고, 그 문자들을 은퇴시키기 위해 형식의 방언 하나가 통째로 존재합니다. 62번과 63번, 알파벳의 마지막 두 자립니다.
  • 열한 문자, 예순네 비트. YouTube 동영상 ID는 11문자의 base64url 문자열, URL옷을 입은 64비트 숫자입니다. 그래서 퍼센트 기호 하나 없이 URL을 여행하죠.
  • Git의 가장 유명한 사칭자. git diff --binary의 바이너리 블록은 Base64처럼 보이지만, z 접두가 있는 줄들은 자기만의 base85 스타일 방언입니다. 한 번 보면 그것이 당신의 알파벳이 아님을 알 수 있습니다. grep해서 디코딩하는 우회로 한 번에 스무 분을 잃습니다.
  • 모든 도구가 의도적으로 다르게 줄을 바꾼다. MIME에는 76, PEM에는 64, BSD 도구에는 0: 기본값 셋, 물려받은 관례 셋, 형식은 하나. 너비는 항상 당신이 고를 것이었고, 도구들은 그저 서로 다른 기본값을 기억했을 뿐입니다.
  • 인코더는 당신의 데이터에서 결코 실패하지 않는다. 디코딩 사촌과 달리, 인코더에는 잘못된 입력도, 손상도, 엄격 모드도 없습니다. 바이트를 받아서 문자를 줍니다, 매번. 이 글의 버그는 전부 당신이 그것에게 넘기는 바이트와, 그것을 보내는 목적지에 있습니다.

그리고 여행이 반대 방향으로 향할 때, 즉 문자와 숫자, 가끔 보이는 대시나 언더스코어의 긴 문자열이 터미널에 도착해 바이트가 필요할 때, 아래에 링크된 관련 Base64 디코딩 아티클이 그 의식을 같은 깊이로 다룹니다. 패딩 없는 JWT 세그먼트부터 디코더들이 숨긴 모든 줄바꿈 함정에 이르기까지요.

마지막 업데이트: 2026-09-08

관련 문서: Bash에서의 Base64 디코딩: 완전한 가이드