Генерация случайного пароля в php - PullRequest
171 голосов
/ 23 мая 2011

Я пытаюсь сгенерировать случайный пароль в php.

Однако я получаю все 'а', и возвращаемый тип имеет тип массив, и я хотел бы, чтобы это была строкаЛюбые идеи о том, как исправить код?

Спасибо.

function randomPassword() {
    $alphabet = "abcdefghijklmnopqrstuwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789";
    for ($i = 0; $i < 8; $i++) {
        $n = rand(0, count($alphabet)-1);
        $pass[$i] = $alphabet[$n];
    }
    return $pass;
}

Ответы [ 24 ]

238 голосов
/ 23 мая 2011

Предупреждение безопасности : rand() не является криптографически безопасным генератором псевдослучайных чисел. Найдите в другом месте генерирующую криптографически безопасную псевдослучайную строку в PHP .

Попробуйте это (используйте strlen вместо count, потому что count в строке всегда 1):

function randomPassword() {
    $alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
    $pass = array(); //remember to declare $pass as an array
    $alphaLength = strlen($alphabet) - 1; //put the length -1 in cache
    for ($i = 0; $i < 8; $i++) {
        $n = rand(0, $alphaLength);
        $pass[] = $alphabet[$n];
    }
    return implode($pass); //turn the array into a string
}

Демо: http://codepad.org/UL8k4aYK

107 голосов
/ 08 июля 2015

TL; DR:

  • Используйте random_int() и приведенное ниже random_str().
  • Если у вас нет random_int(), используйте random_compat .

Объяснение:

Поскольку вы генерируете пароль , вам необходимо убедиться, что сгенерированный вами пароль является непредсказуемым и единственным способомубедитесь, что это свойство присутствует в вашей реализации, чтобы использовать криптографически безопасный генератор псевдослучайных чисел (CSPRNG).

Требование для CSPRNG может быть смягчено для общегослучай случайных строк, но не когда речь идет о безопасности.

Простой, безопасный и правильный ответ для генерации пароля в PHP - использовать RandomLib и не изобретать колесо.Эта библиотека была проверена как экспертами в области безопасности, так и мной.

Для разработчиков, которые предпочитают изобретать собственное решение, PHP 7.0.0 предоставит random_int() для этой цели.Если вы все еще используете PHP 5.x, мы написали PHP 5 polyfill для random_int(), чтобы вы могли использовать новый API до выхода PHP 7.Использование нашего random_int() polyfill , вероятно, безопаснее, чем написание вашей собственной реализации.

С безопасным генератором случайных целых чисел под рукой создание безопасной случайной строки проще, чем pie:

<?php
/**
 * Generate a random string, using a cryptographically secure 
 * pseudorandom number generator (random_int)
 * 
 * For PHP 7, random_int is a PHP core function
 * For PHP 5.x, depends on https://github.com/paragonie/random_compat
 * 
 * @param int $length      How many characters do we want?
 * @param string $keyspace A string of all possible characters
 *                         to select from
 * @return string
 */
function random_str(
    $length,
    $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
) {
    $str = '';
    $max = mb_strlen($keyspace, '8bit') - 1;
    if ($max < 1) {
        throw new Exception('$keyspace must be at least two characters long');
    }
    for ($i = 0; $i < $length; ++$i) {
        $str .= $keyspace[random_int(0, $max)];
    }
    return $str;
}
103 голосов
/ 01 февраля 2014

Я знаю, что вы пытаетесь сгенерировать свой пароль особым образом, но вы, возможно, захотите посмотреть и на этот метод ...

$bytes = openssl_random_pseudo_bytes(2);

$pwd = bin2hex($bytes);

Он взят с сайта php.net и онсоздает строку, которая в два раза длиннее числа, которое вы помещаете в функцию openssl_random_pseudo_bytes.Таким образом, приведенное выше создаст пароль длиной 4 символа.

Короче ...

$pwd = bin2hex(openssl_random_pseudo_bytes(4));

Будет создан пароль длиной 8 символов.

Обратите внимание, что парольсодержит только цифры 0-9 и строчные буквы af!

52 голосов
/ 31 августа 2012

Крошечный код с 2 строкой.

demo: http://codepad.org/5rHMHwnH

function rand_string( $length ) {

    $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    return substr(str_shuffle($chars),0,$length);

}

echo rand_string(8);

с помощью rand_string вы можете определить, сколько символов будет создано.

36 голосов
/ 11 декабря 2012

В одну строку:

substr(str_shuffle('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') , 0 , 10 )
36 голосов
/ 23 мая 2011

Если вы используете PHP7, вы можете использовать функцию random_int():

function generate_password($length = 20){
  $chars =  'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.
            '0123456789`-=~!@#$%^&*()_+,./<>?;:[]{}\|';

  $str = '';
  $max = strlen($chars) - 1;

  for ($i=0; $i < $length; $i++)
    $str .= $chars[random_int(0, $max)];

  return $str;
}

Старый ответ ниже:

