Одной из самых частых проблем становится восприятие
crypto.subtle как синхронного или «почти синхронного» API.
Все криптографические операции в Web Crypto API возвращают
Promise, и это принципиально влияет на архитектуру
кода.
Типичная ошибка — попытка использовать результат напрямую:
const hash = crypto.subtle.digest("SHA-256", data);
console.log(hash); // Promise, а не результат
Правильная работа требует ожидания выполнения:
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
Игнорирование этого приводит к цепочке скрытых ошибок, особенно в сложных пайплайнах обработки данных.
Web Crypto API поддерживает строго ограниченный набор алгоритмов. Попытка использовать произвольные хеш-функции или шифры приводит к исключениям или невозможности реализации:
Частая ошибка — перенос серверной криптографии (Node.js
crypto) без адаптации под браузерный контекст.
// Ошибка: алгоритм не поддерживается
crypto.subtle.digest("MD5", data);
Критическая ошибка безопасности — повторное использование вектора инициализации (IV) при AES-GCM.
const iv = new Uint8Array(12); // фиксированный IV — ошибка
crypto.getRandomValues(iv);
Если IV повторяется, шифрование теряет криптографическую стойкость и становится уязвимым к восстановлению данных.
Правильный подход — генерация уникального IV для каждого сообщения:
const iv = crypto.getRandomValues(new Uint8Array(12));
Распространённая ошибка — применение Math.random() для
криптографических целей.
const key = Math.random().toString(36);
Этот подход полностью нарушает требования безопасности. В Web Crypto API существует специализированный механизм:
const bytes = new Uint8Array(16);
crypto.getRandomValues(bytes);
Web Crypto API строго различает форматы ключей:
rawjwkspkipkcs8Неверный формат приводит к невозможности импорта или экспорта ключа.
await crypto.subtle.importKey(
"raw",
keyData,
{ name: "AES-GCM" },
false,
["encrypt"]
);
Частая ошибка — попытка импортировать PEM-строку напрямую без
декодирования base64 и преобразования в ArrayBuffer.
Web Crypto API работает исключительно с бинарными данными. Передача строк без явного преобразования приводит к некорректным результатам.
Ошибочный вариант:
crypto.subtle.digest("SHA-256", "text");
Правильный:
const data = new TextEncoder().encode("text");
await crypto.subtle.digest("SHA-256", data);
Часто разработчики вручную пытаются преобразовывать строки в байты
через charCodeAt, что приводит к ошибкам кодировки.
// Ошибочный подход
const bytes = "test".split("").map(c => c.charCodeAt(0));
Корректный механизм:
const bytes = new TextEncoder().encode("test");
Ошибки криптографических операций часто остаются необработанными, что приводит к «тихим» сбоям.
crypto.subtle.decrypt(...).then(result => {
// обработка
});
Отсутствие catch делает отладку практически
невозможной:
crypto.subtle.decrypt(...).catch(err => {
console.error(err);
});
API доступно только в защищённом контексте:
Попытка выполнения в HTTP приводит к undefined:
console.log(window.crypto); // может быть недоступно
Одна из распространённых ошибок — использование слишком малого числа итераций или статической соли.
const salt = new Uint8Array(8); // недостаточно и часто фиксированная
Итерации ниже сотен тысяч делают алгоритм уязвимым к перебору.
Web Crypto API предоставляет digest, но он не
предназначен для хранения паролей.
crypto.subtle.digest("SHA-256", password);
Это создаёт ложное ощущение безопасности. Для паролей требуется PBKDF2:
await crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt,
iterations: 100000,
hash: "SHA-256"
},
baseKey,
{ name: "AES-GCM", length: 256 },
true,
["encrypt"]
);
Частая архитектурная ошибка — хранение криптографических ключей в
localStorage.
localStorage.setItem("key", exportedKey);
Это делает ключи доступными для XSS-атак и полностью нивелирует смысл криптографии на стороне клиента.
Разработчики часто смешивают симметричное шифрование и цифровую подпись.
encrypt / decrypt —
конфиденциальностьsign / verify — целостность и
аутентификацияПопытка использовать AES для проверки подлинности данных приводит к архитектурным ошибкам безопасности.
Функции предназначены для защиты ключей, но часто используются как обычное шифрование, что нарушает модель угроз.
crypto.subtle.wrapKey(...)
Непонимание назначения приводит к тому, что ключи оказываются уязвимыми при неправильной схеме обёртки.
Некорректные параметры приводят к исключениям или снижению стойкости:
Ошибкой является использование произвольных значений без проверки поддержки браузером.
Криптографические операции могут быть тяжёлыми, особенно RSA и PBKDF2. Выполнение их в main thread приводит к фризам интерфейса.
Часто игнорируется возможность использования Web Workers, несмотря на
полную поддержку crypto.subtle внутри них.
Несмотря на схожесть API, различия существенны:
Buffer vs
ArrayBuffer)Код, перенесённый напрямую, часто ломается без адаптации слоя преобразования данных.