Сообщения об ошибках коротко
| Сообщение | Где встречается | Наиболее вероятная причина |
|---|---|---|
binascii.Error: Incorrect padding | Python base64.b64decode | Нет = в конце, часто из-за Base64URL или обрезанной строки |
binascii.Error: Invalid base64-encoded string: number of data characters (5) cannot be 1 more than a multiple of 4 | Python | Обрезанная строка или молча отброшенные -/_ |
binascii.Error: Only base64 data is allowed | Python с validate=True | Перевод строки, пробел, - или _ во входных данных |
InvalidCharacterError: Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded. | Chrome, Edge | Символы Base64URL, мусорные символы, неверная длина |
InvalidCharacterError: String contains an invalid character | Firefox | То же самое |
InvalidCharacterError: Invalid character | Node.js atob | То же самое |
IllegalArgumentException («Illegal base64 character …») | Java java.util.Base64 | Декодер не под тот алфавит или переносы строк |
FormatException | .NET Convert.FromBase64String | Символы Base64URL или нет дополнения |
base64: invalid input | GNU coreutils base64 -d | Символы вне алфавита |
Исправляем ошибки декодеров
«Incorrect padding» в Python
Длина вывода 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, дополнение не поможет. Тогда 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:,%2Fот URL-кодирования.
Кое-что atob всё же прощает. Он пропускает пробелы и переводы строк и принимает строку без дополнения, если длина позволяет, так что atob("SGVsbG8") возвращает "Hello". Зеркальная ошибка, когда btoa падает с «characters outside of the Latin1 range», связана с текстом в Unicode и разобрана в статье 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 выполняет ту же очистку и сообщает, что именно изменил.
«Illegal base64 character» в Java и FormatException в .NET
В java.util.Base64 три декодера, и каждый строго держится своего алфавита. Сообщение называет проблемный символ в hex: 5f (_), 2d (-), 2f (/), 20 (пробел), a (перевод строки).
| Во входных данных есть | Используйте |
|---|---|
+ и / | Base64.getDecoder() |
- и _ | Base64.getUrlDecoder() |
| Переносы строк (MIME, тела PEM) | Base64.getMimeDecoder() |
Базовый декодер отвергает переносы строк и пробелы. Отсутствие дополнения он при этом допускает, так что SGVsbG8 декодируется без возражений.
Convert.FromBase64String в .NET бросает FormatException на символах Base64URL и при отсутствии дополнения. Пробелы и переносы строк он пропускает. Перед вызовом исправьте алфавит и дополнение, как показано выше. Полные примеры есть на страницах Base64 в Java и Base64 в C#.
Текст получается искажённым
Декодирование прошло успешно, но текст выглядит неправильно. 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\0 | Hello | Байты UTF-16LE прочитаны как UTF-8 |
Первая строка таблицы описывает классическую ловушку atob. atob("Q2Fmw6k=") возвращает "Café", потому что atob сопоставляет каждому байту один символ. Вместо этого декодируйте байты как UTF-8:
const bytes = Uint8Array.from(atob("Q2Fmw6k="), (c) => c.charCodeAt(0));
new TextDecoder().decode(bytes); // "Café"
UTF-16LE часто встречается у инструментов Windows: его выдают -EncodedCommand в PowerShell и многие API .NET. В инструменте Base64 в текст можно переключать кодировку, пока текст не станет читаемым, а Base64 в UTF-16 справляется со случаем «широких» символов.
Результат похож на двоичные данные
Если декодированный результат полон �, управляющих символов или фрагментов PNG, PK и %PDF, внутри лежит файл. Посмотрите на первые символы Base64: PNG начинается с iVBORw0KGgo, PDF с JVBERi0, gzip с H4sI. Полный список есть в статье частые префиксы 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нужно сначала URL-декодировать вSGk/Pg==, а уже потом декодировать Base64. - Экранирование, скопированное из JSON. Если скопировать строку из сырого JSON, в неё может попасть буквальный
\n(два символа). Нестрогий декодер Python отбрасывает обратную косую черту, оставляетnи без всякой ошибки возвращает неправильные байты:b64decode("SGVs\\nbG8=")даётb'Hel\x9d\xb1\xbc'. Не копируйте из JSON, а разбирайте его. Декодер Base64 в JSON делает это для каждого поля. - Строгое и нестрогое отношение к пробелам. Python без
validate=True,atob, .NET и MIME-декодер Java пропускают переносы строк. Python сvalidate=Trueи базовый декодер Java их отвергают. Данные MIME и PEM перенесены по 76 или 64 символа, так что выбирайте декодер, который это принимает.
Данные, закодированные дважды
Декодирование U0dWc2JHOGdkMjl5YkdRaA== даёт SGVsbG8gd29ybGQh, а это снова корректный Base64, который декодируется в Hello world!. Если результат декодирования состоит только из символов Base64 и его длина кратна 4, подозревайте двойное кодирование.
Часто такое случается с Secrets в 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.
Почему atob падает на JWT?
JWT используют Base64URL, где есть - и _. atob знает только + и /. Сначала замените символы или вставьте токен в декодер JWT.
Почему в декодированном тексте é вместо é?
Байты в UTF-8, но их прочитали как Latin-1 или Windows-1252. Декодируйте байты как UTF-8, например через TextDecoder в JavaScript или .decode("utf-8") в Python.
Можно ли починить строку Base64 длиной 4n+1?
Нет. Один лишний символ несёт всего 6 бит, меньше байта, значит, как минимум один символ потерян. Найдите исходный источник или проверьте, где строку обрезали.
Как понять, что данные закодированы в Base64 дважды?
Декодируйте один раз. Если результат снова состоит только из A-Z a-z 0-9 + / = и его длина кратна 4, декодируйте ещё раз.