function generate_password($length = 20){
  $chars =  'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.
            '0123456789`-=~!@#$%^&*()_+,./<>?;:[]{}\|';

  $str = '';
  $max = strlen($chars) - 1;

  for ($i=0; $i < $length; $i++)
    $str .= $chars[mt_rand(0, $max)];

  return $str;
}
26 голосов
/ 06 июля 2015

Ваша лучшая ставка - это библиотека RandomLib от ircmaxell .

Пример использования:

$factory = new RandomLib\Factory;
$generator = $factory->getGenerator(new SecurityLib\Strength(SecurityLib\Strength::MEDIUM));

$passwordLength = 8; // Or more
$randomPassword = $generator->generateString($passwordLength);

Он генерирует строки, которые более строго случайны, чем обычные функции случайности, такие как shuffle() и rand() (это то, что вам обычно нужно для конфиденциальной информации, такой как пароли, соли и ключи).

14 голосов
/ 11 июля 2015

Я собираюсь опубликовать ответ, потому что некоторые из существующих ответов близки, но имеют один из:

  • меньше места для символов, чем вы хотели, так что или грубое принуждение проще, или пароль должен быть длиннее для той же энтропии
  • a RNG , который не считается криптографически безопасным
  • требование для какой-либо сторонней библиотеки, и я подумал, что было бы интересно показать, что нужно сделать, чтобы сделать это самостоятельно

Этот ответ обойдет проблему count/strlen, поскольку безопасность сгенерированного пароля, по крайней мере, ИМХО, выходит за пределы того, как вы туда попадаете. Я также собираюсь предположить, что PHP> 5.3.0.

Давайте разберем проблему на составные части:

  1. использовать какой-нибудь безопасный источник случайности для получения случайных данных
  2. используйте эти данные и представьте их в виде печатной строки

Для первой части PHP> 5.3.0 предоставляет функцию openssl_random_pseudo_bytes. Обратите внимание, что хотя в большинстве систем используется криптографически стойкий алгоритм, вы должны проверить, чтобы мы использовали оболочку:

/**
 * @param int $length
 */
function strong_random_bytes($length)
{
    $strong = false; // Flag for whether a strong algorithm was used
    $bytes = openssl_random_pseudo_bytes($length, $strong);

    if ( ! $strong)
    {
        // System did not use a cryptographically strong algorithm 
        throw new Exception('Strong algorithm not available for PRNG.');
    }        

    return $bytes;
}

Для второй части мы будем использовать base64_encode, поскольку она принимает строку байтов и создает последовательность символов, алфавит которых очень близок к алфавиту, указанному в исходном вопросе. Если мы не возражаем против появления символов +, / и = в конечной строке, и мы хотим, чтобы результат был длиной не менее $n символов, мы могли бы просто использовать:

base64_encode(strong_random_bytes(intval(ceil($n * 3 / 4))));

Коэффициент 3/4 обусловлен тем фактом, что в результате кодирования base64 получается строка, длина которой как минимум на треть больше, чем строка байта. Результат будет точным для $n, кратным 4 и до 3 символов длиннее в противном случае. Так как дополнительные символы являются преимущественно символом заполнения =, если у нас по какой-то причине было ограничение, что пароль должен быть точной длины, то мы можем урезать его до желаемой длины. Это особенно связано с тем, что для данного $n все пароли должны заканчиваться одинаковым количеством паролей, поэтому у злоумышленника, имеющего доступ к итоговому паролю, будет угадать до 2 символов меньше.


Для дополнительного кредита, если бы мы хотели выполнить точную спецификацию, как в вопросе ОП, нам пришлось бы сделать немного больше работы. Я собираюсь отказаться от базового подхода к конвертации и перейти к быстрому и грязному. Оба должны генерировать больше случайности, чем будет использовано в результате в любом случае из-за длинного алфавита из 62 записей.

Для дополнительных символов в результате мы можем просто отбросить их из полученной строки. Если мы начнем с 8 байтов в нашей байтовой строке, то примерно 25% символов base64 будут этими «нежелательными» символами, так что простое отбрасывание этих символов приводит к тому, что строка не короче, чем хотел OP. Тогда мы можем просто обрезать его, чтобы получить точную длину:

$dirty_pass = base64_encode(strong_random_bytes(8)));
$pass = substr(str_replace(['/', '+', '='], ['', '', ''], $dirty_pass, 0, 8);

Если вы генерируете более длинные пароли, символ заполнения = образует все меньшую и меньшую пропорцию промежуточного результата, так что вы можете реализовать более гибкий подход, если использование ресурсов энтропии, используемых для PRNG, является проблемой.

14 голосов
/ 23 мая 2011

Вы хотите strlen($alphabet), а не count константы alphabet (эквивалентно 'alphabet').

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

Вместо этого читайте из /dev/urandom, чтобы получить криптографически случайные данные.

11 голосов
/ 23 мая 2011

Это в основном то же самое, что и намного проще substr(md5(uniqid()), 0, 8);

...