CodeIgniter-Security的CSRF hash生成功能

来源:互联网 发布:gddr5x和hbm2知乎 编辑:程序博客网 时间:2024/06/01 17:30

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;#elseint    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 < size) {n = read(fd, iv + read_bytes, size - read_bytes);if (n < 0) {break;}read_bytes += n;}n = read_bytes;close(fd);if (n < size) {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 "方式。


0 0
原创粉丝点击