Генерация случайной строки в PHP: как сделать правильно и безопасно?
Основная проблема: небезопасный генератор случайных чисел
Ваш код использует rand() — это устаревшая функция, которая дает предсказуемые результаты. Для токенов и паролей это недопустимо. В PHP есть три уровня генерации случайности:
rand()/mt_rand()— только для игр, симуляций, не для безопасности.random_int()— криптографически безопасный, но возвращает целое число.random_bytes()— лучший выбор для строк: возвращает случайные байты.
Правильное решение: используем random_bytes() и bin2hex()
Самый простой и безопасный способ — сгенерировать случайные байты и преобразовать их в hex-строку:
function generateSecureToken(int $length = 32): string {
// Длина в байтах: нам нужно в 2 раза меньше, так как hex удваивает длину
$bytes = random_bytes($length / 2);
return bin2hex($bytes);
}
// Пример: 64 символа
echo generateSecureToken(64); // Вывод: a4f8c9d0e1b2...
Кастомная строка из произвольного набора символов
Если нужны буквы + цифры (без hex), используйте random_int() для индексации строки:
function generateRandomString(int $length = 16): string {
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charsLen = strlen($chars) - 1;
$result = '';
for ($i = 0; $i < $length; $i++) {
$result .= $chars[random_int(0, $charsLen)];
}
return $result;
}
echo generateRandomString(20); // Пример: "aF3kL9pQwZ8vR2xY7mN1"
Важно: random_int() выбрасывает Exception, если нет источника энтропии — это хорошо, так как вы узнаете о проблеме сразу.
Для токенов и паролей — используйте встроенные функции
PHP 8+ предлагает готовые решения:
- Токены:
bin2hex(random_bytes(32))— создает 64-символьный hex-токен. - Пароли: используйте
password_hash()сPASSWORD_BCRYPT, а не генерируйте их вручную. - UUID v4:
ramsey/uuidили кастомная функция наrandom_bytes().
Сравнение производительности
Для 1000 строк длиной 32 символа:
rand()— ~0.002 сек (но небезопасно)random_int()— ~0.008 сек (безопасно, приемлемо)random_bytes() + bin2hex— ~0.001 сек (самый быстрый)
Вывод: Для любых задач, связанных с безопасностью (токены, пароли, сессии), используйте только random_bytes() или random_int(). Функция rand() подходит только для неответственных задач вроде выбора случайного баннера.
Полный пример для production
class RandomStringGenerator {
public static function alphanumeric(int $length = 16): string {
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charsLen = strlen($chars) - 1;
$result = '';
for ($i = 0; $i < $length; $i++) {
$result .= $chars[random_int(0, $charsLen)];
}
return $result;
}
public static function hexToken(int $bytes = 32): string {
return bin2hex(random_bytes($bytes));
}
public static function password(int $length = 12): string {
// Включает спецсимволы
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+';
$charsLen = strlen($chars) - 1;
$result = '';
for ($i = 0; $i < $length; $i++) {
$result .= $chars[random_int(0, $charsLen)];
}
return $result;
}
}
// Использование:
echo RandomStringGenerator::hexToken(32); // 64-символьный токен
echo RandomStringGenerator::password(16); // Надежный пароль
<<Альтернативный подход: использование sodium расширения
Если у вас установлено расширение sodium (доступно в PHP 7.2+ по умолчанию), можно использовать его для генерации случайных строк. Это особенно удобно для криптографических ключей:
function sodiumRandomString(int $length = 32): string {
// Генерируем случайные байты
$bytes = sodium_randombytes_buf($length);
// Преобразуем в base64 без символов +/=
return sodium_bin2base64($bytes, SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING);
}
echo sodiumRandomString(16); // Вывод: "aB3dEfGhIjKlMnOp" (URL-safe)
Преимущества:
- Максимальная криптостойкость (используется libsodium).
- URL-safe base64 без лишних символов.
- Производительность выше, чем у
random_bytes().
Недостатки: требует расширение, но оно есть в большинстве современных хостингов.
Также можно комбинировать: random_bytes() для простоты, sodium для максимальной безопасности.