Перейти к содержанию
HexaTransfer
Вернуться к блогу
Шифрование и безопасность

Шифрование 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 ГБ бесплатно со сквозным шифрованием. Регистрация не требуется. Ваши файлы шифруются в браузере перед загрузкой — никто другой не может их прочитать.

Отправить файл