Сквозное шифрование в веб-приложениях

Сквозное шифрование (End-to-End Encryption, E2EE) в веб-приложениях строится на принципе, при котором данные шифруются на стороне отправителя и расшифровываются только на стороне получателя. Сервер при этом выполняет роль транспортного узла и не имеет доступа к содержимому сообщений.

В контексте JavaScript-библиотеки SJCL (Stanford JavaScript Crypto Library) реализуются основные криптографические примитивы: симметричное шифрование AES, режимы аутентифицированного шифрования, хэш-функции, генерация случайных чисел, а также эллиптическая криптография для обмена ключами.


Криптографическая модель SJCL

SJCL построена вокруг нескольких фундаментальных компонентов:

  • sjcl.cipher.aes — реализация AES
  • sjcl.mode.ccm / ocb2 — режимы аутентифицированного шифрования
  • sjcl.hash.sha256 — криптографический хэш
  • sjcl.random — генератор криптографической случайности
  • sjcl.ecc.elGamal — эллиптическая криптография (ECDH / ECDSA)
  • sjcl.misc.pbkdf2 — вывод ключа из пароля

Основная концепция: все данные приводятся к bitArray — внутреннему формату представления битовых последовательностей.


Симметричное шифрование в SJCL

В большинстве сценариев E2EE используется AES в режиме CCM (Counter with CBC-MAC), обеспечивающий одновременно конфиденциальность и целостность.

Простейшее шифрование

const plaintext = "секретное сообщение";
const password = "сильный пароль";

const ciphertext = sjcl.encrypt(password, plaintext);
const decrypted = sjcl.decrypt(password, ciphertext);

Внутри sjcl.encrypt происходит:

  • генерация соли
  • вывод ключа через PBKDF2
  • генерация IV (инициализационного вектора)
  • шифрование AES-CCM
  • добавление MAC для проверки целостности

Режим CCM и защита целостности

CCM объединяет два механизма:

  • AES-CTR для шифрования
  • CBC-MAC для аутентификации

Ключевая особенность: невозможно изменить ciphertext без обнаружения подделки.

Структура зашифрованного объекта SJCL:

{
  "iv": "...",
  "v": 1,
  "iter": 10000,
  "ks": 128,
  "ct": "...",
  "salt": "...",
  "mode": "ccm",
  "adata": ""
}

Генерация ключей из пароля

Для пользовательских паролей применяется PBKDF2:

const key = sjcl.misc.pbkdf2(password, salt, iterations, keyLength);

Важные параметры:

  • salt — уникальная случайная соль
  • iterations — количество итераций (например, 10000–100000)
  • keyLength — длина ключа (128 / 256 бит)

PBKDF2 замедляет подбор пароля атакующим, увеличивая стоимость перебора.


Генерация криптографической случайности

Безопасность E2EE критически зависит от качества случайных чисел.

sjcl.random.addEntropy(window.crypto.getRandomValues(new Uint32Array(32)), 1024);
const randomWord = sjcl.random.randomWords(4);

SJCL использует несколько источников энтропии:

  • WebCrypto API
  • события мыши/клавиатуры
  • системные таймеры

Эллиптическая криптография для обмена ключами

Для безопасного обмена ключами используется ECDH (Elliptic Curve Diffie-Hellman).

Генерация пары ключей

const keys = sjcl.ecc.elGamal.generateKeys(256);
const publicKey = keys.pub;
const privateKey = keys.sec;

Обмен ключами

const sharedSecret = privateKey.dh(publicKeyOther);

Полученный sharedSecret используется как основа для симметричного ключа AES.


Модель сквозного шифрования в веб-приложении

Типовая архитектура E2EE в веб-приложении включает следующие этапы:

1. Генерация ключевой пары пользователя

Каждый пользователь создаёт:

  • ECC-пару (для обмена ключами)
  • или производный симметричный ключ (для простых сценариев)

2. Обмен публичными ключами

Публичные ключи передаются через сервер:

  • сервер не может восстановить приватные ключи
  • данные остаются защищёнными даже при компрометации сервера

3. Формирование общего секрета

Используется ECDH:

const shared = myPrivateKey.dh(remotePublicKey);

4. Производный ключ шифрования

Из общего секрета создаётся ключ AES:

const aesKey = sjcl.hash.sha256.hash(shared);

5. Шифрование сообщений

const encrypted = sjcl.encrypt(aesKey, message);

6. Расшифрование на стороне получателя

const decrypted = sjcl.decrypt(aesKey, encrypted);

Защита от атак

Перехват трафика

HTTPS защищает транспорт, но E2EE добавляет дополнительный уровень: даже сервер не видит содержимое.


MITM-атаки (Man-in-the-Middle)

Если подмена публичных ключей возможна, E2EE ломается.

Решения:

  • верификация ключей (fingerprint)
  • доверенные ключевые серверы
  • сигнатуры (ECDSA)

Повторное воспроизведение сообщений

CCM включает nonce (IV), предотвращающий повторное использование ciphertext.


Атаки перебора пароля

Защита обеспечивается:

  • PBKDF2 с высокой итерацией
  • уникальная соль
  • ограничение попыток на уровне приложения

Хранение ключей в браузере

В веб-приложениях ключи могут храниться:

  • в IndexedDB
  • в localStorage (не рекомендуется для приватных ключей)
  • в памяти (самый безопасный вариант)

Пример хранения:

localStorage.setItem("privateKey", JSON.stringify(sjcl.codec.base64.fromBits(privateKey)));

Работа с bitArray

SJCL использует внутренний тип bitArray, представляющий данные как массив 32-битных слов.

const bits = sjcl.codec.utf8String.toBits("data");
const str = sjcl.codec.utf8String.fromBits(bits);

Преобразования:

  • UTF-8 ↔︎ bitArray
  • Base64 ↔︎ bitArray
  • Hex ↔︎ bitArray

Производительность криптографии в браузере

SJCL не использует WebAssembly и полностью написан на JavaScript, что влияет на производительность:

  • PBKDF2 может быть медленным при высоких итерациях
  • ECC требует значительных вычислений
  • AES-CCM работает быстрее, но зависит от длины данных

Оптимизация достигается за счёт:

  • кеширования ключей
  • минимизации повторного шифрования
  • асинхронной обработки больших сообщений

Типичная схема E2EE чата

  1. Пользователь A и B генерируют ECC ключи
  2. Обмениваются публичными ключами через сервер
  3. Выполняют ECDH
  4. Получают общий секрет
  5. Производят AES-ключ
  6. Шифруют сообщения SJCL
  7. Сервер пересылает ciphertext без расшифровки

Роль сервера в модели SJCL E2EE

Сервер:

  • хранит зашифрованные данные
  • передаёт публичные ключи
  • маршрутизирует сообщения

Сервер не имеет:

  • доступа к ключам расшифровки
  • возможности восстановить plaintext
  • информации о содержимом сообщений

Ограничения подхода SJCL

Несмотря на функциональность, существуют ограничения:

  • отсутствие современного AEAD стандарта уровня libsodium
  • устаревшие режимы в некоторых конфигурациях
  • ограниченная поддержка современных curve-алгоритмов по сравнению с X25519
  • высокая нагрузка на CPU в браузере

Безопасная модель использования

Надёжность E2EE на SJCL зависит не только от криптографии, но и от архитектуры приложения:

  • контроль подмены ключей
  • защита от XSS (иначе приватные ключи украдут через DOM)
  • изоляция криптокода
  • минимизация хранения секретов

Основная уязвимость веб-E2EE чаще находится не в алгоритмах, а в слое приложения.