PHP函数生成一个无限唯一的标识符
我正在Zend 3中创建一个新项目,它要求我有一个唯一的ID或散列,以后可以在多个地方使用 我在Google上查看了很多例子,但找不到一个能够满足我要求的函数,因为它需要始终保持99%的唯一性,并且它需要能够始终生成数以亿计的唯一“哈希” 以下功能引起了我的注意:PHP函数生成一个无限唯一的标识符,php,security,zend-framework,zend-framework2,zend-framework3,Php,Security,Zend Framework,Zend Framework2,Zend Framework3,我正在Zend 3中创建一个新项目,它要求我有一个唯一的ID或散列,以后可以在多个地方使用 我在Google上查看了很多例子,但找不到一个能够满足我要求的函数,因为它需要始终保持99%的唯一性,并且它需要能够始终生成数以亿计的唯一“哈希” 以下功能引起了我的注意: function uniqidReal($lenght = 13) { // uniqid gives 13 chars, but you could adjust it to your needs.
function uniqidReal($lenght = 13) {
// uniqid gives 13 chars, but you could adjust it to your needs.
if (function_exists("random_bytes")) {
$bytes = random_bytes(ceil($lenght / 2));
} elseif (function_exists("openssl_random_pseudo_bytes")) {
$bytes = openssl_random_pseudo_bytes(ceil($lenght / 2));
} else {
throw new Exception("no cryptographically secure random function available");
}
return substr(bin2hex($bytes), 0, $lenght);
}
一个简单的测试:
echo "<pre>";
for($i = 0; $i < 100; $i++)
{
echo $this->uniqidReal(25) .PHP_EOL ;
}
但我无法确认这是否能保证我的生产环境有99%的成功率
如果有人能给我建议,或给我举个例子,我将不胜感激 这应该是一个评论,但有点长 你对“独特”和“一直”的使用有些混淆。令牌要么是唯一的,要么不是唯一的。单独使用随机数生成器创建令牌不足以保证唯一性-随机数生成器的关键在于您不知道要生成的下一个值是什么-这意味着您也不知道下一个数字将与前一个数字不同。OTOH,使用random_bytes()或openssl_random_pseudo_bytes()生成一个“99%始终唯一”的令牌似乎是一个巨大的滥杀 为了弄清楚这可能有多独特,我们需要知道在任何时候,有多少代币将在群体中被考虑(或者能够从预期的创建速率和TTL计算出来) 您使用的是大数字,这意味着您有很好的理由不使用最简单和最明显的唯一标识符,即递增整数。因此,对猜测现有标识符的抵制显然是实现的关键——但您也没有告诉我们这一点 将文章标题粘贴到Google中会显示文章的顶部结果(紧随其后的()函数),但出于某种原因,您可能没有找到uniqid(),或者出于某种原因拒绝了它 你文章的标题也是一个矛盾修饰法——为了定义一组无限长的标识符,标识符的长度必须是无限长的 它需要能够生成数以亿计的“散列” ……您希望所有这些都在Zend框架内运行吗?-哈哈 但我无法确认这是否能保证我的生产环境有99%的成功率
为什么不呢?这里有足够的信息来确认按位熵是均匀分布的,并且应该知道生产环境的计划容量。剩下的是基本的算术。函数
随机字节
生成加密安全的随机字节
对于openssl_random_pseudo_bytes
添加crypto_strong
parametre,以确保所使用的算法加密性强
由于您的要求仅为99%,唯一加密安全的随机字节将满足您的要求。我们约为8x10⁹ 人。想象一下,在一年中,我们每秒钟访问一次您的站点,需要一个唯一的标识符。您需要大约252288×10个标识符。如果您认为您的站点将投入生产大约1000年,并且人口将增加1000倍,那么您需要大约10²⁹ 标识符;所以一个32字节的自动增量字符串就足够了。添加伪随机32字节字符串作为后缀,以获取安全的64字节标识符。再加上一点,您可以散列标识符来创建令牌 然后很容易编写一个函数来获取它们 2017年4月13日编辑 小样本: 首先需要一个伪随机强密钥生成器。我将发布我当前使用的函数:
<?php
function pseudo_random_key($byte_count = 32) {
return base64_encode(pseudoRandomBytes($byte_count));
}
如果你想保证你的ID是唯一的,你必须保护你的ID。但是如果你把微时间也算进去呢microtime().$yourKey
这样就足够了吗?@user2659982-不幸的是,这样不行。。。看看CloudFlare->Zone ID,例如,他们有很多网站,每个网站都有不同的唯一区域ID。他们将它们保存在数据库中,然后您可以在生成唯一ID时查找它们。我计划将其存储在数据库中,以便以后使用它。UUID4可能对您有帮助。在packagist.org中有很多库支持该算法。我拒绝了uniqid(),因为它不能保证它的功能,请阅读并亲自查看php.net-而且我可能还没有很好地解释自己,“它需要能够生成成百上千万个”散列…如果您无法衡量您引用的随机数方法的有效性,您如何衡量uniqid()的有效性?正如PHP.net所说:“此函数不保证返回值的唯一性。由于大多数系统通过NTP或类似方式调整系统时钟,因此系统时间不断变化。因此,此函数可能不会返回进程/线程的唯一ID。使用更多的熵来增加唯一性的可能性。“您已经说过,您不需要该值始终是唯一的。您衡量质量的标准是互联网上某个随机的人这么说的吗?谢谢,密码应该是这样的?请参阅:如果crypto\u strong
被传递到函数中,这将包含一个布尔值,确定是否使用了该算法。”“Cryptographics strong”,如果正确,则为TRUE,否则为FALSE。谢谢,我将使用此函数。谢谢,它确实有帮助,但举个例子会更好:)
<?php
function pseudoRandomBytes($count = 32){
static $random_state, $bytes, $has_openssl, $has_hash;
$missing_bytes = $count - strlen($bytes);
if ($missing_bytes > 0) {
// If you are using a Php version before 5.3.4 avoid using
// openssl_random_pseudo_bytes()
if (!isset($has_openssl)) {
$has_openssl = version_compare(PHP_VERSION, '5.3.4', '>=')
&& function_exists('openssl_random_pseudo_bytes');
}
// to get entropy
if ($has_openssl) {
$bytes .= openssl_random_pseudo_bytes($missing_bytes);
} elseif ($fh = @fopen('/dev/urandom', 'rb')) {
// avoiding openssl_random_pseudo_bytes()
// you find entropy at /dev/urandom usually available in most
// *nix systems
$bytes .= fread($fh, max(4096, $missing_bytes));
fclose($fh);
}
// If it fails you must create enough entropy
if (strlen($bytes) < $count) {
// Initialize on the first call. The contents of $_SERVER
// includes a mix of user-specific and system information
// that varies a little with each page.
if (!isset($random_state)) {
$random_state = print_r($_SERVER, TRUE);
if (function_exists('getmypid')) {
// Further initialize with the somewhat random PHP process ID.
$random_state .= getmypid();
}
// hash() is only available in PHP 5.1.2+ or via PECL.
$has_hash = function_exists('hash')
&& in_array('sha256', hash_algos());
$bytes = '';
}
if ($has_hash) {
do {
$random_state = hash('sha256', microtime() . mt_rand() .
$random_state);
$bytes .= hash('sha256', mt_rand() . $random_state, TRUE);
} while (strlen($bytes) < $count);
} else {
do {
$random_state = md5(microtime() . mt_rand() . $random_state);
$bytes .= pack("H*", md5(mt_rand() . $random_state));
} while (strlen($bytes) < $count);
}
}
}
$output = substr($bytes, 0, $count);
$bytes = substr($bytes, $count);
return $output;
}
<?php
function pseudo_random_key($byte_count = 32) {
return base64_encode(pseudoRandomBytes($byte_count));
}
<?php
function uniqueChunkMathKeysPrefix(){
// a call to read your db for prefix
// I suppose you have an environment string-keyed table
// and a couple of dbfunction to read and write data to it
$last18bytesPrefix = dbReadEnvVariable('unique_prefix');
// Also you store your current index wich returns to 0 once you get
// a 99999999999999 value
$lastuniqueindex = dbReadEnvVariable('last_unique_keys_index');
if ($lastuniqueindex < 99999999999999){
$currentuniqueindex = $lastuniqueindex + 1;
$curret18bytesPrefix = $last18bytesPrefix;
}else{
$currentuniqueindex = 0;
$curret18bytesPrefix = dbReadEnvVariable('next_unique_prefix');
// flag your db variables to notify cron to create a new next prefix
dbStoreEnvVariable('next_unique_prefix', 0);
dbStoreEnvVariable('unique_prefix', $curret18bytesPrefix);
// you have the time needed to have site visits and create new
// 99999999999999 keys as a while to run your cron to adjust your
// next prefix
}
// store your current index
dbStoreEnvVariable('last_unique_keys_index', $currentuniqueindex);
// Finally you create the unique index prefix part
$uniqueindexchunk = substr('00000000000000'.$currentuniqueindex, -14);
// return the output
return $curret18bytesPrefix.$uniqueindexchunk;
}
<?php
function createUniquePseudoRandomKey(){
$newkey = uniqueChunkMathKeysPrefix() . pseudo_random_key(32);
// to beautify the output make a dummie call
// masking the 0s ties
return md5($newkey);
}