在php中生成随机密码

nun*_*nos 176 php security random passwords

我想在php中生成一个随机密码.

但是我得到所有'a'并且返回类型是数组类型,我希望它是一个字符串.有关如何更正代码的任何想法?

谢谢.

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

Nea*_*eal 240

安全警告: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
}
Run Code Online (Sandbox Code Playgroud)

演示:http://codepad.org/UL8k4aYK

  • 我已经在生产中看到了你的不安全代码**并希望在源头停止它.你需要**加密安全随机密码. (34认同)
  • 使用*rand*生成*密码*是一个非常糟糕的主意.这不是一个安全的PRNG.(并且没有'mt_rand`也不是更好) (33认同)
  • 似乎更直接使用`$ pass.= $ alphabet [$ n]`. (22认同)
  • 问题是*生成密码*.生成密码的代码显然需要使用安全的随机数. (19认同)
  • 出于同样的原因,因为这不是**重复的问题,这个答案是不正确的,因为问题是关于_generating password_和**而不是**_random string_.这个答案提供了一种非常不安全的方法来生成密码.请使用下面的@ user3260409的答案,其中使用`openssl_random_pseudo_bytes()`而不是`rand()` (10认同)
  • 有人可以解释一下这种不安全方法的攻击是如何起作用的吗?我知道rand()产生的数字不是安全的随机数,但我仍然无法解决有人如何利用它的问题.如果您认为潜在的攻击者无法注册自己接收多个密码进行分析,这会有什么不同吗? (5认同)
  • @Cerbrus你提出了一个替代密码,在一个已知的明文攻击下轻易打破.攻击者只需要注册几次,并记下注册时间和密码,并从中恢复排列. (4认同)
  • 所有这些“不安全!!!” 尖叫完全基于_假设_攻击者知道您如何生成密码。(如果他们这样做,你的问题比那些密码更大)只要确保你随机输入(`$alphabet`),这样就足够安全了。 (2认同)
  • 现在答案是鼓励社区维基社区的贡献,所以我恢复了斯科特的. (2认同)
  • @Neal不,我编辑的唯一要点是直接指出人们的安全答案.现在,这是一个受社区保护的问题,而不是一个封闭的问题,我可以在这里提供自己的问题. (2认同)

Sco*_*ski 112

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()这样你就可以在PHP 7发布之前使用新的API.使用我们的random_int()polyfill 可能比编写自己的实现更安全.

使用安全的随机整数生成器,生成安全的随机字符串比派对更容易:

<?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;
}
Run Code Online (Sandbox Code Playgroud)

  • 接得好!需要将其删除。 (3认同)
  • RandomLib现在已经有两年没有更新了.在最近的PHP版本(在我的例子中为7.1.25)中使用它会抛出各种`mcrypt_*`函数的弃用警告.我可以在[问题主题](https://github.com/ircmaxell/RandomLib/issues/55)上看到[分叉库](https://github.com/paragonie/RandomLib)能够掌握@ircmaxell,但是你的叉子在Travis上说'构建失败'.您是否愿意更新此答案(在Google上仍然显示相当高的答案)? (2认同)

小智 108

我知道您正在尝试以特定方式生成密码,但您可能也想查看此方法...

$bytes = openssl_random_pseudo_bytes(2);

$pwd = bin2hex($bytes);
Run Code Online (Sandbox Code Playgroud)

它取自php.net网站,它创建的字符串长度是op​​enssl_random_pseudo_bytes函数中的两倍.所以上面会创建一个4个字符长的密码.

简而言之...

$pwd = bin2hex(openssl_random_pseudo_bytes(4));
Run Code Online (Sandbox Code Playgroud)

会创建一个长8个字符的密码.

但请注意,密码只包含数字0-9和小写字母af!

  • 如果你想要一个大写,小写和数字的密码,试试这个:https://gist.github.com/zyphlar/7217f566fc83a9633959 (13认同)

BSQ*_*BSQ 52

2行的微小代码.

演示:http://codepad.org/5rHMHwnH

function rand_string( $length ) {

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

}

echo rand_string(8);
Run Code Online (Sandbox Code Playgroud)

使用rand_string,您可以定义要创建的字符数.

  • 很好,虽然你不会使用这种方法得到任何重复的字符,这可能是不可取的. (20认同)
  • 此功能对于生成长密码非常糟糕.首先,如果$ length比$ chars字符串长,那么只要输入的长度,你就不会收到字符串,而是chars字符串的长度.此外,每个字符只保证1个没有重复.它也不保证使用大写字母或数字,这通常是一个要求(当然,如果由于先前的错误你的长度超过26) (11认同)

Pee*_*Haa 37

如果您使用的是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;
}
Run Code Online (Sandbox Code Playgroud)

