В симметричных алгоритмах шифрования, таких как AES, одинаковый открытый текст при одинаковом ключе может давать одинаковый результат шифрования. Это свойство превращается в уязвимость, если не вводится дополнительная случайность. Для устранения этой проблемы используется вектор инициализации (IV — Initialization Vector).
IV представляет собой случайный или псевдослучайный набор байт фиксированной длины, который применяется на этапе первого блока шифрования. Он не является секретным, но должен быть уникальным для каждой операции шифрования с одним и тем же ключом.
Вектор инициализации особенно важен в режимах блочного шифрования, где данные разбиваются на блоки фиксированного размера.
В режиме CBC каждый блок открытого текста перед шифрованием XOR-ится с предыдущим зашифрованным блоком. Для первого блока используется IV.
C_1 = E_K(P_1 IV)
Последующие блоки зависят от предыдущих:
C_i = E_K(P_i C_{i-1})
Таким образом, даже одинаковые входные данные при разных IV дают разные результаты шифрования.
В режиме CTR IV используется как начальное значение счётчика, который изменяется для каждого блока. Здесь IV фактически формирует поток ключа.
S_i = E_K(IV + i)
C_i = P_i S_i
К IV предъявляются строгие требования:
Повтор IV с одним и тем же ключом приводит к утечке информации о структуре данных и может позволить атаки на шифротекст.
В библиотеке CryptoJS вектор инициализации задаётся через параметр
iv при вызове методов шифрования.
Обычно IV создаётся случайным образом:
const iv = CryptoJS.lib.WordArray.random(16);
16 байт соответствует размеру блока AES.
const key = CryptoJS.enc.Utf8.parse("my-secret-key-123");
const message = "Confidential data";
const encrypted = CryptoJS.AES.encrypt(message, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
const decrypted = CryptoJS.AES.decrypt(encrypted, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
const originalText = decrypted.toString(CryptoJS.enc.Utf8);
IV должен быть известен при расшифровании, поэтому обычно он сохраняется вместе с зашифрованными данными.
На практике IV часто добавляется в начало или конец зашифрованного сообщения:
const result = iv.concat(encrypted.ciphertext).toString(CryptoJS.enc.Base64);
При расшифровке данные разделяются обратно:
const raw = CryptoJS.enc.Base64.parse(result);
const iv = CryptoJS.lib.WordArray.create(raw.words.slice(0, 4));
const ciphertext = CryptoJS.lib.WordArray.create(raw.words.slice(4));
Неправильное использование IV приводит к критическим уязвимостям:
Особенно опасно повторение IV в CBC: при одинаковом ключе и IV одинаковые первые блоки открытого текста дают одинаковые блоки шифротекста, что позволяет анализировать структуру данных.
Режим ECB (Electronic Codebook) не использует IV вообще. Это делает его небезопасным для большинства задач, поскольку одинаковые блоки данных шифруются одинаково, независимо от контекста.
CryptoJS позволяет явно выбрать режим:
mode: CryptoJS.mode.ECB
Но в криптографической практике его использование считается нежелательным именно из-за отсутствия IV.
IV часто путают с солью (salt), но их назначение различается:
В CryptoJS соль используется так:
CryptoJS.PBKDF2(password, salt, {
keySize: 256 / 32,
iterations: 1000
});
IV при этом применяется уже на уровне шифрования результата.
Корректная схема работы обычно включает: