파일 업로드
  1. base64hub
  2. Base64 가이드
  3. Base64 오류
문제 해결

Base64 디코딩 오류와 해결 방법

Base64 실패의 대부분은 다섯 가지 원인에서 옵니다. 잘못된 알파벳, 패딩 누락, 엉뚱한 문자, 잘못된 문자 인코딩, 두 번 인코딩된 데이터입니다. 아래에서 오류 메시지를 찾아 해결 방법으로 바로 가세요.

오류 메시지 한눈에 보기

메시지나타나는 곳가장 가능성 높은 원인
binascii.Error: Incorrect paddingPython base64.b64decode끝의 = 누락. Base64URL이거나 잘린 문자열인 경우가 많음
binascii.Error: Invalid base64-encoded string: number of data characters (5) cannot be 1 more than a multiple of 4Python잘린 입력, 또는 -/_가 조용히 버려짐
binascii.Error: Only base64 data is allowedvalidate=True를 쓴 Python입력의 줄 바꿈, 공백, -, _
InvalidCharacterError: Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.Chrome, EdgeBase64URL 문자, 쓰레기 문자, 잘못된 길이
InvalidCharacterError: String contains an invalid characterFirefox위와 같음
InvalidCharacterError: Invalid characterNode.js atob위와 같음
IllegalArgumentException (“Illegal base64 character …”)Java java.util.Base64알파벳에 맞지 않는 디코더, 또는 줄 바꿈
FormatException.NET Convert.FromBase64StringBase64URL 문자나 패딩 누락
base64: invalid inputGNU coreutils base64 -d알파벳 밖의 문자

디코더 오류 고치기

Python의 “Incorrect padding”

Base64 출력의 길이는 항상 4의 배수이며, 끝은 =로 채워집니다. Python의 디코더는 패딩을 요구합니다.

>>> import base64
>>> base64.b64decode("SGVsbG8")
Traceback (most recent call last):
  ...
binascii.Error: Incorrect padding
>>> base64.b64decode("SGVsbG8=")
b'Hello'

패딩 누락은 대개 문자열이 Base64URL이거나(JWT, URL 토큰, 많은 API가 =를 생략함) 무언가가 끝을 잘라 냈다는 뜻입니다. 패딩을 되돌려 놓으세요.

s = "SGVsbG8"
s += "=" * (-len(s) % 4)

길이가 4의 배수보다 1 많다면 패딩으로는 해결되지 않습니다. 이때 Python은 number of data characters (5) cannot be 1 more than a multiple of 4를 보고합니다. 남는 문자 하나는 6비트만 담으므로 데이터 일부가 빠진 것입니다. 문자열이 잘리지 않았는데도 이 메시지를 만날 수 있습니다. validate=True가 없으면 Python은 -와 _를 포함해 알파벳 밖의 문자를 버립니다. base64.b64decode("SGk_Pg==")는 _를 버려서 데이터 문자가 5개가 됩니다. Base64URL 입력에는 base64.urlsafe_b64decode를 사용하세요.

“Invalid character"와 atob 실패

브라우저들은 같은 알고리즘으로 atob를 구현하지만 오류 문구는 다릅니다. Chrome은 “The string to be decoded is not correctly encoded”, Firefox는 “String contains an invalid character”, Node.js는 “Invalid character"라고 합니다. 셋 모두 다음 입력에 InvalidCharacterError를 던집니다.

  • Base64URL 문자. atob("SGk_Pg")는 _ 때문에 실패합니다. 먼저 -를 +로, _를 /로 바꾸세요.
  • 공백을 제거한 뒤의 길이가 4n+1인 경우. 예: atob("A").
  • 속하지 않는 문자. JSON에서 함께 복사된 따옴표, data: 접두사, URL 인코딩에서 온 %2F 등입니다.

atob가 봐주는 것도 있습니다. 공백과 줄 바꿈을 건너뛰고, 길이가 맞으면 패딩 누락도 허용하므로 atob("SGVsbG8")는 "Hello"를 반환합니다. 반대 방향의 오류, 즉 btoa가 “characters outside of the Latin1 range"로 실패하는 것은 한글 같은 유니코드 텍스트 문제이며 Uint8Array.toBase64에서 다룹니다.

두 알파벳, 공백, 패딩 누락을 모두 처리하는 관대한 디코더:

