diff options
Diffstat (limited to 'includes/bootstrap.inc')
-rw-r--r-- | includes/bootstrap.inc | 105 |
1 files changed, 73 insertions, 32 deletions
diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index e31322c0c..1c8b5c72d 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -8,7 +8,7 @@ /** * The current system version. */ -define('VERSION', '7.24-dev'); +define('VERSION', '7.25-dev'); /** * Core API compatibility. @@ -1933,6 +1933,33 @@ function drupal_block_denied($ip) { } /** + * Returns a URL-safe, base64 encoded string of highly randomized bytes (over the full 8-bit range). + * + * @param $byte_count + * The number of random bytes to fetch and base64 encode. + * + * @return string + * The base64 encoded result will have a length of up to 4 * $byte_count. + */ +function drupal_random_key($byte_count = 32) { + return drupal_base64_encode(drupal_random_bytes($byte_count)); +} + +/** + * Returns a URL-safe, base64 encoded version of the supplied string. + * + * @param $string + * The string to convert to base64. + * + * @return string + */ +function drupal_base64_encode($string) { + $data = base64_encode($string); + // Modify the output so it's safe to use in URLs. + return strtr($data, array('+' => '-', '/' => '_', '=' => '')); +} + +/** * Returns a string of highly randomized bytes (over the full 8-bit range). * * This function is better than simply calling mt_rand() or any other built-in @@ -1945,38 +1972,34 @@ function drupal_block_denied($ip) { */ function drupal_random_bytes($count) { // $random_state does not use drupal_static as it stores random bytes. - static $random_state, $bytes, $php_compatible; - // 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(); - } - $bytes = ''; - } - if (strlen($bytes) < $count) { + static $random_state, $bytes, $has_openssl; + + $missing_bytes = $count - strlen($bytes); + + if ($missing_bytes > 0) { // PHP versions prior 5.3.4 experienced openssl_random_pseudo_bytes() // locking on Windows and rendered it unusable. - if (!isset($php_compatible)) { - $php_compatible = version_compare(PHP_VERSION, '5.3.4', '>='); + if (!isset($has_openssl)) { + $has_openssl = version_compare(PHP_VERSION, '5.3.4', '>=') && function_exists('openssl_random_pseudo_bytes'); } - // /dev/urandom is available on many *nix systems and is considered the - // best commonly available pseudo-random source. - if ($fh = @fopen('/dev/urandom', 'rb')) { + + // openssl_random_pseudo_bytes() will find entropy in a system-dependent + // way. + if ($has_openssl) { + $bytes .= openssl_random_pseudo_bytes($missing_bytes); + } + + // Else, read directly from /dev/urandom, which is available on many *nix + // systems and is considered cryptographically secure. + elseif ($fh = @fopen('/dev/urandom', 'rb')) { // PHP only performs buffered reads, so in reality it will always read // at least 4096 bytes. Thus, it costs nothing extra to read and store // that much so as to speed any additional invocations. - $bytes .= fread($fh, max(4096, $count)); + $bytes .= fread($fh, max(4096, $missing_bytes)); fclose($fh); } - // openssl_random_pseudo_bytes() will find entropy in a system-dependent - // way. - elseif ($php_compatible && function_exists('openssl_random_pseudo_bytes')) { - $bytes .= openssl_random_pseudo_bytes($count - strlen($bytes)); - } - // If /dev/urandom is not available or returns no bytes, this loop will + + // If we couldn't get enough entropy, this simple hash-based PRNG will // generate a good set of pseudo-random bytes on any system. // Note that it may be important that our $random_state is passed // through hash() prior to being rolled into $output, that the two hash() @@ -1984,9 +2007,23 @@ function drupal_random_bytes($count) { // the microtime() - is prepended rather than appended. This is to avoid // directly leaking $random_state via the $output stream, which could // allow for trivial prediction of further "random" numbers. - while (strlen($bytes) < $count) { - $random_state = hash('sha256', microtime() . mt_rand() . $random_state); - $bytes .= hash('sha256', mt_rand() . $random_state, TRUE); + 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(); + } + $bytes = ''; + } + + do { + $random_state = hash('sha256', microtime() . mt_rand() . $random_state); + $bytes .= hash('sha256', mt_rand() . $random_state, TRUE); + } + while (strlen($bytes) < $count); } } $output = substr($bytes, 0, $count); @@ -1997,17 +2034,21 @@ function drupal_random_bytes($count) { /** * Calculates a base-64 encoded, URL-safe sha-256 hmac. * - * @param $data + * @param string $data * String to be validated with the hmac. - * @param $key + * @param string $key * A secret string key. * - * @return + * @return string * A base-64 encoded sha-256 hmac, with + replaced with -, / with _ and * any = padding characters removed. */ function drupal_hmac_base64($data, $key) { - $hmac = base64_encode(hash_hmac('sha256', $data, $key, TRUE)); + // Casting $data and $key to strings here is necessary to avoid empty string + // results of the hash function if they are not scalar values. As this + // function is used in security-critical contexts like token validation it is + // important that it never returns an empty string. + $hmac = base64_encode(hash_hmac('sha256', (string) $data, (string) $key, TRUE)); // Modify the hmac so it's safe to use in URLs. return strtr($hmac, array('+' => '-', '/' => '_', '=' => '')); } |