Przejdź do treści
HexaTransfer
Wróć do bloga
Szyfrowanie i bezpieczenstwo

Szyfrowanie WebAssembly: prawie natywna prędkość krypto w przeglądarce

Użyj WebAssembly do prawie natywnych prędkości szyfrowania. Porównaj implementacje WASM crypto.

Biblioteki kryptograficzne skompilowane do WASM działają z prędkością ok. 60–80% natywnego C w przeglądarce, co jest 3–10x szybciej niż implementacje czysto-JavaScript. Dla prymitywów szyfrowania nieobecnych w Web Crypto API (ChaCha20-Poly1305, Argon2id, XChaCha20, post-kwantowe KEM jak Kyber), WASM to praktyczna droga do użytecznej wydajności. libsodium.js to dominująca opcja, dostarczająca pełne API libsodium z binarnym WASM 200 KB. Dla prymitywów obsługiwanych przez Web Crypto jak AES-256-GCM, natywna sprzętowo akcelerowana implementacja przeglądarki bije WASM bez trudu, więc WASM nie jest zawsze właściwym narzędziem. Ten przewodnik omawia kiedy po nie sięgać i jak mierzyć kompromisy.

Kiedy natywne Web Crypto wygrywa

Dla algorytmów, które Web Crypto już udostępnia — AES-GCM, AES-CBC, AES-CTR, PBKDF2, HMAC, RSA-OAEP, ECDH, ECDSA, SHA-256/384/512 — natywna implementacja przeglądarki używa akceleracji sprzętowej przez AES-NI na x86 i rozszerzenia krypto ARMv8 na urządzeniach mobilnych. Typowa przepustowość AES-256-GCM:

  • Web Crypto (Chrome na Apple Silicon): 1,7 GB/s
  • Web Crypto (Chrome na Intel x86 z AES-NI): 1,2 GB/s
  • libsodium WASM AES-GCM: 400–800 MB/s
  • Natywny OpenSSL referencyjny: 3–5 GB/s

WASM nie ma dostępu do AES-NI z wnętrza sandboxa; cofa się do bitslicowanych implementacji AES, które są wolniejsze ale w stałym czasie. Dla wszystkiego co Web Crypto obsługuje, używaj go jako pierwszego wyboru.

Gdzie WASM wygrywa

Dla algorytmów nieobecnych w Web Crypto:

  • ChaCha20-Poly1305: szybszy niż AES na urządzeniach bez AES-NI. Brak natywnego wsparcia przeglądarki w 2026.
  • XChaCha20-Poly1305: 192-bitowe nonce sprawiają, że losowe użycie nonce jest bezpieczne w każdej skali.
  • Argon2id: zwycięska funkcja hashowania haseł PHC. Brak natywnego wsparcia przeglądarki.
  • X25519 / Ed25519: Safari 17 i Firefox 129 dodały natywne wsparcie, ale WASM to nadal przenośna ścieżka.
  • BLAKE2b / BLAKE3: szybkie funkcje hash nieobecne w Web Crypto.
  • Kyber, Dilithium, SPHINCS+: post-kwantowe prymitywy; wyłącznie terytorium bibliotek.
  • Strumieniowe AEAD: crypto_secretstream libsodium obsługuje strumieniowe szyfrowanie dużych plików w czysty sposób; Web Crypto nie ma odpowiednika.

Dla tych algorytmów, implementacje JavaScript istnieją ale są 5–20x wolniejsze niż WASM. Na pliku 1 GB z ChaCha20-Poly1305, WASM działa w ~2 sekundy, podczas gdy czysty JS zajmuje 15–40 sekund.

libsodium.js: koń roboczy

libsodium-wrappers (libsodium kompilowane przez Emscripten z opakowaniami JS) to domyślna biblioteka. 200 KB WASM + ~50 KB opakowania JS, skompresowanych.

import _sodium from 'libsodium-wrappers';
await _sodium.ready;
const sodium = _sodium;

// Uwierzytelnione szyfrowanie 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
);

Wersja „sumo" (zawiera więcej algorytmów) to ~600 KB; domyślna kompilacja pokrywa 90% przypadków użycia, w tym AEAD, hashowanie haseł (Argon2id), X25519 i Ed25519.

