CodeIgniter-Security的CSRF hash生成功能

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 < 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值