PHP: Как создать случайную уникальную буквенно-цифровую строку?
Как можно было бы генерировать случайную уникальную строку, используя цифры и буквы для использования в проверочной ссылке? Например, когда вы создаете учетную запись на веб-сайте, и она отправляет вам электронное письмо со ссылкой, и вам нужно нажать на эту ссылку, чтобы подтвердить свой аккаунт... да... один из них.
Как я могу создать один из тех, кто использует PHP?
Обновление: только что вспомнил о uniqid()
, Это функция PHP, которая генерирует уникальный идентификатор на основе текущего времени в микросекундах. Я думаю, что я буду использовать это.
35 ответов
Уведомление о безопасности. Это решение не следует использовать в ситуациях, когда качество вашей случайности может повлиять на безопасность приложения. Особенно,
rand()
а такжеuniqid()
не являются криптографически безопасными генераторами случайных чисел. См ответ Скотта для безопасной альтернативы.
Если вам не нужно, чтобы он был абсолютно уникальным со временем:
md5(uniqid(rand(), true))
В противном случае (если вы уже определили уникальный логин для вашего пользователя):
md5(uniqid($your_user_login, true))
Я просто смотрел, как решить эту проблему, но я также хочу, чтобы моя функция создавала токен, который также можно использовать для получения пароля. Это означает, что мне нужно ограничить способность токена угадываться. Так как uniqid
основывается на времени, и согласно php.net "возвращаемое значение мало отличается от microtime()",uniqid
не соответствует критериям. PHP рекомендует использоватьopenssl_random_pseudo_bytes()
вместо этого генерировать криптографически безопасные токены.
Быстрый, короткий и конкретный ответ:
bin2hex(openssl_random_pseudo_bytes($bytes))
который будет генерировать случайную строку буквенно-цифровых символов длиной = $ байтов * 2. К сожалению, это только алфавит[a-f][0-9]
, но это работает.
Ниже приведена самая сильная функция, которую я мог сделать, которая удовлетворяет критериям (это реализованная версия ответа Эрика).
function crypto_rand_secure($min, $max)
{
$range = $max - $min;
if ($range < 1) return $min; // not so random...
$log = ceil(log($range, 2));
$bytes = (int) ($log / 8) + 1; // length in bytes
$bits = (int) $log + 1; // length in bits
$filter = (int) (1 << $bits) - 1; // set all lower bits to 1
do {
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
$rnd = $rnd & $filter; // discard irrelevant bits
} while ($rnd > $range);
return $min + $rnd;
}
function getToken($length)
{
$token = "";
$codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
$codeAlphabet.= "0123456789";
$max = strlen($codeAlphabet); // edited
for ($i=0; $i < $length; $i++) {
$token .= $codeAlphabet[crypto_rand_secure(0, $max-1)];
}
return $token;
}
crypto_rand_secure($min, $max)
работает как капля для замены rand()
или же mt_rand
, Он использует openssl_random_pseudo_bytes, чтобы помочь создать случайное число между $min и $max.
getToken($length)
создает алфавит для использования в токене, а затем создает строку длины $length
,
РЕДАКТИРОВАТЬ: Я не упомянул источник - http://us1.php.net/manual/en/function.openssl-random-pseudo-bytes.php
РЕДАКТИРОВАТЬ (PHP7): С выпуском PHP7 стандартная библиотека теперь имеет две новые функции, которые могут заменить / улучшить / упростить функцию crypto_rand_secure, описанную выше. random_bytes($length)
а также random_int($min, $max)
http://php.net/manual/en/function.random-bytes.php
http://php.net/manual/en/function.random-int.php
Пример:
function getToken($length){
$token = "";
$codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
$codeAlphabet.= "0123456789";
$max = strlen($codeAlphabet); // edited
for ($i=0; $i < $length; $i++) {
$token .= $codeAlphabet[random_int(0, $max-1)];
}
return $token;
}
Объектно-ориентированная версия решения с наибольшим количеством голосов
Я создал объектно-ориентированное решение, основанное на ответе Скотта:
<?php
namespace Utils;
/**
* Class RandomStringGenerator
* @package Utils
*
* Solution taken from here:
* http://stackru.com/a/13733588/1056679
*/
class RandomStringGenerator
{
/** @var string */
protected $alphabet;
/** @var int */
protected $alphabetLength;
/**
* @param string $alphabet
*/
public function __construct($alphabet = '')
{
if ('' !== $alphabet) {
$this->setAlphabet($alphabet);
} else {
$this->setAlphabet(
implode(range('a', 'z'))
. implode(range('A', 'Z'))
. implode(range(0, 9))
);
}
}
/**
* @param string $alphabet
*/
public function setAlphabet($alphabet)
{
$this->alphabet = $alphabet;
$this->alphabetLength = strlen($alphabet);
}
/**
* @param int $length
* @return string
*/
public function generate($length)
{
$token = '';
for ($i = 0; $i < $length; $i++) {
$randomKey = $this->getRandomInteger(0, $this->alphabetLength);
$token .= $this->alphabet[$randomKey];
}
return $token;
}
/**
* @param int $min
* @param int $max
* @return int
*/
protected function getRandomInteger($min, $max)
{
$range = ($max - $min);
if ($range < 0) {
// Not so random...
return $min;
}
$log = log($range, 2);
// Length in bytes.
$bytes = (int) ($log / 8) + 1;
// Length in bits.
$bits = (int) $log + 1;
// Set all lower bits to 1.
$filter = (int) (1 << $bits) - 1;
do {
$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
// Discard irrelevant bits.
$rnd = $rnd & $filter;
} while ($rnd >= $range);
return ($min + $rnd);
}
}
использование
<?php
use Utils\RandomStringGenerator;
// Create new instance of generator class.
$generator = new RandomStringGenerator;
// Set token length.
$tokenLength = 32;
// Call method to generate random string.
$token = $generator->generate($tokenLength);
Пользовательский алфавит
Вы можете использовать пользовательский алфавит, если требуется. Просто передайте строку с поддерживаемыми символами конструктору или установщику:
<?php
$customAlphabet = '0123456789ABCDEF';
// Set initial alphabet.
$generator = new RandomStringGenerator($customAlphabet);
// Change alphabet whenever needed.
$generator->setAlphabet($customAlphabet);
Вот выходные образцы
SRniGU2sRQb2K1ylXKnWwZr4HrtdRgrM
q1sRUjNq1K9rG905aneFzyD5IcqD4dlC
I0euIWffrURLKCCJZ5PQFcNUCto6cQfD
AKwPJMEM5ytgJyJyGqoD5FQwxv82YvMr
duoRF6gAawNOEQRICnOUNYmStWmOpEgS
sdHUkEn4565AJoTtkc8EqJ6cC4MLEHUx
eVywMdYXczuZmHaJ50nIVQjOidEVkVna
baJGt7cdLDbIxMctLsEBWgAw5BByP5V0
iqT0B2obq3oerbeXkDVLjZrrLheW4d8f
OUQYCny6tj2TYDlTuu1KsnUyaLkeObwa
Надеюсь, это кому-нибудь поможет. Ура!
Я опоздал, но я здесь с некоторыми хорошими исследовательскими данными, основанными на функциях, предоставленных ответом Скотта. Поэтому я настроил каплю Digital Ocean только для этого 5-дневного автоматического теста и сохранил сгенерированные уникальные строки в базе данных MySQL.
В течение этого тестового периода я использовал 5 разных длин (5, 10, 15, 20, 50) и для каждой длины было вставлено +/-0,5 миллиона записей. Во время моего теста только длина 5 генерировала дубликаты +/-3K из 0,5 миллиона, а оставшиеся длины не генерировали дубликатов. Таким образом, мы можем сказать, что если мы используем длину 15 или выше с функциями Скотта, то мы можем генерировать высоконадежные уникальные строки. Вот таблица с моими данными исследований:
Надеюсь, это поможет.
Вы можете использовать UUID(универсальный уникальный идентификатор), его можно использовать для любых целей, от строки аутентификации пользователя до идентификатора платежной транзакции.
UUID - это 16-октетное (128-битное) число. В своей канонической форме UUID представлен 32 шестнадцатеричными цифрами, отображаемыми в пяти группах, разделенных дефисами, в форме 8-4-4-4-12, в общей сложности 36 символов (32 буквенно-цифровых символа и четыре дефиса).
function generate_uuid() {
return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
mt_rand( 0, 0xffff ),
mt_rand( 0, 0x0C2f ) | 0x4000,
mt_rand( 0, 0x3fff ) | 0x8000,
mt_rand( 0, 0x2Aff ), mt_rand( 0, 0xffD3 ), mt_rand( 0, 0xff4B )
);
}
// вызов функции
$transationID = generate_uuid();
некоторые примеры выходных данных будут выглядеть так:
E302D66D-87E3-4450-8CB6-17531895BF14
22D288BC-7289-442B-BEEA-286777D559F2
51B4DE29-3B71-4FD2-9E6C-071703E1FF31
3777C8C6-9FF5-4C78-AAA2-08A47F555E81
54B91C72-2CF4-4501-A6E9-02A60DCBAE4C
60F75C7C-1AE3-417B-82C8-14D456542CD7
8DE0168D-01D3-4502-9E59-10D665CEBCB2
надеюсь, это поможет кому-то в будущем:)
Эта функция сгенерирует случайный ключ, используя цифры и буквы:
function random_string($length) {
$key = '';
$keys = array_merge(range(0, 9), range('a', 'z'));
for ($i = 0; $i < $length; $i++) {
$key .= $keys[array_rand($keys)];
}
return $key;
}
echo random_string(50);
Пример вывода:
zsd16xzv3jsytnp87tk7ygv73k8zmr0ekh6ly7mxaeyeh46oe8
Я использую этот однострочник:
base64_encode(openssl_random_pseudo_bytes(3 * ($length >> 2)));
где длина - длина желаемой строки (делится на 4, в противном случае она округляется до ближайшего числа, кратного 4)
Используйте приведенный ниже код, чтобы сгенерировать случайное число из 11 символов или изменить число в соответствии с вашими требованиями.
$randomNum=substr(str_shuffle("0123456789abcdefghijklmnopqrstvwxyz"), 0, 11);
or we can use custom function to generate the random number
function randomNumber($length){
$numbers = range(0,9);
shuffle($numbers);
for($i = 0;$i < $length;$i++)
$digits .= $numbers[$i];
return $digits;
}
//generate random number
$randomNum=randomNumber(11);
Для действительно случайных строк вы можете использовать
<?php
echo md5(microtime(true).mt_Rand());
выходы:
40a29479ec808ad4bcff288a48a25d5c
так что даже если вы попытаетесь сгенерировать строку несколько раз в одно и то же время, вы получите другой вывод.
- Генерация случайного числа с помощью вашего любимого генератора случайных чисел
- Умножьте и разделите его, чтобы получить число, соответствующее количеству символов в вашем алфавите кода
- Получить элемент по этому индексу в вашем кодовом алфавите.
- Повторите с 1), пока у вас есть длина, которую вы хотите
например (в псевдокоде)
int myInt = random(0, numcharacters)
char[] codealphabet = 'ABCDEF12345'
char random = codealphabet[i]
repeat until long enough
Это простая функция, которая позволяет генерировать случайные строки, содержащие буквы и цифры (буквенно-цифровые). Вы также можете ограничить длину строки. Эти случайные строки могут быть использованы для различных целей, в том числе: код реферала, промокод, код купона. Функция опирается на следующие функции PHP:base_convert, sha1, uniqid, mt_rand
function random_code($length)
{
return substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, $length);
}
echo random_code(6);
/*sample output
* a7d9e8
* 3klo93
*/
При попытке создать случайный пароль вы пытаетесь:
Сначала создайте криптографически безопасный набор случайных байтов
Во-вторых, превращение этих случайных байтов в строку для печати
Теперь есть несколько способов генерировать случайные байты в php, например:
$length = 32;
//PHP 7+
$bytes= random_bytes($length);
//PHP < 7
$bytes= openssl_random_pseudo_bytes($length);
Затем вы хотите превратить эти случайные байты в печатную строку:
Вы можете использовать bin2hex:
$string = bin2hex($bytes);
или base64_encode:
$string = base64_encode($bytes);
Однако обратите внимание, что вы не контролируете длину строки, если используете base64. Вы можете использовать bin2hex, чтобы сделать это, использование 32 байтов превратится в строку из 64 символов. Но это будет работать только так в строке EVEN.
Вот что я использую:
md5(time() . rand());
// Creates something like 0c947c3b1047334f5bb8a3b7adc1d97b
Простой однострочный хэш-генератор строки, например
77zd43-7bc495-64cg9a-535548
(1 байт = 2 символа)
$hash = implode('-', [
bin2hex(random_bytes(3)),
bin2hex(random_bytes(3)),
bin2hex(random_bytes(3)),
bin2hex(random_bytes(3)),
]);
Вот вам уникальный генератор идентификаторов. сделано мной.
<?php
$d=date ("d");
$m=date ("m");
$y=date ("Y");
$t=time();
$dmt=$d+$m+$y+$t;
$ran= rand(0,10000000);
$dmtran= $dmt+$ran;
$un= uniqid();
$dmtun = $dmt.$un;
$mdun = md5($dmtran.$un);
$sort=substr($mdun, 16); // if you want sort length code.
echo $mdun;
?>
Вы можете повторить любой "var" для вашего идентификатора, как вам нравится. но $mdun лучше, вы можете заменить md5 на sha1 для лучшего кода, но это будет очень долго, что может вам и не понадобиться.
Спасибо.
Этот народ захлебывается стаканом воды ...
$random= substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*.-_"), 0, 10);
Простой. Возможность повторения этой случайной строки составляет 0,000000000000000000000000000001^70
Если вы хотите сгенерировать уникальную строку в PHP, попробуйте следующее.
md5(uniqid().mt_rand());
В этом,
uniqid()
- Он будет генерировать уникальную строку. Эта функция возвращает уникальный идентификатор на основе метки времени в виде строки.
mt_rand()
- Генерация случайного числа.
md5()
- Он сгенерирует хеш-строку.
function random_string($length = 8) {
$alphabets = range('A','Z');
$numbers = range('0','9');
$additional_characters = array('_','=');
$final_array = array_merge($alphabets,$numbers,$additional_characters);
while($length--) {
$key = array_rand($final_array);
$password .= $final_array[$key];
}
if (preg_match('/[A-Za-z0-9]/', $password))
{
return $password;
}else{
return random_string();
}
}
Мне нравится использовать хэш-ключи при работе с проверочными ссылками. Я бы порекомендовал использовать microtime и использовать хеширование с использованием MD5, поскольку не должно быть никаких причин, по которым ключи должны совпадать, поскольку хэши основаны на микротайме.
$key = md5(rand());
$key = md5(microtime());
<?php
function generateRandomString($length = 11) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
?>
Выше функция генерирует случайную строку длиной 11 символов.
function codeGenerate() {
$randCode = (string)mt_rand(1000,9999);
$randChar = rand(65,90);
$randInx = rand(0,3);
$randCode[$randInx] = chr($randChar);
return $randCode;
}
echo codeGenerate();
ВЫХОД
38I7
33V7
E836
736U
Простое решение - преобразовать базу 64 в буквенно-цифровую, отбросив не буквенно-цифровые символы.
Здесь используется random_bytes() для криптографически безопасного результата.
function random_alphanumeric(int $length): string
{
$result='';
do
{
//Base 64 produces 4 characters for each 3 bytes, so most times this will give enough bytes in a single pass
$bytes=random_bytes(($length+3-strlen($result))*2);
//Discard non-alhpanumeric characters
$result.=str_replace(['/','+','='],['','',''],base64_encode($bytes));
//Keep adding characters until the string is long enough
//Add a few extra because the last 2 or 3 characters of a base 64 string tend to be less diverse
}while(strlen($result)<$length+3);
return substr($result,0,$length);
}
После прочтения предыдущих примеров я придумал это:
protected static $nonce_length = 32;
public static function getNonce()
{
$chars = array();
for ($i = 0; $i < 10; $i++)
$chars = array_merge($chars, range(0, 9), range('A', 'Z'));
shuffle($chars);
$start = mt_rand(0, count($chars) - self::$nonce_length);
return substr(join('', $chars), $start, self::$nonce_length);
}
Я 10 раз продублирую массив [0-9,AZ] и перетасую элементы, после того как я получаю случайную начальную точку для substr(), чтобы быть более "креативной":), вы можете добавить [az] и другие элементы в массив, дублировать более или менее, будь более креативным, чем я
Я всегда использую эту функцию для генерации произвольной случайной буквенно-цифровой строки... Надеюсь, это поможет.
<?php
function random_alphanumeric($length) {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345689';
$my_string = '';
for ($i = 0; $i < $length; $i++) {
$pos = mt_rand(0, strlen($chars) -1);
$my_string .= substr($chars, $pos, 1);
}
return $my_string;
}
echo random_alphanumeric(50); // 50 characters
?>
Он генерирует, например: Y1FypdjVbFCFK6Gh9FDJpe6dciwJEfV6MQGpJqAfuijaYSZ86g
Если вы хотите сравнить с другой строкой, чтобы убедиться, что это уникальная последовательность, вы можете использовать этот трюк...
$string_1 = random_alphanumeric(50);
$string_2 = random_alphanumeric(50);
while ($string_1 == $string_2) {
$string_1 = random_alphanumeric(50);
$string_2 = random_alphanumeric(50);
if ($string_1 != $string_2) {
break;
}
}
echo $string_1;
echo "<br>\n";
echo $string_2;
он генерирует две уникальные строки:
qsBDs4JOoVRfFxyLAOGECYIsWvpcpMzAO9pypwxsqPKeAmYLOi
Ti3kE1WfGgTNxQVXtbNNbhhvvapnaUfGMVJecHkUjHbuCb85pF
Надеюсь, это то, что вы ищете...
можно использовать этот код. я тестировал 35 000 000 идентификаторов без дубликатов.
<?php
$characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_';
$result = '';
for ($i = 0; $i < 11; $i++)
$result .= $characters[mt_rand(0, 63)];?>
вы можете изменить его по своему усмотрению. и если у вас есть какие-либо предложения, не стесняйтесь комментировать. Рекомендуется, чтобы вы проверили каждый идентификатор в своей базе данных, прежде чем использовать эти идентификаторы, сделав так, чтобы у вас были 100% уникальные идентификаторы в вашей базе данных.
Я добавил несколько специальных символов в ответ Скота, чтобы токен был более безопасным:
function getToken($length){
$token = "";
$codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
$codeAlphabet.= "0123456789";
$codeAlphabet.= "-_.~"; // Special characters allowed in url
$max = strlen($codeAlphabet);
for ($i=0; $i < $length; $i++) {
$token .= $codeAlphabet[random_int(0, $max-1)];
}
return $token;
}
согласно https://developers.google.com/maps/documentation/urls/url-encoding:
эти специальные символы (-_.~
) разрешены в URL-адресе, поэтому вы также можете использовать токен в качестве имени файла, но если вы просто хотите использовать токен в качестве значения параметра в URL-адресе, вы можете использовать больше специальных символов.
Я уверен что ты не можешь использовать & # +
в значении параметра, но я тестировал следующие символы и может использоваться:
!@$%^*()-_.~
Вот забавный
public function randomStr($length = 16) {
$string = '';
while (($len = strlen($string)) < $length) {
$size = $length - $len;
$bytes = random_bytes($size);
$string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
}
return $string;
}
Украдено из laravel здесь
Я думаю, что это лучший метод для использования.
str_shuffle(md5(rand(0,100000)))
Скотт, да ты очень пишешь и хорошее решение! Благодарю.
Я также должен создать уникальный токен API для каждого моего пользователя. Вот мой подход, я использовал информацию о пользователе (Userid и Username):
public function generateUniqueToken($userid, $username){
$rand = mt_rand(100,999);
$md5 = md5($userid.'!(&^ 532567_465 ///'.$username);
$md53 = substr($md5,0,3);
$md5_remaining = substr($md5,3);
$md5 = $md53. $rand. $userid. $md5_remaining;
return $md5;
}
Пожалуйста, посмотрите и дайте мне знать, если я смогу улучшить. Спасибо