Работа с Base64 строками в Python

Тема кодировки является одной из самых сложных тем для понимания, особенно для новичков. В данной статье я постараюсь разобрать алгоритм работы Base64 и показать примеры использования. От вас требуется немного терпения и усидчивости. Приятного чтения!

Контекст проблемы

Вы когда-нибудь получали письмо по e-mail, содержащее PDF или изображение, при попытке открыть которое на экране выводились странные символы? Это могло произойти, если ваш e-mail сервер настроен на обработку только текстовых данных. Файлы с двоичными данными, байты (bytes) которых представляют НЕ-текстовую (non-text) информацию (например картинки), могут быть легко повреждены, если их передавать и обрабатывать в только текстовых (text-only) системах.

Типичный пример "битой" кодировки

Кодировка Base64 позволяет переводить (конвертировать) байты, содержащие двоичную или символьную (текстовую) информацию в ASCII-символы. Подробно с ASCII ознакомиться можно самостоятельно, но идея там простая: каждому литералу ставится в соответствие целое число. Т.к. диапазон значений ограничен латинскими символами + спец символы/управляющие (перевод каретки, табуляция и тд), то все можно ограничить 8 битами == 1 байтом. Т.е. ASCII - такая же кодировка, что и Base64.

P.S. Точнее, ограничить можно и 7 битами, просто добавляют 0 в старший разряд, чтобы добить до 1 байта.

Ниже мы посмотрим как работает кодирование/декодирование в Base64. Будем использовать стандартную библиотеку Python, так что проблем с повторением кода у вас возникнуть не должно. Погнали!

Что такое кодирование Base64

Кодирование / шифрование - процесс, когда в соответствие исходному литералу ставится новое уникальное значение. Обычно это число, причем в любой системе исчисления (СИ).

Кодирование в Base64 - перевод байт в ASCII-символы по определенному правилу. В информатике, основание СИ показывает, как много различных (уникальных) символов могут быть представлены числами. Как видно из имени кодировки - таких значений 64.

Картинка - такой же набор байтов, которые можно закодировать во что угодно!

Кодировка Base64 содержит:

  • 26 ЗАГЛАВНЫХ символа
  • 26 строчных символа
  • 10 цифр
  • "+" и "/" для новых строк (реализации могут отличаться)

ASCII-символы в Python можно посмотреть

$ python -V Python 3.9.7 >>> from string import ascii_lowercase >>> len(ascii_lowercase) 26 >>> ascii_lowercase 'abcdefghijklmnopqrstuvwxyz' >>> from string import ascii_uppercase

Каждый ASCII-символ может быть придставлен целым числом

>>> ord('A') 65

Base64 не является алгоритмом шифрования и не должен использоваться в security целях.

Как это работает

Шаги кодирования:

  1. Получаем ASCII значение для каждого символа в кодируемой строке.
  2. Вычисляем 8-битный двоичный эквивалент для этих значений.
  3. Переводим 8-битные куски (chunks) в 6-битные просто перегруппировывая цифры.
  4. Переводим 6-битные двоичные группы в десятеричную форму.
  5. Используя Base64 таблицу кодировки, ставим в соответствие каждому числу соотв символ.

Посмотрим, как это выглядит в коде.

Step #1

>>> ord('P') 80 >>> bin(80) '0b1010000'

Step #2

>>> dec_chars_eq = [ord(ch) for ch in 'Python'] [80, 121, 116, 104, 111, 110] >>> bin_dec_range = [bin(x) for x in dec_chars_eq] ['0b1010000', '0b1111001', '0b1110100', '0b1101000', '0b1101111', '0b1101110']

Step #3

010100 000111 100101 110100 011010 000110 111101 101110

Step #4

Полученные значения опять добиваем до 8 бит (в старшие разряды добавляем 0). Проверить можно так

>>> assert int(0b010100) == int(0b00010100)

После того, как привели значения к 1 байту, обратно возвращаем в десятичную форму записи

0b010100 == 0b00010100 >>> int(0b00010100) 20 >>> from_6bit_to_8bit = [int(x) for x in (0b010100, 0b000111, 0b100101, 0b110100, 0b011010, 0b000110, 0b111101, 0b101110)] [20, 7, 37, 52, 26, 6, 61, 46]

Step #5

Исходя из таблицы, числу 20 соответствует символ "U", 7"H" и тд

В результате слово "Python" в кодировке Base64 преобразуется в "UHl0aG9u"

Для чего используется Base64 кодирование?

В компьютерах все данные различных типов представлены 0 и 1. Кроме того, некоторые каналы коммуникаций и приложений не в состоянии понять все принимаемые биты. Это потому, что последовательность 0 и 1 зависит от типа информации, которую они отражают. К примеру, 10110001 должно быть обработано по-разному, в зависимости от того, представляет ли это письмо или картинку.

Чтобы обойти это ограничение, мы можем зашифровать данные в текст, улучшив т.о. возможность корректно передать и обработать данные.

Base64 - популярный метод конвертации двоичных данных в ASCII символы, широко применяемый в большинстве сетевых протоколов и приложений.

В реальном коде, места где применяется Base64:

  • почтовые серверы
  • html-теги
  • файлы конфигураций
  • хранение ssh-ключей, подписи запросов
Кусок HTML <img src="data:image/png;base64,aVRBOw0ZKg1mL9..."> Кусок settings.toml [secrets] SSH_PUBLIC_KEY = "LS0tLS1CRUdJTiZOAUlsQVUJMSUMgS0VZ..."

Выяснив, что иногда данные необходимо отправлять в виде текста, чтобы они не были повреждены, посмотрим как в Python обстоят дела с Base64.

Кодирование строк в Python

В Python3 доступен модуль base64 данные, который содержит необходимый функционал.

from base64 import b64encode message = "Hello world" message_bytes = message.encode('ascii') base64_bytes = b64encode(message_bytes) base64_message = base64_bytes.decode('ascii') print(base64_message) Output: SGVsbG8gd29ybGQ=

⚠ Важно! При обратном декодировании необходимо использовать такую же кодировку, с помощью которой исходная строка была закодирована.

Декодирование строк

Декодирование строки Base64 по сути является обратным процессом кодирования. Мы декодируем строку Base64 в байты некодированных данных. Затем мы преобразуем байтоподобный объект в строку.

from base64 import b64decode base64_message = 'SGVsbG8gd29ybGQ=' base64_bytes = base64_message.encode('ascii') message_bytes = b64decode(base64_bytes) message = message_bytes.decode('ascii') print(message) Output: Hello world

Кодирование двоичных данных

Теперь попробуем представить бинарный файл (картинку) в виде Base64 строки.

import base64 with open('picture.jpeg', 'rb') as binary_file: binary_file_data = binary_file.read() base64_encoded_data = base64.b64encode(binary_file_data) base64_message = base64_encoded_data.decode('utf-8') print(base64_message)

Output:

/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExc...

⚠ Важно! Открывать файл необходимо именно в режиме binary - 'rb'

Восстановление исходного двоичного файла

import base64 base64_img = """ /9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAUDBAQEAwUEBAQFBQUGBwwIBwcHBw8LCwkMEQ8SEhEPERETFhwXExQaFRERGCEYGh0dHx8fExciJCIeJBweHx7 /2wBDAQUFBQcGBw4ICA4eFBEUHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh4eHh7/wAARCAFeAV4DASIAAhEBAxEB... """ base64_img_bytes = base64_img.encode('utf-8') with open('decoded_image.jpeg', 'wb') as file_to_save: decoded_image_data = base64.decodebytes(base64_img_bytes) file_to_save.write(decoded_image_data)

Заключение

Кодировка Base64 - это популярный метод преобразования данных в различных двоичных форматах в строку символов ASCII. Это полезно при передаче данных через сеть или приложение, которые не могут обрабатывать необработанные двоичные данные, но легко обрабатывают текст.

С помощью Python мы можем использовать модуль base64 для кодирования и декодирования текстовых и двоичных данных Base64.

🖤 Подписывайтесь на мою телегу. Больше кода 🐍 - меньше багов 🪲!

0
1 комментарий
Alexei Mironov

Метод base64.decodebytes принимает байты, а у вас строка и код сломается

Ответить
Развернуть ветку
-2 комментариев
Раскрывать всегда