В режиме блочного шифрования, например AES-CBC, данные разбиваются на блоки фиксированного размера (обычно 16 байт). Если длина сообщения не кратна размеру блока, применяется дополнение (padding). Наиболее распространённый вариант — PKCS#7, где каждый добавленный байт содержит значение количества добавленных байт.
Пример padding для блока из 16 байт:
05 05 05 05 0501Именно обработка padding становится источником уязвимости.
Padding oracle — это ситуация, когда система косвенно сообщает атакующему, корректно ли было дополнение при расшифровке.
Oracle («оракул») появляется, если:
сервер возвращает разные ошибки для:
либо поведение отличается по времени обработки
либо различается HTTP-ответ (код, текст, структура)
В AES-CBC расшифрование выглядит так:
C_i — шифртекстP_i = D_k(C_i) ⊕ C_{i-1}Если атакующий изменяет C_{i-1}, он влияет на
расшифрованный блок P_i.
Атака позволяет восстановить открытый текст без знания ключа, используя обратную связь системы.
Основная идея:
Перехватывается шифртекст
Блоки модифицируются побайтно
Отправляются на сервер
По ответу определяется:
Постепенно восстанавливаются байты plaintext
Ключевой момент — наличие различимого поведения системы.
В CBC режиме:
P_i = D_k(C_i) C_{i-1}
Это означает:
C_{i-1} влияет на
соответствующий байт P_iPadding проверяется отдельно от целостности данных, что создаёт окно атаки.
В экосистеме SJCL типичная ошибка возникает при следующей архитектуре:
Пример (антипаттерн):
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)Padding oracle появляется, если атакующий может наблюдать:
Даже миллисекундные различия иногда достаточны.
Атакующий работает по следующей схеме:
После этого:
Сложность — порядка 256 * block_size на блок.
Классическая ошибка архитектуры:
CBC без MAC:
Именно это создаёт padding oracle.
В SJCL предусмотрены режимы, устраняющие классическую проблему:
CCM объединяет:
Особенности:
Если MAC неверен — данные не расшифровываются дальше.
GCM обеспечивает:
Главное свойство:
Классическая безопасная схема:
= _k(), = _k(C)
И затем проверка:
Плохо:
Хорошо:
Даже при одинаковых ошибках:
Это создаёт тайминг-оракул.
Если происходит:
атака становится возможной.
Правильный подход:
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 не спасает, если протокол раскрывает информацию.
В прикладных системах с SJCL безопасная модель обычно включает:
Padding oracle — это не уязвимость шифра, а уязвимость протокола вокруг него.
Любая система, использующая SJCL или другой криптографический стек, должна гарантировать: