Перейти к содержимому
New

DeepSeek V4.1 Flash free for 7 days: 10M tokens a day after a top-up of $10 or more. Until Nov 30.

How it works

Шифрование, кодирование и хеширование: что делает каждое

Разделяем кодирование, шифрование и хеширование на лабораторных с Base64, SHA-256 и OpenSSL. Почему Base64 ничего не защищает, а хеши необратимы.

Начальный8 мин чтенияАвтор: NotHoly

Кодирование, шифрование и хеширование превращают данные во что-то другое, поэтому их и путают. Цели у них разные. Кодирование меняет способ записи данных, и обратить его может любой. Шифрование скрывает данные от тех, у кого нет ключа, а с этим ключом его можно обратить. Хеширование даёт короткий отпечаток, из которого нельзя получить исходные данные. Запомните одну фразу: Base64 не шифрование, а хеш ничего не скрывает при коротких входных данных.

В статье эти три идеи разводятся по отдельности, а затем вы запускаете каждую в терминале на одном и том же тексте. Результаты вы сравните сами.

С чего начать

Нужен терминал с base64, sha256sum и openssl. В Windows 11 команды ниже работают в Git Bash, который поставляется с Git for Windows. Для шага с хешированием есть версия для PowerShell. Предварительные знания криптографии не нужны, но сначала стоит прочитать статью о шифре Цезаря: она помогает понять идею ключа.

Все команды лабораторной безопасно запускать на своей машине. Используйте тестовый файл, настоящие секреты здесь не нужны.

Три разные задачи

Три операции отвечают на три разных вопроса.

Операция На какой вопрос отвечает Нужен ключ? Можно обратить?
Кодирование Как представить эти байты в виде текста или в формате, который ожидает другая система? Нет Да, любой
Шифрование Кто может прочитать эти данные? Да Да, владельцы нужного ключа
Хеширование Изменились ли эти данные, совпадают ли два значения? Нет Нет, так задумано

Сверяйтесь с этой таблицей во время лабораторной. Остальная часть статьи объясняет каждую строку.

Кодирование меняет представление

Кодирование переписывает данные в другой форме, чтобы их могла передать система. Классический пример — Base64. Он берёт байты и записывает их 64 печатными символами: от A до Z, от a до z, от 0 до 9, + и /, а для дополнения используется =. Формат определён в RFC 4648. Его используют вложения в письмах, data URL и многие payload API, потому что транспорт ожидает текст.

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

Шифрование прячет данные за ключом

Шифрование берёт читаемые данные, открытый текст, и с помощью алгоритма и ключа превращает их в шифртекст. Тот, у кого нет ключа, не должен узнать из шифртекста ничего полезного. Тот, у кого ключ есть, может его расшифровать.

Есть два семейства. Симметричное шифрование использует один секретный ключ для шифрования и расшифрования. Обычный выбор здесь — AES. Асимметричное шифрование использует открытый и закрытый ключи. В этой статье используется симметричное шифрование, как и в команде OpenSSL из лабораторной.

Хорошее шифрование зависит не только от алгоритма. Нужно выбрать безопасный режим, генерировать ключи хорошим источником случайности и защищать ключ. Команда OpenSSL в лабораторной получает ключ из пароля через PBKDF2. В справке OpenSSL сказано, что PBKDF2 по умолчанию использует 10,000 итераций, если не изменить это через -iter.

Хеширование создаёт отпечаток

Криптографическая хеш-функция принимает вход любой длины и возвращает вывод фиксированной длины. SHA-256 описан в FIPS 180-4, его вывод всегда равен 256 битам и записывается 64 шестнадцатеричными символами. Для начинающих важны два свойства:

  • Один и тот же вход всегда даёт один и тот же вывод.
  • Небольшое изменение входа должно менять вывод настолько, чтобы он казался никак не связанным с прежним. Хорошие хеш-функции спроектированы так, что найти два входа с одинаковым выводом практически невозможно.

Хеширование однонаправленно. По выводу нельзя узнать исходный текст. Можно проверить, даёт ли кандидат на вход известный хеш. Восстановить вход из одного хеша нельзя, разве что перебором догадок.

Хеш работает как отпечаток, замком он не служит. SHA-256 публичного файла может вычислить кто угодно. Хеш доказывает совпадение двух копий. Файл он не скрывает.

Почему это важно