老答案如下:

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;
}
Run Code Online (Sandbox Code Playgroud)

  • 不要使用`mt_rand`来生成密码. (12认同)
  • @willbradley种子的质量和`mt_rand`一样糟糕,所以它仍然不适合任何安全用途. (4认同)
  • @CodesInChaos它比rand()更好,这是上面的例子所使用的.根据PHP手册,首选openssl_random_pseudo_bytes(). (2认同)
  • 您会看到这++ ChaosInCodes惹恼了我。您没有看过这个问题,只是做了一些通用的陈述,重复了许多持之以恒的信念。简而言之:针对完全不同的问题提供正确的建议。随机密码也可以。它们可能仅是“ x”使用。老实说,如果您在设计系统时没有定时锁定和DOS检测并且“先尝试x次-&gt;锁定”,那么您做错了。采取这样的措施不可能猜测mt_rand密码。相反,使用mt_rand不会使暴力破解密码更加容易。只是不会。 (2认同)

San*_*dra 35

在一行中:

substr(str_shuffle('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') , 0 , 10 )
Run Code Online (Sandbox Code Playgroud)

  • 这可以防止重复使用相同的字母,因为它们只是随机播放,但不会出现多次. (10认同)
  • 不用说,没有人应该根据行数优化他们的密码生成功能.即使这个使用的RNG是安全的(事实并非如此),在生成10个字符的密码时避免重复字符会使您从~52位熵减少到~50位熵(约4倍快速破解).如果你将它扩展到20个字符,那么非重复会使你从~103位降低到~94位(破解速度快〜512倍). (8认同)
  • `substr(str_shuffle(str_repeat($ chars,$ length)),0,$ length);`熵恢复 (3认同)

Mad*_*iha 26

最好的选择是ircmaxellRandomLib库.

用法示例:

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

$passwordLength = 8; // Or more
$randomPassword = $generator->generateString($passwordLength);
Run Code Online (Sandbox Code Playgroud)

它产生的字符串这是不是像正常的随机性功能更强烈的随机shuffle()rand()(这是你一般要像密码,盐和密钥敏感信息).

  • @Cerbrus:当然这个答案没有使用`rand()`做错答案.他们都是不正确的! (8认同)
  • [`rand`不是密码的好选择](https://paragonie.com/blog/2015/07/how-safely-generate-random-strings-and-integers-in-php) (6认同)
  • 这是正确的答案.不要使用`rand()`或`mt_rand()`. (4认同)

phi*_*hag 14

你想要的strlen($alphabet),不是count常数alphabet(相当于'alphabet').

但是,rand用于此目的不是合适的随机函数.它的输出很容易预测,因为它隐含在当前时间播种.另外,rand不是加密安全的; 因此,从输出中确定其内部状态相对容易.

相反,读取/dev/urandom以获取加密随机数据.


jet*_*eon 14

我将发布一个答案,因为现有的一些答案很接近但有一个:

  • 一个比你想要的更小的字符空间,这样就可以更容易地进行强制执行,或者对于相同的熵,密码必须更长
  • 一个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;
}
Run Code Online (Sandbox Code Playgroud)

对于第二部分,我们将使用,base64_encode因为它需要一个字节字符串,并将生成一系列字符,其字母非常接近原始问题中指定的字符.如果我们不介意+,/并且=字符出现在最终字符串中,并且我们希望结果至少有$n字符长,我们可以简单地使用:

base64_encode(strong_random_bytes(intval(ceil($n * 3 / 4))));
Run Code Online (Sandbox Code Playgroud)

这个3/4因素是由于base64编码导致字符串的长度至少比字节字符串大三分之一.结果将是$n4的倍数,否则最多3个字符.由于额外的字符主要是填充字符=,如果我们由于某种原因有一个约束密码是一个确切的长度,那么我们可以将它截断到我们想要的长度.这尤其是因为对于给定的$n密码,所有密码都将以相同数量的密码结束,因此有权访问结果密码的攻击者最多可以猜测2个字符.


为了获得额外的功劳,如果我们想要满足OP问题中的确切规范,那么我们将不得不做更多的工作.我将放弃这里的基本转换方法,并快速而肮脏地进行.由于62条长字母表,两者都需要产生比结果中使用的更多的随机性.

对于结果中的额外字符,我们可以简单地从结果字符串中丢弃它们.如果我们在字节字符串中以8个字节开始,则最多约25%的base64字符将是这些"不合需要"的字符,因此简单地丢弃这些字符会导致字符串不短于OP所需的字符串.然后我们可以简单地截断它以达到确切的长度:

$dirty_pass = base64_encode(strong_random_bytes(8)));
$pass = substr(str_replace(['/', '+', '='], ['', '', ''], $dirty_pass, 0, 8);
Run Code Online (Sandbox Code Playgroud)

如果生成更长的密码,填充字符将=形成较小和较小比例的中间结果,这样您就可以实现更精简的方法,如果排除用于PRNG的熵池是一个问题.


src*_*der 11

base_convert(uniqid('pass', true), 10, 36);

例如. e0m6ngefmj4

编辑

正如我在评论中提到的那样,长度意味着暴力攻击会更好地对抗它然后进行定时攻击,因此担心"随机生成器有多安全"并不是真正相关的.安全性,特别是针对此用例,需要补充可用性,因此上述解决方案实际上足以满足所需的问题.

但是,如果你在搜索安全的随机字符串生成器时偶然发现了这个答案(因为我假设有些人基于响应),对于诸如生成令牌之类的东西,这里是这样的代码生成器的样子:

function base64urlEncode($data) {
    return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}

function secureId($length = 32) {

    if (function_exists('openssl_random_pseudo_bytes')) {
        $bytes = openssl_random_pseudo_bytes($length);
        return rtrim(strtr(base64_encode($bytes), '+/', '0a'), '=');
    }
    else { // fallback to system bytes

        error_log("Missing support for openssl_random_pseudo_bytes");

        $pr_bits = '';

        $fp = @fopen('/dev/urandom', 'rb');
        if ($fp !== false) {
            $pr_bits .= @fread($fp, $length);
            @fclose($fp);
        }

        if (strlen($pr_bits) < $length) {
            error_log('unable to read /dev/urandom');
            throw new \Exception('unable to read /dev/urandom');
        }

        return base64urlEncode($pr_bits);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 根据PHP手册,@ Benenird rand()也不具有加密安全性.该手册建议使用openssl_random_pseudo_bytes(). (7认同)

小智 9

另一个(仅限linux)

function randompassword()
{
    $fp = fopen ("/dev/urandom", 'r');
    if (!$fp) { die ("Can't access /dev/urandom to get random data. Aborting."); }
    $random = fread ($fp, 1024); # 1024 bytes should be enough
    fclose ($fp);
    return trim (base64_encode ( md5 ($random, true)), "=");
}
Run Code Online (Sandbox Code Playgroud)

  • 读取 1024 字节并压缩为 128 位加密哈希熵有点浪费。另外,“fread()”默认缓冲到 8192 字节,因此您总是会使用给定的代码从“/dev/urandom”读取这么多字节。这在 Windows 上也不起作用。不过,感谢使用 CSPRNG。 (2认同)

Ami*_*ati 9

更聪明一点:

function strand($length){
  if($length > 0)
    return chr(rand(33, 126)) . strand($length - 1);
}
Run Code Online (Sandbox Code Playgroud)

检查它在这里.


San*_*eev 6

尝试使用大写字母,小写字母,数字和特殊字符

function generatePassword($_len) {

    $_alphaSmall = 'abcdefghijklmnopqrstuvwxyz';            // small letters
    $_alphaCaps  = strtoupper($_alphaSmall);                // CAPITAL LETTERS
    $_numerics   = '1234567890';                            // numerics
    $_specialChars = '`~!@#$%^&*()-_=+]}[{;:,<.>/?\'"\|';   // Special Characters

    $_container = $_alphaSmall.$_alphaCaps.$_numerics.$_specialChars;   // Contains all characters
    $password = '';         // will contain the desired pass

    for($i = 0; $i < $_len; $i++) {                                 // Loop till the length mentioned
        $_rand = rand(0, strlen($_container) - 1);                  // Get Randomized Length
        $password .= substr($_container, $_rand, 1);                // returns part of the string [ high tensile strength ;) ] 
    }

    return $password;       // Returns the generated Pass
}
Run Code Online (Sandbox Code Playgroud)

让我们说我们需要10位数通过

echo generatePassword(10);  
Run Code Online (Sandbox Code Playgroud)

示例输出:

,IZCQ_IV\7

@wlqsfhT(d

1!8 + 1\4 @ UD

  • “rand”函数实际上在加密上并不安全,因此使用它生成密码可能存在相当大的风险 (3认同)

小智 6

使用这个简单的代码生成med-strong密码12长度

$password_string = '!@#$%*&abcdefghijklmnpqrstuwxyzABCDEFGHJKLMNPQRSTUWXYZ23456789';
$password = substr(str_shuffle($password_string), 0, 12);
Run Code Online (Sandbox Code Playgroud)