作者:王耀 | 来源:互联网 | 2023-08-18 11:34
Security类中get_random_bytes()方法负责随机生成字符串,用于CSRF验证的hash值。publicfunctionget_random_bytes($le
Security类中get_random_bytes()方法负责随机生成字符串,用于 CSRF 验证的hash值。
public function get_random_bytes($length)
{
if (empty($length) OR ! ctype_digit((string) $length))
{
return FALSE;
}
if (defined('MCRYPT_DEV_URANDOM') && ($output = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM)) !== FALSE)
{
return $output;
}
if (is_readable('/dev/urandom') && ($fp = fopen('/dev/urandom', 'rb')) !== FALSE)
{
// Try not to waste entropy ...
is_php('5.4') && stream_set_chunk_size($fp, $length);
$output = fread($fp, $length);
fclose($fp);
if ($output !== FALSE)
{
return $output;
}
}
if (function_exists('openssl_random_pseudo_bytes'))
{
return openssl_random_pseudo_bytes($length);
}
return FALSE;
}
从上面的代码可以看出,程序调用的优先级 mcrypt_create_iv -> /dev/urandom ->openssl_random_pseudo_bytes. 那么这三种调用方式有什么区别呢?
首先,查看 mcrypt_create_iv 函数源码 (/ext/mcrypt/mcrypt.c).
PHP_FUNCTION(mcrypt_create_iv)
{
char *iv;
long source = RANDOM;
long size;
int n = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &size, &source) == FAILURE) {
return;
}
if (size <= 0 || size >= INT_MAX) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create an IV with a size of less than 1 or greater than %d", INT_MAX);
RETURN_FALSE;
}
iv = ecalloc(size + 1, 1);
if (source == RANDOM || source == URANDOM) {
#if PHP_WIN32
/* random/urandom equivalent on Windows */
BYTE *iv_b = (BYTE *) iv;
if (php_win32_get_random_bytes(iv_b, (size_t) size) == FAILURE){
efree(iv);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not gather sufficient random data");
RETURN_FALSE;
}
n = size;
#else
int fd;
size_t read_bytes = 0;
fd = open(source == RANDOM ? "/dev/random" : "/dev/urandom", O_RDONLY);
if (fd <0) {
efree(iv);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot open source device");
RETURN_FALSE;
}
while (read_bytes n = read(fd, iv + read_bytes, size - read_bytes);
if (n <0) {
break;
}
read_bytes += n;
}
n = read_bytes;
close(fd);
if (n efree(iv);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not gather sufficient random data");
RETURN_FALSE;
}
#endif
} else {
n = size;
while (size) {
iv[--size] = (char) (255.0 * php_rand(TSRMLS_C) / RAND_MAX);
}
}
RETURN_STRINGL(iv, n, 0);
}
mcrypt_create_iv函数接受两个参数。在linux操作系统下, 当source == RANDOM 时 调用 "/dev/random"获取随机数, 当 source == URANDOM时,则调用“/dev/urandom”。
回看get_random_bytes函数代码, mcrypt_create_iv的第二个参数值为MCRYPT_DEV_URANDOM,实际上,调用"dev/urandom"来获取随机数。
那么相比直接调用dev/urandom获取随机数,mcrypt_create_iv 兼容性更好。
再来看看openssl_random_pseudo_bytes函数源码(ext/openssl/openssl.c):
PHP_FUNCTION(openssl_random_pseudo_bytes)
{
long buffer_length;
unsigned char *buffer = NULL;
zval *zstrong_result_returned = NULL;
int strong_result = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|z", &buffer_length, &zstrong_result_returned) == FAILURE) {
return;
}
if (buffer_length <= 0) {
RETURN_FALSE;
}
if (zstrong_result_returned) {
zval_dtor(zstrong_result_returned);
ZVAL_BOOL(zstrong_result_returned, 0);
}
buffer = emalloc(buffer_length + 1);
if ((strong_result = RAND_pseudo_bytes(buffer, buffer_length)) <0) {
efree(buffer);
RETURN_FALSE;
}
buffer[buffer_length] = 0;
RETVAL_STRINGL((char *)buffer, buffer_length, 0);
if (zstrong_result_returned) {
ZVAL_BOOL(zstrong_result_returned, strong_result);
}
}
openssl_random_pseudo_bytes内部实际调用RAND_pseudo_byte()生成随机数(RAND_pseudo_bytes不作介绍,有兴趣的童鞋可参考http://blog.csdn.net/sunspider107/article/details/7364770资料).
以上就是get_random_bytes()生成随机数三种方式的介绍,下面稍带简单地介绍 /dev/random 与 /dev/urandom 区别。
/dev/random 与 /dev/urandom 都是linux系统随机数生成器。唯一的区别是 当linux 熵池随机数不多时,读取/dev/random 时 会发生阻塞, 而/dev/urandom 不会。
总结:
在生成环境使用中,考虑到平台的兼容性,尽量使用 mcrypt_create_iv 函数获取随机数,尽量杜绝使用"/dev/random "方式。