Большинство реальных ошибок безопасности в этой области происходит от неподходящего инструмента.

  • Разработчик хранит API-ключ в Base64 и называет его «зашифрованным». Любой, кто увидит значение, декодирует его одной командой.
  • Команда проверяет загрузку по хешу, но хеш опубликован на той же странице, что и файл, и отдаётся тем же скомпрометированным сервером. Проверка доказывает только то, что файл соответствует странице.
  • Веб-приложение хранит пароли в обычном SHA-256. Украденную базу можно быстро атаковать перебором, потому что SHA-256 задуман быстрым. В руководстве OWASP по криптографическому хранению сказано, что пароли следует защищать безопасными алгоритмами хеширования паролей, обратимое шифрование для этого не подходит, а подробности вынесены в Password Storage Cheat Sheet. Обычный выбор — медленные хеши паролей с солью, такие как Argon2id, scrypt, bcrypt или PBKDF2. Об Argon2id рассказывает следующая статья этой серии.

Если знать разницу с самого начала, в эти ловушки вы не попадёте.

Лабораторная 1: кодирование в Base64

Откройте Git Bash в пустой папке. Закодируйте короткую фразу, затем декодируйте результат.

bash
echo -n 'hello lab' | base64

Вывод на этой машине с OpenSSL 3.2.4 и Git Bash:

text
aGVsbG8gbGFi

Теперь декодируйте этот вывод:

bash
echo -n 'aGVsbG8gbGFi' | base64 -d

Вывод:

text
hello lab

Обратите внимание на две вещи. Во-первых, ключ не понадобился. Во-вторых, echo -n важен. Без -n команда echo добавляет перевод строки, и вывод Base64 меняется. Выполните echo 'hello lab' | base64 и сравните: у меня получилось aGVsbG8gbGFiCg==. Здесь Base64 превращает 9 байт в 12 символов, поэтому вывод длиннее входа.

Спросите себя: мог бы это обратить кто-то без всякого секрета? Да. Этот тест и отделяет кодирование от шифрования.

Лабораторная 2: хеширование SHA-256

Пропустите ту же фразу через SHA-256:

bash
echo -n 'hello lab' | sha256sum

Вывод:

text
8fc6baba79a0d60b37dee612ccc1ec096ef52dba8cb0a24a6451e8bfb0e4d761 *-

Теперь добавьте один восклицательный знак и запустите снова:

bash
echo -n 'hello lab!' | sha256sum

Вывод:

text
36902fb7fdea33458ad09dfeb52118dc47f99a8ca2b9001080366d55511de915 *-

У двух хешей нет видимого общего рисунка. Один символ изменил весь вывод. Оба состоят из 64 шестнадцатеричных символов, это 256 бит.

- в конце означает, что вход пришёл со стандартного ввода, а не из файла. * перед ним — маркер двоичного режима, который печатает sha256sum в Git Bash. Другие системы могут печатать вместо него два пробела, хеш от этого не меняется. Хеш файла работает так же.

Хеш файла и обнаружение изменений

Создайте небольшой файл и вычислите его хеш:

bash
echo 'secret note' > note.txt
sha256sum note.txt

На моей машине, с окончаниями строк Unix в файле, вывод был таким:

text
b5f891a5e064fec661025d0b0ec2de157487a6c2e5cc46dc0971fee56f86c645 *note.txt

Измените файл, хотя бы один символ, и вычислите хеш снова. Значение будет другим. Так работают проверки целостности. Проверка говорит, что файл изменился. Она не говорит, что именно изменилось, и не мешает атакующему изменить и файл, и опубликованный хеш.

Вариант для Windows PowerShell

Если вы предпочитаете PowerShell, в Windows есть встроенная команда:

powershell
Get-FileHash note.txt -Algorithm SHA256

В выводе проверьте строку Hash, в ней 64 шестнадцатеричных символа. Хеш зависит от точных байтов файла. Set-Content в PowerShell записывает окончания строк Windows (CRLF), которые отличаются от окончания LF в примере для Bash выше. На моей машине файл, записанный таким способом, дал такой хеш:

text
406A5646AE75CA75D032F306ABD33085DC9F656E3109BB962AEAB335EEC9ED85

Те же байты, захешированные sha256sum в Git Bash, дали то же значение в нижнем регистре. Если ваш хеш отличается от примера для Bash, но совпадает с результатом PowerShell для вашего файла, причина в окончаниях строк.

Лабораторная 3: шифрование файла через OpenSSL

Создайте заметку и зашифруйте её алгоритмом AES-256-CBC. OpenSSL запросит пароль. Выберите такой, который запомните и будете использовать только для этой лабораторной.

bash
echo 'secret note' > note.txt
openssl enc -aes-256-cbc -pbkdf2 -salt -in note.txt -out note.enc

OpenSSL запрашивает пароль, и зашифрованный файл записывается в note.enc. Параметр -salt добавляет случайную соль, поэтому при двойном шифровании одного файла вывод получается разным. Зашифрованный с -salt файл начинается с текста ASCII Salted__. Я проверил это на своей машине. Остальная часть файла двоичная и нечитаемая.

Теперь расшифруйте его:

bash
openssl enc -d -aes-256-cbc -pbkdf2 -in note.enc

Введите тот же пароль. Вывод:

text
secret note

Расшифровка работает только с правильным паролем. Я проверил с неправильным: OpenSSL напечатал bad decrypt и завершился с ошибкой.

Шифрование само по себе не обнаруживает подмену

Теперь измените один байт зашифрованного файла и попробуйте его расшифровать. Я инвертировал один бит внутри блока шифртекста в копии с именем tampered.enc. Расшифровка завершилась ошибкой bad decrypt, а команда вернула статус 1.

Такой сбой — счастливая случайность, связанная с дополнением в CBC. Другие изменения могут дать мусор на выходе без всякой ошибки. Обычное шифрование не даёт надёжного сигнала о том, что шифртекст изменён. Системы, которым нужны и секретность, и целостность, используют аутентифицированное шифрование, например AES-GCM, или добавляют код аутентификации сообщения. Это продвинутая тема, но она объясняет, почему зашифрованному файлу нельзя доверять автоматически.

Все три операции рядом

Пропустите один и тот же текст через каждую операцию и сравните:

Операция Команда Вывод для hello lab (полученный)
Кодирование echo -n 'hello lab' | base64 aGVsbG8gbGFi
Хеширование echo -n 'hello lab' | sha256sum 8fc6baba...e4d761
Шифрование openssl enc ... -in note.txt Двоичные данные, для чтения нужен пароль

Спросите, что даёт каждый вывод:

  • Из Base64 любой восстановит hello lab.
  • По хешу можно проверить, что кандидат на вход равен hello lab, но сам вход из одного хеша не восстановить.
  • Из зашифрованного файла secret note можно восстановить только с паролем.

Частые ошибки

Называть Base64 «шифрованием» или «обфускацией» для секретов. Base64 ничего не скрывает. Любой, кто видит строку Base64, может её декодировать. Храните секреты в менеджере секретов или зашифрованном хранилище, кодирования для этого мало.

Использовать хеш как замок. Хеш однонаправлен, но короткие или распространённые входы можно угадать. Захешируйте hello lab, и атакующий со списком распространённых фраз восстановит его, не взламывая SHA-256. Поэтому для хеширования паролей нужны соль и медленные алгоритмы.

Использовать обычный SHA-256 для паролей. SHA-256 быстрый по замыслу: для контрольных сумм файлов это подходит, а для хранения паролей подходит плохо. Используйте функцию хеширования паролей, созданную для этой задачи.

Доверять хешу из того же места, что и файл. Контрольная сумма на той же скомпрометированной странице ничего не доказывает. Получите ожидаемый хеш по отдельному доверенному каналу или проверьте цифровую подпись.

Забывать, что шифрованию нужно управление ключами. В лабораторной используется пароль, введённый в приглашении. Реальные системы должны хранить, ротировать и защищать ключи. Потеря ключа означает потерю данных.

Писать собственную криптографию. Используйте проверенные библиотеки и протоколы. Команды лабораторной нужны для обучения, шаблоном для продакшен-систем они не служат.

Что дальше

Повторите три лабораторные с другой фразой, затем с файлом в несколько килобайт. Заметьте, что вывод Base64 растёт примерно на треть, хеш остаётся длиной 64 символа, а зашифрованный файл увеличивается только на заголовок и дополнение.

Дальше почитайте о хешировании паролей. Сложность там связана со стоимостью перебора, само хеширование ни при чём. Следующая статья серии, Как работает хеширование паролей с Argon2id, объясняет соли, коэффициенты сложности и то, почему важны медленные хеши. Если сначала хотите освежить в памяти шифры, прочитайте Шифр Цезаря на Python. Там ключ и алгоритм показаны в виде, который можно проверить.

Изучая реальные протоколы, ищите в каждой схеме три вещи: что она кодирует, что шифрует и что хеширует. Большая часть путаницы исчезает, если спросить, какую из трёх задач решает каждая часть.

Источники

Источники

  1. RFC 4648: The Base16, Base32, and Base64 Data Encodings
  2. FIPS 180-4: Secure Hash Standard (SHS), with update 1
  3. OWASP Cryptographic Storage Cheat Sheet

Ещё по теме: Криптография

Криптография

Пароли и хеширование с Argon2id

Почему быстрые хеши не годятся для паролей, что делают соль и коэффициент нагрузки и как хешировать и проверять пароли с Argon2id в Python через argon2-cffi.

Средний9 мин чтения