diff --git includes/bootstrap.inc includes/bootstrap.inc index 203b052..e4aaaa8 100644 --- includes/bootstrap.inc +++ includes/bootstrap.inc @@ -1709,6 +1709,46 @@ function drupal_block_denied($ip) { } /** + * 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 + * PHP function because it can return a long string of bytes (compared to < 4 + * bytes normally from mt_rand()) and uses the best available pseudo-random source. + * + * @param $count + * The number of characters (bytes) to return in the string. + */ +function drupal_random_bytes($count) { + // $random_state does not use drupal_static as it stores random bytes. + static $random_state; + // We initialize with the somewhat random PHP process ID on the first call. + if (empty($random_state)) { + $random_state = getmypid(); + } + $output = ''; + // /dev/urandom is available on many *nix systems and is considered the best + // commonly available pseudo-random source. + if ($fh = @fopen('/dev/urandom', 'rb')) { + $output = fread($fh, $count); + fclose($fh); + } + // If /dev/urandom is not available or returns no bytes, this loop 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() + // invocations are different, and that the extra input into the first one - + // 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($output) < $count) { + $random_state = hash('sha256', microtime() . mt_rand() . $random_state); + // Re-hash and truncate from 32 to 20 bytes. + $output .= substr(hash('sha256', mt_rand() . $random_state, TRUE), 0, 20); + } + return substr($output, 0, $count); +} + +/** * Generates a default anonymous $user object. * * @return Object - the user object. diff --git includes/common.inc includes/common.inc index 8026209..f596541 100644 --- includes/common.inc +++ includes/common.inc @@ -4183,45 +4183,6 @@ function drupal_json_output($var = NULL) { } /** - * 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 - * PHP function because it can return a long string of bytes (compared to < 4 - * bytes normally from mt_rand()) and uses the best available pseudo-random source. - * - * @param $count - * The number of characters (bytes) to return in the string. - */ -function drupal_random_bytes($count) { - // $random_state does not use drupal_static as it stores random bytes. - static $random_state; - // We initialize with the somewhat random PHP process ID on the first call. - if (empty($random_state)) { - $random_state = getmypid(); - } - $output = ''; - // /dev/urandom is available on many *nix systems and is considered the best - // commonly available pseudo-random source. - if ($fh = @fopen('/dev/urandom', 'rb')) { - $output = fread($fh, $count); - fclose($fh); - } - // If /dev/urandom is not available or returns no bytes, this loop 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 md5() prior to being rolled into $output, that the two md5() - // invocations are different, and that the extra input into the first one - - // 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($output) < $count) { - $random_state = md5(microtime() . mt_rand() . $random_state); - $output .= md5(mt_rand() . $random_state, TRUE); - } - return substr($output, 0, $count); -} - -/** * Get a salt useful for hardening against SQL injection. * * @return @@ -4242,7 +4203,7 @@ function drupal_get_hash_salt() { */ function drupal_get_private_key() { if (!($key = variable_get('drupal_private_key', 0))) { - $key = md5(drupal_random_bytes(64)); + $key = hash('sha256', drupal_random_bytes(64)); variable_set('drupal_private_key', $key); } return $key; @@ -4255,10 +4216,25 @@ function drupal_get_private_key() { * An additional value to base the token on. */ function drupal_get_token($value = '') { - $private_key = drupal_get_private_key(); - // A single md5() is vulnerable to length-extension attacks, so use it twice. - // @todo: add md5 and sha1 hmac functions to core. - return md5(drupal_get_hash_salt() . md5(session_id() . $value . $private_key)); + return drupal_hmac_base64(session_id() . $value, drupal_get_private_key() . drupal_get_hash_salt()); +} + +/** + * Calculate a base-64 encoded, URL-safe sha-256 hmac. + * + * @param $data + * String data. + * @param $key + * A secret string key. + * + * @return + * 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)); + // Modify the hmac so it's safe to use in URLs. + return strtr($hmac, array('+' => '-', '/' => '_', '=' => '')); } /** diff --git includes/session.inc includes/session.inc index 438d47d..d59c9e0 100644 --- includes/session.inc +++ includes/session.inc @@ -206,7 +206,7 @@ function drupal_session_initialize() { // processes (like drupal_get_token()) needs to know the future // session ID in advance. $user = drupal_anonymous_user(); - session_id(md5(uniqid('', TRUE))); + session_id(hash('sha256', drupal_random_bytes(64))); } } diff --git modules/update/update.fetch.inc modules/update/update.fetch.inc index 7944828..9db4d1c 100644 --- modules/update/update.fetch.inc +++ modules/update/update.fetch.inc @@ -137,7 +137,7 @@ function _update_process_fetch_task($project) { $success = FALSE; $available = array(); - $site_key = md5($base_url . drupal_get_private_key()); + $site_key = drupal_hmac_base64($base_url, drupal_get_private_key()); $url = _update_build_fetch_url($project, $site_key); $fetch_url_base = _update_get_fetch_url_base($project); $project_name = $project['name']; diff --git modules/user/user.module modules/user/user.module index 51e4db5..30f45d4 100644 --- modules/user/user.module +++ modules/user/user.module @@ -2079,8 +2079,7 @@ function user_cancel_url($account) { } function user_pass_rehash($password, $timestamp, $login) { - // A single md5() is vulnerable to length-extension attacks, so use it twice. - return md5(drupal_get_hash_salt() . md5($timestamp . $password . $login)); + return drupal_hmac_base64($timestamp . $login, drupal_get_hash_salt() . $password); } /**