Шифрование WebAssembly: почти нативная скорость крипто в браузере
Используйте WebAssembly для почти нативной скорости шифрования. Сравните реализации WASM crypto.
Скомпилированные в WASM криптографические библиотеки работают примерно на 60-80% от скорости нативного C в браузере — в 3-10 раз быстрее реализаций на чистом JavaScript. Для примитивов шифрования, отсутствующих в Web Crypto API (ChaCha20-Poly1305, Argon2id, XChaCha20, постквантовые KEM как Kyber), WASM — практический путь к приемлемой производительности. libsodium.js — доминирующий вариант, предоставляющий полный API libsodium с WASM-бинарником размером 200 КБ. Для примитивов, поддерживаемых Web Crypto, — таких как AES-256-GCM — нативная аппаратно-ускоренная реализация браузера легко превосходит WASM, поэтому WASM подходит не всегда. Это руководство описывает, когда стоит обращаться к WASM и как оценить компромиссы.
Когда нативный Web Crypto выигрывает
Для алгоритмов, уже доступных через Web Crypto — AES-GCM, AES-CBC, AES-CTR, PBKDF2, HMAC, RSA-OAEP, ECDH, ECDSA, SHA-256/384/512 — нативная реализация браузера использует аппаратное ускорение через AES-NI на x86 и криптографические расширения ARMv8 на мобильных. Типичная пропускная способность AES-256-GCM:
- Web Crypto (Chrome на Apple Silicon): 1.7 ГБ/с
- Web Crypto (Chrome на Intel x86 с AES-NI): 1.2 ГБ/с
- libsodium WASM AES-GCM: 400-800 МБ/с
- Нативный OpenSSL эталон: 3-5 ГБ/с
WASM не имеет доступа к AES-NI изнутри песочницы; он откатывается к побитовым (bitsliced) реализациям AES, которые медленнее, но выполняются за константное время. Для всего, что поддерживает Web Crypto, используйте его в первую очередь.
Где выигрывает WASM
Для алгоритмов, отсутствующих в Web Crypto:
- ChaCha20-Poly1305: быстрее AES на устройствах без AES-NI. Нативная поддержка браузера отсутствует в 2026 году.
- XChaCha20-Poly1305: 192-битные nonce делают случайные nonce безопасными при любом масштабе.
- Argon2id: победитель PHC для хэширования паролей. Нативной поддержки браузера нет.
- X25519 / Ed25519: Safari 17 и Firefox 129 добавили нативную поддержку, но WASM остаётся портируемым путём.
- BLAKE2b / BLAKE3: быстрые хэш-функции, отсутствующие в Web Crypto.
- Kyber, Dilithium, SPHINCS+: постквантовые примитивы; только в виде библиотек.
- Потоковый AEAD:
crypto_secretstreamиз libsodium обрабатывает потоковое шифрование больших файлов; в Web Crypto эквивалента нет.
Для этих алгоритмов реализации на JavaScript существуют, но в 5-20 раз медленнее WASM. На файле 1 ГБ с ChaCha20-Poly1305 WASM работает ~2 секунды, а чистый JS — 15-40 секунд.
libsodium.js: основной инструмент
libsodium-wrappers (libsodium, скомпилированный через Emscripten, с JS-обёртками) — главная библиотека выбора. 200 КБ WASM + ~50 КБ JS-обёртки в gzip.
import _sodium from 'libsodium-wrappers';
await _sodium.ready;
const sodium = _sodium;
// Аутентифицированное шифрование ChaCha20-Poly1305
const key = sodium.crypto_aead_xchacha20poly1305_ietf_keygen();
const nonce = sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
const ciphertext = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(
plaintext, null, null, nonce, key
);
Сборка «sumo» (с дополнительными алгоритмами) — ~600 КБ; дефолтная сборка покрывает 90% сценариев использования, включая AEAD, хэширование паролей (Argon2id), X25519 и Ed25519.
Загружайте динамически, чтобы WASM-бинарник не блокировал первоначальный рендер страницы:
async function getSodium() {
if (!window._sodium) {
const mod = await import('libsodium-wrappers');
await mod.ready;
window._sodium = mod;
}
return window._sodium;
}
Потоковый AEAD через crypto_secretstream
Для передачи больших файлов crypto_secretstream_xchacha20poly1305 из libsodium — наиболее чистый потоковый AEAD в браузере:
const { state, header } = sodium.crypto_secretstream_xchacha20poly1305_init_push(key);
const chunk1 = sodium.crypto_secretstream_xchacha20poly1305_push(
state, plaintext1, null,
sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE
);
// Последний чанк использует TAG_FINAL для обнаружения усечения получателем
const chunkLast = sodium.crypto_secretstream_xchacha20poly1305_push(
state, plaintextLast, null,
sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
);
Маркер TAG_FINAL позволяет получателю обнаружить атаки усечения — если атакующий удаляет завершающие чанки, дешифрование на клиенте завершается неудачей. В AES-GCM Web Crypto этого свойства нет; пришлось бы строить обнаружение усечения самостоятельно (число чанков в AAD или общий хэш файла, проверяемый после дешифрования).
Argon2 в браузере
Для деривации ключей на основе пароля в браузере Argon2id через WASM — лучшая практика 2026 года. Варианты:
- argon2-browser: специализированная WASM-библиотека Argon2, ~200 КБ
- libsodium.js: Argon2id через
crypto_pwhash, если libsodium уже используется - @noble/hashes: чистый JS Argon2id, ~15 КБ бандл, в 3-5 раз медленнее WASM
Пример с argon2-browser:
import argon2 from 'argon2-browser';
const result = await argon2.hash({
pass: password,
salt: salt, // Uint8Array, 16+ байт
type: argon2.ArgonType.Argon2id,
time: 3,
mem: 65536, // КиБ, то есть 64 МиБ
parallelism: 4,
hashLen: 32,
});
// result.hash — Uint8Array, который можно использовать как AES-256 ключ
Подбирайте параметры под целевое время ожидания пользователя. Базовый уровень OWASP 2024 (m=19 МиБ, t=2, p=1) выполняется за ~300-500 мс. Более строгий (m=64 МиБ, t=3, p=4) — за ~1-2 секунды на современном железе.
Постквантовая крипто через WASM
Kyber (инкапсуляция ключей) и Dilithium (подписи) были стандартизированы NIST в 2024 году как ML-KEM и ML-DSA. JavaScript-реализации существуют (pq-crystals имеет справочную реализацию), но WASM-версии — liboqs-js — быстрее и лучше поддаются аудиту.
Для передачи файлов постквантовые KEM позволяют обернуть симметричный ключ файла квантово-стойким асимметричным примитивом. Гибридные схемы (ML-KEM + X25519) защищают одновременно от классических и будущих квантовых атакующих. Chrome включил ML-KEM в TLS-рукопожатия в версии 116 (2023), но прикладной WASM остаётся путём для ключей содержимого файлов.
Размер бандла
WASM-бинарники поставляются как часть JS-бандла или загружаются лениво. Примерные размеры (gzip):
- libsodium.js дефолтный: 200 КБ
- libsodium.js sumo: 600 КБ
- argon2-browser: 200 КБ
- liboqs-js (постквантовый): 1 МБ+
Для лэндинга, шифрующего в браузере, 200-300 КБ WASM допустимы при ленивой загрузке после взаимодействия с пользователем. Для приложения, где шифрование — основная функция, поставка WASM при первоначальной загрузке обоснована. Используйте подсказки rel="modulepreload" или кэширование через service worker для мгновенной повторной загрузки.
Проверяйте в сетевой панели инструментов разработчика, кэшируется ли WASM-бинарник после первой загрузки. Неправильные заголовки кэша могут вызывать повторные загрузки при каждом посещении.
Накладные расходы на компиляцию и инстанцирование
WebAssembly.instantiate() разбирает и компилирует бинарник — это занимает 20-100 мс для модуля 200 КБ на десктопе и 100-500 мс на мобильных. Это происходит один раз за сессию. Кэшируйте скомпилированный модуль в IndexedDB через сериализацию WebAssembly.Module для более быстрых последующих загрузок.
WebAssembly.instantiateStreaming() объединяет загрузку и компиляцию, экономя 30-50% времени запуска по сравнению с instantiate() для полученного ответа:
const response = fetch('/sodium.wasm');
const { instance } = await WebAssembly.instantiateStreaming(response, importObject);
Подводные камни управления памятью
WASM-модули выделяют память в линейных страницах (64 КиБ каждая). libsodium по умолчанию начинает с малой памяти и растёт по мере необходимости, но неограниченный рост может достичь лимитов браузера (обычно 4 ГБ линейной памяти в 32-битном WASM). Для шифрования файлов нескольких ГБ:
- Обрабатывайте чанки через WASM-модуль потоком, не загружайте целый файл в WASM-память
- Освобождайте буферы после каждого чанка через
sodium.memzeroили обнуляя ссылки - Отслеживайте
WebAssembly.Memory.buffer.byteLengthв долгих сессиях
Memory64 (предложен, частично реализован в Chrome) снимает ограничение 4 ГБ. Пока что чанкованная обработка — надёжный путь.
Соображения безопасности, специфичные для WASM
WASM работает в том же источнике, что и загрузившая его страница, наследуя те же правила CSP и CORS. Если атакующий внедряет код в ваш источник (XSS), он может вызывать экспорты вашего WASM-модуля. WASM не создаёт дополнительного барьера безопасности.
Гарантии константного времени: большинство крипто-библиотек, скомпилированных в WASM, сохраняют свойства константного времени из исходного кода на C, но WASM-в-JIT трансляция может вносить код с переменным временем на некоторых платформах. Журналы аудита libsodium отмечают несколько специфичных для WASM проблем с константным временем. Для большинства моделей угроз это незначительно; для защиты от локальных атакующих с точными измерениями времени — разрыв стоит учитывать.
Практический рецепт
Для приложения передачи файлов в 2026 году:
- Используйте Web Crypto для AES-256-GCM, PBKDF2, HMAC, SHA-256, RSA-OAEP при необходимости RSA
- Используйте libsodium.js через WASM для хэширования паролей Argon2id
- Используйте libsodium.js для потокового шифрования больших файлов (
crypto_secretstream) - Используйте нативный Ed25519/X25519 там, где поддерживается, и откатывайтесь на libsodium иначе
- Загружайте WASM лениво после взаимодействия с пользователем, чтобы держать бандл лёгким
- Проводите бенчмарки на целевых устройствах; не переносите десктопные числа на мобильные
HexaTransfer остаётся на Web Crypto AES-GCM для массового шифрования файлов, поскольку аппаратно-ускоренный путь быстрейший. WASM зарезервирован для алгоритмов, которые нативные API не предоставляют.
Попробуйте на hexatransfer.com — бесплатно, без регистрации, до 10 ГБ.
Безопасная отправка больших файлов со сквозным шифрованием
Передавайте файлы до 10 ГБ бесплатно со сквозным шифрованием. Регистрация не требуется. Ваши файлы шифруются в браузере перед загрузкой — никто другой не может их прочитать.
Отправить файл