Ładuj dynamicznie, by binarny WASM nie blokował pierwszego renderowania strony:

async function getSodium() {
  if (!window._sodium) {
    const mod = await import('libsodium-wrappers');
    await mod.ready;
    window._sodium = mod;
  }
  return window._sodium;
}

Strumieniowe AEAD przez crypto_secretstream

Do transferu dużych plików, crypto_secretstream_xchacha20poly1305 libsodium to najczystsze strumieniowe AEAD w przeglądarce:

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
);
// Ostatni fragment używa TAG_FINAL, by odbiorca mógł wykryć obcięcie
const chunkLast = sodium.crypto_secretstream_xchacha20poly1305_push(
  state, plaintextLast, null,
  sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
);

Marker TAG_FINAL pozwala odbiorcy wykryć ataki obcięcia — jeśli atakujący upuści końcowe fragmenty, deszyfrowanie po stronie klienta się nie powiedzie. AES-GCM Web Crypto nie ma tej właściwości; musiałbyś samodzielnie zbudować wykrywanie obcięcia (liczba fragmentów w AAD lub ogólny hash pliku weryfikowany po deszyfrowaniu).

Argon2 w przeglądarce

Dla wyprowadzania kluczy opartego na haśle w przeglądarce, Argon2id przez WASM to najlepsza praktyka 2026. Opcje:

  • argon2-browser: dedykowana biblioteka Argon2 WASM, ~200 KB
  • libsodium.js: Argon2id przez crypto_pwhash, jeśli już używasz libsodium
  • @noble/hashes: czysty JS Argon2id, ~15 KB w pakiecie, 3–5x wolniejszy niż WASM

Przykład z argon2-browser:

import argon2 from 'argon2-browser';
const result = await argon2.hash({
  pass: password,
  salt: salt, // Uint8Array, 16+ bajtów
  type: argon2.ArgonType.Argon2id,
  time: 3,
  mem: 65536, // KiB, więc 64 MiB
  parallelism: 4,
  hashLen: 32,
});
// result.hash to Uint8Array do użycia jako klucz AES-256

Kalibruj parametry do docelowego czasu oczekiwania użytkownika. Wartość bazowa OWASP 2024 (m=19 MiB, t=2, p=1) działa w ~300–500 ms. Silniejsza (m=64 MiB, t=3, p=4) działa w ~1–2 sekundy na nowoczesnym sprzęcie.

Post-kwantowa kryptografia przez WASM

Kyber (enkapsulacja kluczy) i Dilithium (podpisy) zostały standaryzowane przez NIST w 2024 roku jako ML-KEM i ML-DSA. Implementacje JavaScript istnieją (pq-crystals ma referencję), ale wersje WASM jak liboqs-js są szybsze i bardziej audytowalne.

Do transferu pliku, post-kwantowe KEM pozwalają na zawijanie symetrycznego klucza pliku za pomocą odpornego na ataki kwantowe prymitywu klucza publicznego. Schematy hybrydowe (ML-KEM + X25519) chronią zarówno przed klasycznymi, jak i przyszłymi kwantowymi atakującymi. Chrome wdrożył ML-KEM w uzgodnieniach TLS w wersji 116 (2023), ale WASM na poziomie aplikacji pozostaje ścieżką dla kluczy zawartości pliku.

Kwestie rozmiaru pakietu

Binarne pliki WASM są dostarczane jako część pakietu JS lub pobierane leniwie. Przybliżone rozmiary (skompresowane):

  • libsodium.js domyślny: 200 KB
  • libsodium.js sumo: 600 KB
  • argon2-browser: 200 KB
  • liboqs-js (post-kwantowy): ponad 1 MB

Dla strony docelowej szyfrującej w przeglądarce, 200–300 KB WASM jest tolerowalne gdy ładowane leniwie po interakcji użytkownika. Dla aplikacji, gdzie szyfrowanie jest podstawową funkcją, dostarczanie WASM przy pierwszym ładowaniu jest rozsądne. Używaj podpowiedzi rel="modulepreload" lub cachowania w service worker, by kolejne ładowania były natychmiastowe.

