Повторное использование вектора инициализации (IV) в симметричном шифровании разрушает ключевое свойство современных режимов — семантическую стойкость. Даже при корректно выбранном алгоритме (например, AES) ошибка в управлении IV приводит к утечке информации о структуре исходных данных и потенциальной возможности восстановления текста.
В блочных шифрах один и тот же открытый текст при одинаковом ключе должен давать разные шифротексты. Эту задачу решает IV (Initialization Vector).
C = E_K(P IV)
В зависимости от режима работы алгоритма IV может:
Ключевая характеристика IV — уникальность при одном ключе, а не секретность.
В CryptoJS часто применяется AES в режиме CBC по умолчанию при явной передаче IV.
Пример корректного использования:
import CryptoJS from "crypto-js";
const key = CryptoJS.enc.Utf8.parse("1234567890123456");
const iv = CryptoJS.lib.WordArray.random(16);
const encrypted = CryptoJS.AES.encrypt("secret data", key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
const result = iv.concat(encrypted.ciphertext).toString(CryptoJS.enc.Base64);
IV генерируется случайно для каждого сообщения и передаётся вместе с шифротекстом.
Использование одного и того же IV с одинаковым ключом приводит к следующим эффектам:
Особенно критично это в режимах:
В этих режимах IV фактически задаёт начальное состояние цепочки.
На практике часто встречается следующий антишаблон:
const key = CryptoJS.enc.Utf8.parse("1234567890123456");
const iv = CryptoJS.enc.Utf8.parse("0000000000000000");
function encrypt(message) {
return CryptoJS.AES.encrypt(message, key, {
iv: iv
}).toString();
}
Проблема заключается в том, что каждый вызов функции использует одинаковое начальное состояние.
Если два сообщения начинаются одинаково, их шифротексты также будут иметь одинаковые префиксы, что создаёт утечку информации.
IV должен быть:
Рекомендуемый подход:
function encrypt(message, key) {
const iv = CryptoJS.lib.WordArray.random(16);
const encrypted = CryptoJS.AES.encrypt(message, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return {
iv: iv.toString(CryptoJS.enc.Base64),
data: encrypted.toString()
};
}
Здесь каждый вызов создаёт новое начальное состояние цепочки шифрования.
При расшифровке IV должен быть восстановлен:
function decrypt(payload, key) {
const iv = CryptoJS.enc.Base64.parse(payload.iv);
const encrypted = payload.data;
const decrypted = CryptoJS.AES.decrypt(encrypted, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return decrypted.toString(CryptoJS.enc.Utf8);
}
Отсутствие IV или его подмена делает расшифровку невозможной или приводит к некорректному результату.
Повтор IV не раскрывает ключ напрямую, но создаёт структурные утечки:
Для CBC это особенно критично, так как первый блок зависит только от IV и первого блока данных.
В режимах OFB и CFB повтор IV приводит к генерации одинакового ключевого потока:
C_i = P_i KS_i
Если KS (key stream) повторяется, то:
C_i C’_i = P_i P’_i
что позволяет восстановить взаимосвязи между открытыми текстами без знания ключа.
Наиболее частые ошибки:
Особенно опасна ситуация, когда IV генерируется глобально:
const iv = CryptoJS.lib.WordArray.random(16);
function encrypt(msg) {
return CryptoJS.AES.encrypt(msg, key, { iv }).toString();
}
В таком случае вся сессия шифрования становится уязвимой.
Безопасность симметричного шифрования в CryptoJS определяется не только алгоритмом AES и длиной ключа, но и корректностью управления параметрами режима.
IV не является дополнительным уровнем защиты, но его неправильное использование полностью нарушает свойства:
Корректная работа с IV — обязательное условие безопасного применения AES в прикладных JavaScript-сценариях.