Python: игнорировать ошибку "Ошибка неправильного заполнения" при декодировании base64
У меня есть некоторые данные, кодированные base64, которые я хочу преобразовать обратно в двоичные, даже если в нем есть ошибка заполнения. Если я использую
base64.decodestring(b64_string)
он вызывает ошибку "Неправильное заполнение". Есть ли другой способ?
ОБНОВЛЕНИЕ: Спасибо за отзывы. Честно говоря, все упомянутые методы звучали немного хитом
и пропустил, поэтому решил попробовать openssl. Следующая команда сработала:
openssl enc -d -base64 -in b64string -out binary_data
Ответы
Ответ 1
Как сказано в других ответах, существуют различные способы повреждения данных base64.
Однако, как гласит Википедия, удаление отступов (символов '=' в конце данных, закодированных в base64) "без потерь":
С теоретической точки зрения символ заполнения не требуется, поскольку количество пропущенных байтов можно рассчитать из числа цифр Base64.
Так что, если это действительно единственное, что "не так" с вашими данными base64, отступ можно просто добавить обратно. Я придумал это, чтобы иметь возможность анализировать URL-адреса "данных" в WeasyPrint, некоторые из которых были base64 без заполнения:
import base64
import re
def decode_base64(data, altchars=b'+/'):
"""Decode base64, padding being optional.
:param data: Base64 data as an ASCII byte string
:returns: The decoded byte string.
"""
data = re.sub(rb'[^a-zA-Z0-9%s]+' % altchars, b'', data) # normalize
missing_padding = len(data) % 4
if missing_padding:
data += b'='* (4 - missing_padding)
return base64.b64decode(data, altchars)
Тесты для этой функции: weasyprint/tests/test_css.py # L68
Ответ 2
Просто добавьте дополнение по мере необходимости. Однако прислушайтесь к Майклу.
b64_string += "=" * ((4 - len(b64_string) % 4) % 4) #ugh
Ответ 3
Если есть ошибка заполнения, это означает, что ваша строка повреждена; Строки, закодированные base64, должны иметь кратность в четыре длины. Вы можете попробовать добавить символ заполнения (=
) самостоятельно, чтобы строка была кратной четыре, но она уже должна иметь это, если что-то не так.
Ответ 4
"Неправильное заполнение" может означать не только "отсутствие прокладки", но также (верьте или нет) "неправильное заполнение".
Если предлагаемые методы "добавления дополнений" не работают, попробуйте удалить несколько завершающих байтов:
lens = len(strg)
lenx = lens - (lens % 4 if lens % 4 else 4)
try:
result = base64.decodestring(strg[:lenx])
except etc
Обновление. Любые попытки добавить дополнение или удаление возможных байт с конца должны быть выполнены ПОСЛЕ удаления любых пробелов, иначе вычисления длины будут расстроены.
Было бы неплохо, если бы вы показали нам (короткую) выборку данных, которые нужно восстановить. Измените свой вопрос и скопируйте/вставьте результат print repr(sample)
.
Обновление 2: возможно, что кодировка была выполнена с использованием URL-адреса. Если это так, вы сможете увидеть минус и символы подчеркивания в своих данных, и вы должны иметь возможность декодировать его с помощью base64.b64decode(strg, '-_')
Если вы не можете видеть символы минус и подчеркивания в своих данных, но можете видеть символы плюса и косой черты, тогда у вас есть еще одна проблема, и вам могут понадобиться дополнительные дополнения или удаленные трюки.
Если вы не видите ни одного минуса, подчеркивания, плюс и косой черты в своих данных, вам нужно определить два альтернативных символа; они будут теми, которых нет в [A-Za-z0-9]. Затем вам нужно будет поэкспериментировать, чтобы определить, какой порядок им нужно использовать во втором аргументе base64.b64decode()
Обновление 3. Если ваши данные являются "конфиденциальными в компании":
(а) вы должны сказать так:
(б) мы можем исследовать другие способы понимания проблемы, которая, скорее всего, будет связана с тем, какие символы используются вместо +
и /
в алфавите кодирования, или другими форматированием или посторонними символами.
Одним из таких путей было бы изучение того, что нестандартные символы в ваших данных, например.
from collections import defaultdict
d = defaultdict(int)
import string
s = set(string.ascii_letters + string.digits)
for c in your_data:
if c not in s:
d[c] += 1
print d
Ответ 5
Используйте
string += '=' * (-len(string) % 4) # restore stripped '='s
Кредит идет к комментарию где-то здесь.
>>> import base64
>>> enc = base64.b64encode('1')
>>> enc
>>> 'MQ=='
>>> base64.b64decode(enc)
>>> '1'
>>> enc = enc.rstrip('=')
>>> enc
>>> 'MQ'
>>> base64.b64decode(enc)
...
TypeError: Incorrect padding
>>> base64.b64decode(enc + '=' * (-len(enc) % 4))
>>> '1'
>>>
Ответ 6
У меня нет представителя, чтобы комментировать, но приятно отметить, что (по крайней мере, в Python 3.x) base64.b64decode будет обрезать любые дополнительные отступы, если их будет достаточно в первую очередь.
Итак, что-то вроде: b'abc='
работает так же, как b'abc=='
.
Это означает, что вы можете просто добавить максимальное количество дополняющих символов, которое вам когда-либо понадобится - это три (b'==='
) - и base64 обрежет любые ненужные.
В принципе:
base64.b64decode(s + b'===')
чище чем
base64.b64decode(s + b'=' * (-len(s) % 4))
Ответ 7
Проверьте документацию источника данных, который вы пытаетесь декодировать. Возможно ли, что вы намеревались использовать base64.urlsafe_b64decode(s)
вместо base64.b64decode(s)
? Это одна из причин, почему вы могли видеть это сообщение об ошибке.
Декодируйте строку s, используя URL-безопасный алфавит, который заменяет - вместо + и _ вместо/в стандартном алфавите Base64.
Например, это относится к различным API Google, таким как Google Identity Toolkit и полезные данные Gmail.
Ответ 8
Добавление отступов довольно... нерешительно. Здесь функция, которую я написал с помощью комментариев в этом потоке, а также страницу wiki для base64 (это удивительно полезно) https://en.wikipedia.org/wiki/Base64#Padding.
import logging
import base64
def base64_decode(s):
"""Add missing padding to string and return the decoded base64 string."""
log = logging.getLogger()
s = str(s).strip()
try:
return base64.b64decode(s)
except TypeError:
padding = len(s) % 4
if padding == 1:
log.error("Invalid base64 string: {}".format(s))
return ''
elif padding == 2:
s += b'=='
elif padding == 3:
s += b'='
return base64.b64decode(s)
Ответ 9
Просто добавьте дополнительные символы, такие как "=" или любой другой, и сделайте его кратным 4, прежде чем пытаться декодировать целевое строковое значение. Что-то вроде:
if len(value) % 4 != 0: #check if multiple of 4
while len(value) % 4 != 0:
value = value + "="
req_str = base64.b64decode(value)
else:
req_str = base64.b64decode(value)
Ответ 10
В случае, если эта ошибка произошла с веб-сервера: попробуйте url-кодировку вашего значения сообщения. Я выполнял POSTing через "curl" и обнаружил, что мое значение base64 не кодирует url-код, поэтому такие символы, как "+", не экранируются, поэтому логика url-decode веб-сервера автоматически запускает url-decode и преобразует + в пробелы.
"+" является допустимым символом base64 и, возможно, единственным символом, который искажается неожиданным декодированием URL-адреса.
Ответ 11
В моем случае я столкнулся с этой ошибкой при разборе электронной почты. Я получил вложение в виде строки base64 и извлек его через re.search. В конце концов появилась странная дополнительная подстрока в конце.
dHJhaWxlcgo8PCAvU2l6ZSAxNSAvUm9vdCAxIDAgUiAvSW5mbyAyIDAgUgovSUQgWyhcMDAyXDMz
MHtPcFwyNTZbezU/VzheXDM0MXFcMzExKShcMDAyXDMzMHtPcFwyNTZbezU/VzheXDM0MXFcMzEx
KV0KPj4Kc3RhcnR4cmVmCjY3MDEKJSVFT0YK
--_=ic0008m4wtZ4TqBFd+sXC8--
Когда я удалил --_=ic0008m4wtZ4TqBFd+sXC8--
и --_=ic0008m4wtZ4TqBFd+sXC8--
строку, анализ был исправлен.
Поэтому я советую убедиться, что вы декодируете правильную строку base64.
Ответ 12
Вы должны использовать
base64.b64decode(b64_string, ' /')
По умолчанию, altchars - '+/'
.
Ответ 13
Существует два способа исправления входных данных, описанных здесь, или, более конкретно и в соответствии с OP, чтобы метод Python-модуля base64 b64decode мог обрабатывать входные данные во что-то, не вызывая незамеченное исключение:
- Добавьте == в конец входных данных и вызовите base64.b64decode (...)
-
Если это вызывает исключение, то
я. Поймай это через попробуйте/кроме,
II. (R?) Убрать любые = символы из входных данных (NB это может не понадобиться),
III. Добавьте A == к входным данным (A == - P == будет работать),
внутривенно Вызовите base64.b64decode (...) с этими входными данными A == -appended
Результат из пункта 1 или пункта 2 выше даст желаемый результат.
Предостережения
Это не гарантирует, что декодированный результат будет тем, что был первоначально закодирован, но это (иногда?) Даст OP достаточно для работы:
Даже с повреждением я хочу вернуться к двоичному файлу, потому что я все еще могу получить некоторую полезную информацию из потока ASN.1 ").
Посмотрите, что мы знаем и предположения ниже.
TL; DR
Из некоторых быстрых тестов base64.b64decode (...)
-
похоже, что он игнорирует символы non- [A-Za-z0-9 +/]; это включает игнорирование = s, если только они не являются последним символом (ами) в разобранной группе из четырех, и в этом случае = s прекращает декодирование (a = b = c = d = дает тот же результат, что и abc =, и a = = b == c == дает тот же результат, что и ab ==).
-
Также кажется, что все добавленные символы игнорируются после точки, где base64.b64decode (...) прекращает декодирование, например, из = 4 как четвертого в группе.
Как отмечалось в нескольких комментариях выше, в конце входных данных требуется либо ноль, либо один, либо два = s заполнения, когда значение [количество проанализированных символов до этой точки по модулю 4] равно 0 или 3, или 2 соответственно. Таким образом, из пунктов 3. и 4. выше добавление двух или более = s к входным данным исправит любые проблемы [Неправильное заполнение] в этих случаях.
ОДНАКО, декодирование не может обрабатывать случай, когда [общее количество проанализированных символов по модулю 4] равно 1, потому что для представления первого декодированного байта в группе из трех декодированных байтов требуется не менее двух кодированных символов. В не поврежденных закодированных входных данных этот случай [N modulo 4] = 1 никогда не случается, но, поскольку OP заявил, что символы могут отсутствовать, это может произойти здесь. Вот почему просто добавление = s не всегда будет работать, и поэтому добавление A == будет работать, когда добавление == не работает. NB Использование [A] практически произвольно: оно добавляет только очищенные (нулевые) биты к декодированному, что может или не может быть правильным, но тогда объект здесь не является корректностью, а завершается с помощью base64.b64decode (...) без исключений,
Что мы знаем из ФП и особенно последующих комментариев
- Предполагается, что во входных данных в кодировке Base64 отсутствуют данные (символы)
- В кодировке Base64 используются стандартные 64 места-значения плюс отступы: AZ; аз; 0-9; +; /; = заполнение. Это подтверждается или, по крайней мере, подтверждается тем фактом, что
openssl enc...
работает.
Предположения
- Входные данные содержат только 7-битные данные ASCII
- Единственный вид искажения - отсутствие кодированных входных данных.
- OP не заботится о декодированных выходных данных в любой точке после той, которая соответствует любым пропущенным кодированным входным данным
Github
Вот обертка для реализации этого решения:
https://github.com/drbitboy/missing_b64
Ответ 14
Эта ошибка также может быть вызвана наличием символа новой строки в конце строки, которую вы пытаетесь декодировать. Это может произойти, если вы читаете из файла и получаете новые строки непреднамеренно. Например.
$ cat decode_strings.txt
GA4TSNRSGE======
Выше приведен пример строки из файла, поэтому Python:
file = open('decode_strings.txt', 'r')
for decode_str in file:
print decode_str
# try to decode it
id = base64.b32decode(decode_str)
print id
Выведет что-то вроде:
raise TypeError('Incorrect padding')
TypeError: Incorrect padding
Если вы обновите код следующим образом, вы можете увидеть новую строку:
for decode_str in file:
print decode_str
print "Look at that newline above me :( "
try:
id = base64.b32decode(decode_str)
except:
pass
Это должно выводить:
GA4TSNRSGE======
Look at that newline above me :(
Чтобы устранить проблему, просто используйте .rstrip() в своей строке:
for decode_str in file:
decode_str = decode_str.rstrip()
id = base64.b32decode(decode_str)
print decode_str, id