Sprawdź panel Sieć w narzędziach deweloperskich przeglądarki, by potwierdzić, że binarny WASM jest cachowany po pierwszym załadowaniu. Źle skonfigurowane nagłówki cache mogą powodować ponowne pobieranie przy każdej wizycie.

Overhead kompilacji i instancjowania

WebAssembly.instantiate() przetwarza i kompiluje plik binarny, co zajmuje 20–100 ms dla modułu 200 KB na desktopie, 100–500 ms na urządzeniach mobilnych. Dzieje się to raz na sesję. Cachuj skompilowany moduł w IndexedDB przez serializację WebAssembly.Module dla szybszego ładowania przy kolejnych wizytach.

WebAssembly.instantiateStreaming() zestrawia pobieranie i kompilację, oszczędzając 30–50% czasu startowania w porównaniu do instantiate() na pobranej odpowiedzi:

const response = fetch('/sodium.wasm');
const { instance } = await WebAssembly.instantiateStreaming(response, importObject);

Pułapki zarządzania pamięcią

Moduły WASM alokują pamięć w liniowych stronach (64 KiB każda). libsodium domyślnie startuje z małą ilością pamięci i rośnie w miarę potrzeb, ale nieograniczony wzrost może trafić na limity przeglądarki (zazwyczaj limit pamięci liniowej 4 GB w 32-bitowym WASM). Dla szyfrowania pliku wielogigabajtowego:

  • Przesyłaj fragmenty przez moduł WASM strumieniowo, nie ładuj całego pliku do pamięci WASM
  • Zwalniaj bufory po każdym fragmencie przez sodium.memzero lub nullowanie referencji
  • Monitoruj WebAssembly.Memory.buffer.byteLength w długotrwałych sesjach

Memory64 (propozycja, częściowo wdrożona w Chrome) usuwa limit 4 GB. Na razie przetwarzanie fragmentowane to niezawodna ścieżka.

Kwestie bezpieczeństwa specyficzne dla WASM

WASM działa w tym samym źródle co strona, która go załadowała, dziedzicząc te same reguły CSP i CORS. Jeśli atakujący wstrzyknie kod do Twojego źródła (XSS), może wywoływać eksporty Twojego modułu WASM. WASM nie zapewnia dodatkowej granicy bezpieczeństwa.

Gwarancje stałego czasu: większość bibliotek kryptograficznych skompilowanych do WASM zachowuje właściwości stałego czasu ze źródła C, ale translacja WASM-do-JIT może wprowadzać kod zmienny czasowo na niektórych platformach. Logi audytowe libsodium odnotowują kilka specyficznych dla WASM problemów ze stałym czasem. Dla większości modeli zagrożeń jest to pomijalny problem; dla obrony przed lokalnymi atakującymi z precyzyjnymi pomiarami czasu, uwzględnij tę lukę.

Praktyczny przepis

Dla aplikacji transferu pliku w 2026 roku:

  • Używaj Web Crypto dla AES-256-GCM, PBKDF2, HMAC, SHA-256, RSA-OAEP jeśli potrzebujesz RSA
  • Używaj libsodium.js przez WASM dla hashowania haseł Argon2id
  • Używaj libsodium.js do strumieniowego szyfrowania dużych plików (crypto_secretstream)
  • Używaj natywnego Ed25519/X25519 gdzie obsługiwane, w przeciwnym razie fall back na libsodium
  • Ładuj WASM leniwie po interakcji użytkownika, by utrzymać lekki początkowy pakiet
  • Testuj na docelowych urządzeniach; nie zakładaj, że liczby desktopowe odpowiadają urządzeniom mobilnym

HexaTransfer pozostaje przy Web Crypto AES-GCM dla masowego szyfrowania pliku, bo sprzętowo akcelerowana ścieżka jest najszybsza. WASM jest zarezerwowany dla algorytmów, których natywne API nie może dostarczyć.

Wypróbuj na hexatransfer.com — bezpłatnie, bez konta, do 10 GB.

Wysyłaj duże pliki bezpiecznie z szyfrowaniem end-to-end

Przesyłaj pliki do 10 GB za darmo z szyfrowaniem end-to-end. Bez rejestracji. Twoje pliki są szyfrowane w przeglądarce przed przesłaniem — nikt inny nie może ich odczytać.

Wyślij plik