Padding oracle атака и как её избежать

В режиме блочного шифрования, например AES-CBC, данные разбиваются на блоки фиксированного размера (обычно 16 байт). Если длина сообщения не кратна размеру блока, применяется дополнение (padding). Наиболее распространённый вариант — PKCS#7, где каждый добавленный байт содержит значение количества добавленных байт.

Пример padding для блока из 16 байт:

  • если нужно добавить 5 байт → 05 05 05 05 05
  • если нужно добавить 1 байт → 01

Именно обработка padding становится источником уязвимости.


Как возникает padding oracle

Padding oracle — это ситуация, когда система косвенно сообщает атакующему, корректно ли было дополнение при расшифровке.

Oracle («оракул») появляется, если:

  • сервер возвращает разные ошибки для:

    • некорректного padding
    • некорректной подписи / MAC
  • либо поведение отличается по времени обработки

  • либо различается HTTP-ответ (код, текст, структура)

В AES-CBC расшифрование выглядит так:

  • C_i — шифртекст
  • P_i = D_k(C_i) ⊕ C_{i-1}

Если атакующий изменяет C_{i-1}, он влияет на расшифрованный блок P_i.


Суть атаки padding oracle

Атака позволяет восстановить открытый текст без знания ключа, используя обратную связь системы.

Основная идея:

  1. Перехватывается шифртекст

  2. Блоки модифицируются побайтно

  3. Отправляются на сервер

  4. По ответу определяется:

    • корректен padding или нет
  5. Постепенно восстанавливаются байты plaintext

Ключевой момент — наличие различимого поведения системы.


Почему CBC особенно уязвим

В CBC режиме:

P_i = D_k(C_i) C_{i-1}

Это означает:

  • изменение одного байта в C_{i-1} влияет на соответствующий байт P_i
  • атакующий может контролировать результат расшифровки

Padding проверяется отдельно от целостности данных, что создаёт окно атаки.


Пример уязвимого сценария в JavaScript (SJCL + CBC)

В экосистеме SJCL типичная ошибка возникает при следующей архитектуре:

  • AES-CBC используется для шифрования
  • MAC отсутствует или проверяется неправильно
  • ошибка различается для padding и integrity

Пример (антипаттерн):

const sjcl = require("sjcl");

function decrypt(key, iv, ciphertext) {
  try {
    const aes = new sjcl.cipher.aes(key);
    const plain = sjcl.mode.cbc.decrypt(aes, ciphertext, iv);

    // проверка padding внутри библиотеки или отдельно
    return sjcl.codec.utf8String.fromBits(plain);
  } catch (e) {
    if (e.message === "bad padding") {
      throw new Error("Padding error");
    }
    throw new Error("Decrypt error");
  }
}

Проблема:

  • различие ошибок (bad padding vs decrypt error)
  • возможность различить причину сбоя
  • наличие oracle-эффекта

Где возникает утечка информации

Padding oracle появляется, если атакующий может наблюдать:

  • HTTP 400 vs 500
  • разные сообщения ошибок
  • разное время ответа
  • различный формат ответа

Даже миллисекундные различия иногда достаточны.


Практическая модель атаки

Атакующий работает по следующей схеме:

  • берётся последний байт блока
  • перебираются значения 0–255
  • отправляется модифицированный шифртекст
  • фиксируется момент, когда padding становится валидным

После этого:

  • вычисляется значение байта plaintext
  • процесс повторяется для остальных байтов

Сложность — порядка 256 * block_size на блок.


Основная проблема: разделение шифрования и целостности

Классическая ошибка архитектуры:

  • шифрование ≠ защита целостности

CBC без MAC:

  • конфиденциальность есть
  • целостность отсутствует

Именно это создаёт padding oracle.


Как SJCL решает проблему корректно

В SJCL предусмотрены режимы, устраняющие классическую проблему:

1. CCM (Counter with CBC-MAC)

CCM объединяет:

  • шифрование
  • аутентификацию

Особенности:

  • сначала вычисляется MAC
  • затем шифруется
  • проверка целостности происходит до расшифрования

Если MAC неверен — данные не расшифровываются дальше.


2. GCM (в некоторых интеграциях)

GCM обеспечивает:

  • потоковое шифрование
  • встроенную аутентификацию

Главное свойство:

  • любая модификация ciphertext выявляется до раскрытия plaintext

3. OCB (в SJCL присутствовал, но считается устаревшим в ряде реализаций)


Правильная модель защиты: Encrypt-then-MAC

Классическая безопасная схема:

= _k(), = _k(C)

И затем проверка:

  • сначала HMAC
  • только потом дешифрование

Критические ошибки, приводящие к oracle

1. Разные сообщения об ошибках

Плохо:

  • “invalid padding”
  • “invalid MAC”

Хорошо:

  • “invalid data”

2. Разное время выполнения

Даже при одинаковых ошибках:

  • ранний возврат при padding error
  • более долгий процесс при MAC error

Это создаёт тайминг-оракул.


3. Частичное раскрытие данных до проверки MAC

Если происходит:

  • сначала decrypt
  • потом verify

атака становится возможной.


Безопасная реализация в SJCL

Правильный подход:

const sjcl = require("sjcl");

function encrypt(key, plaintext) {
  return sjcl.mode.ccm.encrypt(
    new sjcl.cipher.aes(key),
    sjcl.codec.utf8String.toBits(plaintext),
    iv,
    [],
    64
  );
}

function decrypt(key, ciphertext) {
  try {
    const aes = new sjcl.cipher.aes(key);

    const plainBits = sjcl.mode.ccm.decrypt(aes, ciphertext, iv, [], 64);

    return sjcl.codec.utf8String.fromBits(plainBits);
  } catch (e) {
    throw new Error("Invalid data");
  }
}

Особенности:

  • единая ошибка
  • отсутствие утечек причины сбоя
  • проверка целостности до раскрытия данных

Почему даже «малые различия» критичны

Padding oracle относится к классу атак по побочным каналам. Уязвимость возникает не в математике шифра, а в:

  • обработке ошибок
  • поведении системы
  • наблюдаемом отклике

Даже идеальный AES не спасает, если протокол раскрывает информацию.


Типовые архитектуры, которые приводят к уязвимости

  • AES-CBC + отсутствие MAC
  • AES-CBC + MAC, но проверка после decrypt
  • разные exception messages
  • REST API с разными HTTP-кодами на ошибки криптографии
  • логирование причин крипто-ошибок

Практика защиты в системах на SJCL

В прикладных системах с SJCL безопасная модель обычно включает:

  • AES-CCM или AES-GCM
  • единый путь обработки ошибок
  • отказ от CBC без аутентификации
  • минимизация различий в ответах API

Ключевая идея безопасности

Padding oracle — это не уязвимость шифра, а уязвимость протокола вокруг него.

Любая система, использующая SJCL или другой криптографический стек, должна гарантировать:

  • отсутствие различимых ошибок расшифрования
  • обязательную аутентификацию ciphertext
  • отсутствие раннего раскрытия структуры данных