Детерминированное шифрование — это подход, при котором одинаковый открытый текст при одинаковом ключе всегда преобразуется в одинаковый шифртекст. На уровне интуиции это выглядит удобно: можно сравнивать зашифрованные значения без расшифрования, использовать их как индекс, выполнять поиск по базе данных. Однако с точки зрения криптографии это почти всегда означает существенное ослабление модели безопасности.
Web Crypto API (WebCrypto) изначально ориентирован на вероятностное (недетерминированное) шифрование, где каждый вызов шифрования с одним и тем же ключом и одинаковыми данными даёт различный результат за счёт случайного вектора инициализации (IV/nonce). Это ключевая часть модели безопасности современных режимов, таких как AES-GCM.
В WebCrypto шифрование реализуется через
SubtleCrypto.encrypt. Типичный пример AES-GCM:
const key = await crypto.subtle.generateKey(
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"]
);
const iv = crypto.getRandomValues(new Uint8Array(12));
const ciphertext = await crypto.subtle.encrypt(
{ name: "AES-GCM", iv },
key,
new TextEncoder().encode("secret data")
);
Ключевой элемент здесь — iv. Он обязан быть уникальным
для каждого шифрования с одним и тем же ключом. Именно он делает
результат недетерминированным.
Если убрать случайность IV и использовать фиксированное значение, система становится детерминированной, но одновременно теряет криптографическую стойкость.
Детерминированное шифрование означает:
На практике это достигается либо:
AES-GCM специально разработан как режим аутентифицированного шифрования с обязательным уникальным nonce. Его безопасность формально опирается на предположение:
nonce никогда не повторяется для одного ключа
Если использовать фиксированный IV:
const iv = new Uint8Array(12); // все нули
то возникает ряд проблем:
Фактически это превращает систему в криптографически небезопасную.
Главная проблема детерминированного шифрования — утечка равенства.
Даже если сам текст неизвестен, наблюдатель получает информацию:
Это особенно опасно в контексте:
Пример: если зашифровать список пользователей, то одинаковые email будут давать одинаковые шифртексты, позволяя восстановить статистику активности.
В WebCrypto отсутствует полноценная поддержка схем вроде:
Эти режимы специально разработаны для “безопасного детерминизма”, где одинаковый вход даёт одинаковый выход, но при этом сохраняется устойчивость к ошибкам использования nonce.
Поэтому разработчики иногда пытаются эмулировать детерминированность через:
const iv = new Uint8Array(12); // всегда нули
Результат:
const iv = crypto.subtle.digest("SHA-256", data)
Проблема:
Иногда вместо шифрования используют:
HMAC(key, data)
Но это уже не шифрование, а MAC:
Детерминированное шифрование ломает ключевое свойство современных схем — semantic security (IND-CPA).
При нарушении этого свойства атакующий получает возможность:
Особенно опасны сценарии, где атакующий может наблюдать множество шифртекстов и сравнивать их между собой.
В недетерминированных схемах ошибка часто компенсируется случайностью IV.
В детерминированной схеме любой повтор:
приводит к идентичному шифртексту, что делает компрометацию данных системной, а не локальной.
Основной мотив использования детерминированного шифрования — возможность выполнять:
например:
Однако в WebCrypto это приводит к необходимости внешних компромиссов:
Вместо детерминированного шифрования часто используется:
Это позволяет:
На практике наиболее частые ошибки:
Каждая из этих ошибок приводит к разным, но критическим последствиям — от утечки структуры данных до полной компрометации ключа.
При переходе к детерминированному шифрованию исчезают:
Вместо этого появляется только одно преимущество — возможность сравнения без расшифрования, которое почти всегда достигается более безопасными архитектурными методами.
В рамках Web Crypto API корректная модель выглядит так:
crypto.getRandomValuesЛюбое отклонение от этого приводит к деградации модели безопасности ниже уровня, для которого WebCrypto вообще проектировался.