Padding oracle и ошибки режимов
Как один-единственный бит обратной связи — «паддинг правильный или нет» — позволяет расшифровать сообщение, не зная ключа.
Padding oracle — уязвимость, при которой система по-разному реагирует на корректный и некорректный паддинг расшифрованного блока, и эта реакция (ошибка, время ответа, код статуса) превращается в «оракула», помогающего восстановить открытый текст.
Это хрестоматийный пример того, как безупречный шифр (AES в режиме CBC) ломается из-за того, ЧТО система сообщает наружу. Сам ключ остаётся неизвестным — атакующему достаточно уметь спрашивать сервер «подходит ли паддинг?» и наблюдать ответ.
Зачем это знать защитнику
Padding oracle годами всплывал в реальных протоколах (атаки на TLS вроде POODLE и Lucky Thirteen — родственники этой идеи). Урок для защитника фундаментальный: шифрование само по себе не защищает целостность. Если приёмник расшифровывает любой присланный ему шифртекст и как-то реагирует на «мусор» внутри, он невольно становится оракулом. Понимание этого диктует выбор режима: только аутентифицированное шифрование (AEAD), которое отвергает подделанный шифртекст ещё до того, как что-либо о нём «расскажет».
Как режим CBC создаёт оракула
В CBC сообщение делится на блоки, и каждый блок перед шифрованием XOR-ится с предыдущим шифрблоком. Чтобы длина была кратна блоку, добавляют паддинг по схеме PKCS#7: дополняют байтами, значение которых равно их количеству (один недостающий байт — 01, три — 03 03 03). При расшифровке приёмник проверяет, что паддинг валиден.
Проблема в обратной связи. Если сервер на неверный паддинг отвечает «ошибка дешифрования», а на верный — «ошибка данных» (или просто отвечает с разной задержкой), он раскрывает один бит: «паддинг корректен?». Концептуально процесс выглядит так:
Атакующий подменяет байты предыдущего блока и шлёт серверу.
Сервер расшифровывает -> XOR с подменёнными байтами -> проверяет паддинг.
если ответ "паддинг неверный" -> гипотеза не подошла
если ответ "паддинг верный" -> найден байт промежуточного значения
Зная промежуточное значение и настоящий предыдущий блок,
атакующий вычисляет байт открытого текста — БЕЗ ключа.
Повторяя по байту, восстанавливает блок целиком.
Важно: это не «угадывание» сообщения, а детерминированное восстановление по одному биту обратной связи на каждый подобранный байт. Никаких подробностей подбора здесь не нужно — суть в том, что любая различимая реакция на структуру расшифрованного блока опасна.
Другие ошибки режимов
Padding oracle — частный случай большого класса «использовали шифрование без аутентификации». Сюда же:
- ECB — шифрует одинаковые блоки одинаково, выдавая структуру данных (классическая демонстрация — «зашифрованная картинка пингвина», на которой всё ещё виден силуэт).
- CBC/CTR без MAC — позволяют атакующему незаметно менять биты шифртекста, влияя на открытый текст предсказуемым образом.
- MAC-then-Encrypt и «сначала расшифруй, потом проверь» — приёмник вынужден обрабатывать неаутентифицированные данные, открывая окно для оракулов.
Как это работает под капотом
Корень — нарушение принципа: получатель не должен совершать НИКАКИХ наблюдаемых действий над шифртекстом, пока не убедится в его подлинности. В уязвимой схеме порядок обратный: сначала расшифровали, потом (или вообще никогда) проверили целостность. Любой шаг между «расшифровал» и «проверил подлинность» — потенциальный канал утечки: исключение, лог, ветка кода, задержка. Правильная схема — encrypt-then-MAC: сначала проверяем MAC по шифртексту, и если он не сошёлся, немедленно отвергаем, ничего не расшифровывая.
Полезно сравнить три порядка операций и понять, почему безопасен только один. При Encrypt-and-MAC (MAC считается по открытому тексту параллельно) сам тег может утечь о содержимом, а проверка происходит поздно. При MAC-then-Encrypt (сначала MAC по открытому тексту, потом всё шифруется) приёмник вынужден сперва расшифровать, чтобы добраться до MAC, — именно это окно и эксплуатирует padding oracle. И только Encrypt-then-MAC позволяет проверить подлинность, не прикасаясь к открытому тексту: MAC берётся по готовому шифртексту, сверяется constant-time-сравнением, и при несовпадении запрос отвергается мгновенно и единообразно. AEAD-режимы реализуют по сути этот же безопасный порядок внутри одного примитива, поэтому собирать схему вручную почти никогда не нужно.
Как защититься
- Используйте AEAD. Aутентифицированное шифрование с присоединёнными данными — AES-GCM, ChaCha20-Poly1305 — шифрует И проверяет целостность одним примитивом. Подделанный шифртекст отвергается единообразно, оракула не возникает.
- Если только CBC — то encrypt-then-MAC. Считайте HMAC по шифртексту (и IV), проверяйте его constant-time-сравнением ДО расшифровки. Не сошёлся — отвергайте, не трогая содержимое.
- Единый ответ на любую ошибку. Не различайте «неверный паддинг» и «неверные данные» ни текстом, ни кодом, ни временем ответа. Снаружи всё «невалидно».
- Никогда не пишите шифрование вручную. Берите
cryptography(Python, класс AESGCM), libsodium (crypto_secretbox), Tink. Они по умолчанию аутентифицируют. - Не используйте ECB. Никогда, ни для чего, кроме узких низкоуровневых построений внутри других режимов.
Правовое напоминание: проверять устойчивость к padding oracle можно только на своих сервисах или в разрешённой лаборатории (DVWA, juice-shop, локальная ВМ). Атака на чужой реальный сервис незаконна (УК РФ ст. 272).
Итоги
- Шифрование без аутентификации не защищает целостность — этим и пользуется padding oracle.
- Любая различимая реакция на корректность расшифрованного блока (ошибка, код, время) — оракул, раскрывающий открытый текст без ключа.
- Правильный порядок — encrypt-then-MAC: проверяем подлинность шифртекста ДО расшифровки.
- Лучшая защита — AEAD (AES-GCM, ChaCha20-Poly1305) из проверенной библиотеки; единый ответ на все ошибки; никакого ECB и самописного крипто.