function decodeLoose(s) {
  s = s.replace(/\s+/g, "").replace(/-/g, "+").replace(/_/g, "/");
  s += "=".repeat((4 - (s.length % 4)) % 4);
  return Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
}

new TextDecoder().decode(decodeLoose("SGk_Pg")); // "Hi?>"

Python에서는 다음과 같습니다.

import base64

def b64decode_any(s: str) -> bytes:
    s = "".join(s.split())
    s = s.replace("-", "+").replace("_", "/")
    s += "=" * (-len(s) % 4)
    return base64.b64decode(s, validate=True)

Base64 복구 도구는 같은 정리 작업을 하고 무엇을 바꿨는지 알려 줍니다.

Java의 “Illegal base64 character"와 .NET의 FormatException

Java의 java.util.Base64에는 디코더가 세 개 있으며, 각각 자기 알파벳에 엄격합니다. 메시지는 문제가 된 문자를 16진수로 알려 줍니다. 5f는 _, 2d는 -, 2f는 /, 20은 공백, a는 줄 바꿈입니다.

입력에 들어 있는 것사용할 디코더
+와 /Base64.getDecoder()
-와 _Base64.getUrlDecoder()
줄 바꿈 (MIME, PEM 본문)Base64.getMimeDecoder()

기본 디코더는 줄 바꿈과 공백을 거부합니다. 패딩 누락은 허용하므로 SGVsbG8은 아무 불평 없이 디코딩됩니다.

.NET의 Convert.FromBase64String은 Base64URL 문자와 패딩 누락에 FormatException을 던집니다. 공백과 줄 바꿈은 건너뜁니다. 호출하기 전에 위에서 보인 대로 알파벳과 패딩을 고치세요. 전체 예제는 Java에서 Base64 사용하기와 C#에서 Base64 사용하기에 있습니다.

텍스트가 깨져서 나올 때

디코딩은 성공했는데 텍스트가 이상하게 보이는 경우입니다. Base64는 바이트를 저장하며, 그 바이트는 어떤 문자 인코딩으로 텍스트에서 만들어진 것입니다. 다른 인코딩으로 읽으면 글자가 깨집니다.

보이는 것원래 텍스트일어난 일
CaféCaféUTF-8 바이트를 Latin-1이나 Windows-1252로 읽음
Caf�CaféWindows-1252 바이트를 UTF-8로 읽음
H e l l o 또는 H\0e\0l\0l\0o\0HelloUTF-16LE 바이트를 UTF-8로 읽음

한국어 텍스트도 마찬가지입니다. EUC-KR(CP949) 바이트를 UTF-8로 읽거나 그 반대로 읽으면 한글이 �나 엉뚱한 글자로 깨집니다.

첫 번째 행은 전형적인 atob 함정입니다. atob는 바이트 하나를 문자 하나에 대응시키므로 atob("Q2Fmw6k=")는 "Café"를 반환합니다. 대신 바이트를 UTF-8로 디코딩하세요.

const bytes = Uint8Array.from(atob("Q2Fmw6k="), (c) => c.charCodeAt(0));
new TextDecoder().decode(bytes); // "Café"

UTF-16LE는 Windows 도구에서 흔합니다. PowerShell의 -EncodedCommand와 많은 .NET API가 이 형식을 만듭니다. Base64 텍스트 변환에서는 출력이 제대로 읽힐 때까지 문자 인코딩을 바꿔 볼 수 있고, Base64 UTF-16 변환은 와이드 문자의 경우를 처리합니다.

출력이 바이너리처럼 보일 때

디코딩한 결과에 �, 제어 문자, 또는 PNG, PK, %PDF 조각이 가득하다면 페이로드는 파일입니다. Base64의 앞 글자를 보세요. iVBORw0KGgo는 PNG, JVBERi0는 PDF, H4sI는 gzip입니다. 전체 목록은 흔한 Base64 접두사에 있습니다. 형식을 감지하고 다운로드를 제공하는 Base64 파일 변환으로 데이터를 여세요. H4sI라면 gzip Base64 디코딩을 사용하세요.

디코딩 결과가 예상과 다른 바이트일 때

오류는 없는데 바이트가 틀린 경우입니다. 흔한 이유는 다음과 같습니다.

  • 인코딩할 때 붙은 끝 줄 바꿈. echo "hello" | base64는 aGVsbG8K(\n 포함)를, echo -n "hello" | base64는 aGVsbG8=를 줍니다. printf '%s'나 echo -n을 사용하세요.
  • +가 공백으로 바뀜. URL 쿼리 문자열과 폼 본문에서 +는 공백을 뜻합니다. URLSearchParams("t=PGI+Ym9sZDwvYj4=").get("t")는 PGI Ym9sZDwvYj4=를 반환합니다. 그러면 어떤 디코더는 예외를 던지고, 어떤 디코더는 공백을 버리고 쓰레기 값을 만듭니다. Base64를 URL에 넣기 전에 encodeURIComponent로 퍼센트 인코딩하거나 Base64URL을 사용하세요.
  • 남아 있는 퍼센트 인코딩. SGk%2FPg%3D%3D는 Base64 디코딩 전에 URL 디코딩해서 SGk/Pg==로 만들어야 합니다.
  • JSON에서 복사된 이스케이프. 원시 JSON에서 문자열을 복사하면 글자 그대로의 \n(두 문자)이 딸려 올 수 있습니다. Python의 관대한 디코더는 백슬래시를 버리고 n은 남겨서 오류 없이 잘못된 바이트를 반환합니다. b64decode("SGVs\\nbG8=")는 b'Hel\x9d\xb1\xbc'를 줍니다. 복사하지 말고 JSON을 파싱하세요. JSON Base64 디코더는 모든 필드에 대해 이를 처리합니다.
  • 공백에 대한 엄격함과 관대함의 차이. validate=True 없는 Python, atob, .NET, Java MIME 디코더는 줄 바꿈을 건너뜁니다. validate=True를 쓴 Python과 Java 기본 디코더는 이를 거부합니다. MIME과 PEM 데이터는 76자나 64자에서 줄 바꿈되므로 이를 받아들이는 디코더를 고르세요.

두 번 인코딩된 데이터

U0dWc2JHOGdkMjl5YkdRaA==를 디코딩하면 SGVsbG8gd29ybGQh가 나오는데, 이것도 올바른 Base64이며 Hello world!로 디코딩됩니다. 한 번 디코딩한 결과가 Base64 문자로만 이루어지고 길이가 4의 배수라면 이중 인코딩을 의심하세요.

Kubernetes 시크릿에서 자주 일어납니다. data: 아래의 값은 Base64이며, kubectl create secret --from-literal은 인코딩을 대신 해 줍니다. 값을 직접 먼저 인코딩하면 파드는 비밀번호 대신 Base64 텍스트를 받게 됩니다. 일반 값에는 stringData:를 쓰거나, 저장된 내용을 Kubernetes 시크릿 디코더로 확인하세요. 확신이 서지 않을 때는 Base64 검사기가 문자열이 올바른지, 몇 바이트를 담고 있는지 보여 줍니다.

자주 묻는 질문

binascii.Error: Incorrect padding 오류는 어떻게 없애나요?

길이가 4의 배수가 될 때까지 =를 덧붙이세요. s += "=" * (-len(s) % 4). 문자열이 Base64URL이라면 -를 +로, _를 /로 바꾸거나 base64.urlsafe_b64decode를 사용하세요.

JWT에서 atob가 실패하는 이유는 무엇인가요?

JWT는 -와 _가 있는 Base64URL을 씁니다. atob는 +와 /만 압니다. 먼저 문자를 변환하거나 토큰을 JWT 디코더에 붙여넣으세요.

디코딩한 텍스트에 é 대신 é가 보이는 이유는 무엇인가요?

바이트는 UTF-8인데 Latin-1이나 Windows-1252로 읽었기 때문입니다. JavaScript의 TextDecoder나 Python의 .decode("utf-8")처럼 바이트를 UTF-8로 디코딩하세요.

길이가 4n+1인 Base64 문자열을 복구할 수 있나요?

없습니다. 남는 문자 하나는 1바이트에 못 미치는 6비트만 담으므로, 적어도 한 문자가 빠진 것입니다. 원본 출처를 찾거나 문자열이 어디에서 잘렸는지 확인하세요.

데이터가 Base64로 두 번 인코딩되었는지 어떻게 알 수 있나요?

한 번 디코딩해 보세요. 결과가 다시 A-Z a-z 0-9 + / =로만 이루어지고 길이가 4의 배수라면 한 번 더 디코딩하세요.

계속 읽기