Генерация случайных данных в контексте безопасности отличается от обычной псевдослучайности, используемой в математических библиотеках или игровых алгоритмах. Основное требование здесь — непредсказуемость. В WebCrypto API для этой цели используется источник криптографически стойкой энтропии, предоставляемый окружением браузера.
Ключевой механизм — метод crypto.getRandomValues(),
входящий в интерфейс Crypto. Он заполняет заранее созданный
буфер случайными значениями, полученными из системного генератора
случайных чисел операционной системы.
crypto.getRandomValuesМетод работает с типизированными массивами, что позволяет строго контролировать структуру выходных данных.
const array = new Uint8Array(16);
crypto.getRandomValues(array);
console.log(array);
В этом примере создаётся массив из 16 байт, каждый из которых заполняется случайным значением от 0 до 255.
Поддерживаемые типы:
Int8ArrayUint8ArrayInt16ArrayUint16ArrayInt32ArrayUint32ArrayНе допускаются типы с плавающей точкой (Float32Array,
Float64Array), так как их формат не гарантирует
равномерного распределения битовой энтропии.
На практике часто требуется токен для сессий, CSRF-защиты или временной авторизации. Один из распространённых подходов — преобразование байтов в строку.
function generateHexToken(length = 32) {
const array = new Uint8Array(length / 2);
crypto.getRandomValues(array);
return Array.from(array, byte =>
byte.toString(16).padStart(2, '0')
).join('');
}
const token = generateHexToken(32);
console.log(token);
Особенности:
lengthHex-формат не всегда эффективен по размеру. Более компактное представление — Base64.
function generateBase64Token(byteLength = 32) {
const array = new Uint8Array(byteLength);
crypto.getRandomValues(array);
let binary = '';
for (let i = 0; i < array.length; i++) {
binary += String.fromCharCode(array[i]);
}
return btoa(binary);
}
const token = generateBase64Token(32);
console.log(token);
Особенности:
btoa, работающего со строками бинарного
представления+, /,
=Для URL-safe варианта обычно применяется замена символов:
function base64Url(token) {
return token
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
Современные браузеры поддерживают встроенный метод:
const id = crypto.randomUUID();
console.log(id);
Формат результата соответствует UUID v4:
xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
Особенности:
UUID используется в:
getRandomValuesНесмотря на наличие встроенного метода, полезно понимать структуру UUID.
function generateUUID() {
const bytes = new Uint8Array(16);
crypto.getRandomValues(bytes);
// версия 4
bytes[6] = (bytes[6] & 0x0f) | 0x40;
// variant RFC4122
bytes[8] = (bytes[8] & 0x3f) | 0x80;
const hex = Array.from(bytes, b =>
b.toString(16).padStart(2, '0')
);
return (
hex.slice(0, 4).join('') + '-' +
hex.slice(4, 6).join('') + '-' +
hex.slice(6, 8).join('') + '-' +
hex.slice(8, 10).join('') + '-' +
hex.slice(10, 16).join('')
);
}
console.log(generateUUID());
Структура UUID:
При работе с WebCrypto часто допускаются критические ошибки:
Math.random()const id = Math.random().toString(36);
Недостатки:
const buffer = new Uint8Array(16);
function badToken() {
return crypto.getRandomValues(buffer);
}
Проблема:
Правильный подход — создавать новый буфер каждый раз.
String.fromCharCode(byte)
Используется только как промежуточный шаг. Нельзя интерпретировать байты как UTF-16 символы напрямую для криптографических целей.
crypto.getRandomValues() работает синхронно и
оптимизирован на уровне браузера и ОС.
Сравнение:
Math.random() — быстрее, но небезопасноgetRandomValues() — безопасно, но чуть дороже по
стоимости вызоваПрактически разница заметна только при массовой генерации миллионов значений.
Иногда требуется ограниченный набор символов (например, для пользовательских кодов подтверждения).
function generateToken(length = 10) {
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
const array = new Uint8Array(length);
crypto.getRandomValues(array);
return Array.from(array, x =>
chars[x % chars.length]
).join('');
}
console.log(generateToken());
Особенности:
Сессионные токены требуют:
Типичная схема:
function sessionId() {
const bytes = new Uint8Array(24);
crypto.getRandomValues(bytes);
return btoa(String.fromCharCode(...bytes))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
Размер 24 байта даёт 192 бита энтропии, что достаточно для большинства веб-приложений.
Хотя getRandomValues() формально не является
криптографической функцией шифрования или подписи, он является частью
Web Crypto API и служит фундаментом для:
При проектировании идентификаторов учитываются:
Случайные значения часто используются как:
Пример nonce для AES-GCM:
const iv = crypto.getRandomValues(new Uint8Array(12));
12 байт — рекомендуемый размер для GCM-режима.