Сравнение с crypto.getRandomValues

Криптографическая случайность в JavaScript опирается на два принципиально разных подхода: системный источник энтропии, предоставляемый средой выполнения, и программно реализованные генераторы псевдослучайных чисел внутри библиотек общего назначения.

В современных браузерах и Node.js доступен интерфейс Web Crypto API, предоставляющий метод генерации криптографически стойких случайных значений:

crypto.getRandomValues(typedArray)

Этот метод заполняет переданный массив значениями, полученными из операционной системы. В основе лежит CSPRNG (Cryptographically Secure Pseudo-Random Number Generator), который использует системные источники энтропии: движения мыши, события устройства, аппаратные шумы и специализированные инструкции процессора.

Ключевая особенность — отсутствие предсказуемости. Даже при полном знании состояния программы восстановить последовательность значений невозможно.

Пример генерации случайного идентификатора:

const array = new Uint8Array(16);
crypto.getRandomValues(array);

const id = Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');

Особенности:

  • работает только с типизированными массивами (Uint8Array, Uint32Array и др.)
  • синхронный вызов
  • не требует дополнительных библиотек
  • реализуется на уровне платформы

В Node.js аналогичный интерфейс доступен через модуль crypto:

import { randomFillSync } from 'crypto';

const buffer = Buffer.alloc(16);
randomFillSync(buffer);

или через глобальный crypto.getRandomValues в новых версиях среды.


Генерация случайных значений в Crypto-js

Библиотека Crypto-js предоставляет собственный механизм генерации случайных данных:

CryptoJS.lib.WordArray.random(16)

На первый взгляд функциональность аналогична, однако источник случайности принципиально иной.

Внутри Crypto-js используется цепочка fallback-реализаций:

  • если доступен window.crypto — он используется
  • в противном случае может применяться Math.random()
  • возможны полифилы в старых окружениях

Критическая проблема заключается в том, что Math.random() не является криптографически стойким генератором. Он предназначен для моделирования и визуализации, но не для безопасности.

Пример:

const key = CryptoJS.lib.WordArray.random(32);

В зависимости от среды выполнения этот код может опираться либо на безопасный источник, либо на предсказуемый PRNG.


Сравнение источников случайности

Криптографическая стойкость

crypto.getRandomValues:

  • гарантированная криптографическая стойкость
  • используется системный CSPRNG
  • подходит для ключей, IV, токенов, nonce

Crypto-js:

  • зависит от окружения
  • может использовать небезопасный PRNG
  • не даёт гарантии непредсказуемости

Контроль над источником энтропии

Web Crypto API:

  • полностью контролируется операционной системой
  • не подвержен влиянию JavaScript-уровня
  • изолирован от состояния приложения

Crypto-js:

  • использует JavaScript-реализации
  • возможны деградации до Math.random()
  • зависит от конфигурации среды

Производительность

Генерация через crypto.getRandomValues выполняется на уровне нативного кода и обычно быстрее при больших объёмах данных.

Crypto-js реализует обёртки над массивами и дополнительную обработку WordArray, что увеличивает накладные расходы.


Форматы данных

crypto.getRandomValues:

  • работает только с TypedArray
const arr = new Uint8Array(32);
crypto.getRandomValues(arr);

Crypto-js:

  • использует собственный формат WordArray
const wa = CryptoJS.lib.WordArray.random(32);

Преобразование между форматами требует дополнительных операций:

const wordArray = CryptoJS.lib.WordArray.create(arr);

Практические последствия для безопасности

Использование случайных значений в криптографии затрагивает несколько критических областей:

  • генерация симметричных ключей
  • создание IV (Initialization Vector)
  • формирование salt для PBKDF2
  • генерация токенов сессий
  • nonce в протоколах шифрования

Любая предсказуемость в этих значениях приводит к компрометации всей криптосистемы.

Пример уязвимого подхода:

const salt = CryptoJS.lib.WordArray.random(16);

Если среда использует Math.random, атакующий может восстановить возможные значения соли и провести атаки на словарной основе или предвычисление ключей.

Корректный подход:

const salt = new Uint8Array(16);
crypto.getRandomValues(salt);

Различия в архитектуре безопасности

Web Crypto API проектировался как часть стандарта безопасных веб-операций. Он:

  • изолирован от JavaScript-движка
  • не имеет доступного состояния генератора
  • периодически обновляет внутреннюю энтропию
  • сертифицируется в рамках криптографических стандартов платформ

Crypto-js — это прикладная библиотека, ориентированная на совместимость и удобство, а не на гарантию системной безопасности.


Типичные ошибки при использовании Crypto-js

Наиболее распространённые проблемы:

  • использование WordArray.random для генерации ключей
  • применение случайных значений для JWT секретов
  • генерация IV без контроля качества энтропии
  • доверие к одинаковому поведению во всех окружениях

Особенно опасен сценарий кросс-платформенного кода, где браузер использует crypto, а серверная среда деградирует до Math.random.


Рекомендованные схемы интеграции

При использовании Crypto-js в современных приложениях корректная архитектура разделяет генерацию случайности и криптографические операции.

Генерация случайных данных:

const iv = new Uint8Array(16);
crypto.getRandomValues(iv);

Использование в Crypto-js:

const ivWordArray = CryptoJS.lib.WordArray.create(iv);

Далее IV используется в алгоритмах шифрования:

const encrypted = CryptoJS.AES.encrypt(message, key, {
    iv: ivWordArray
});

Поведение в разных средах выполнения

Среда crypto.getRandomValues Crypto-js random
Современный браузер CSPRNG ОС CSPRNG через crypto
Node.js ≥ 19 доступен глобально через crypto модуль
Старые браузеры отсутствует Math.random fallback
Изолированные окружения зависит от реализации непредсказуемо

Ключевое архитектурное различие

Системный генератор случайных чисел представляет собой внешний доверенный источник энтропии. Crypto-js, напротив, является внутренней реализацией, зависящей от качества окружения.

Это различие определяет границу между криптографически безопасным кодом и условно безопасным псевдослучайным поведением.

Использование crypto.getRandomValues формирует предсказуемую модель безопасности на уровне платформы, тогда как Crypto-js требует дополнительного контроля и проверки среды выполнения.