PleaseStand has uploaded a new change for review. https://gerrit.wikimedia.org/r/168931
Change subject: hash_equals(): Avoid division by zero when $known_string is empty ...................................................................... hash_equals(): Avoid division by zero when $known_string is empty Per Tim Starling's review of Icb239471, reverted back to the version of the function from Patch Set 1 of Iece006ec, which did not have the bug. This version does not attempt to minimize the inevitable leakage of the string's length. Also revised the doc comment to explain more effectively what the problem with a normal (===) string comparison is for the use cases of this function. Follows-up b9e1d5f5c066. Change-Id: I1b347e69b39af3d7d8ba6673af63f1a616befbdf (cherry picked from commit 4620e3b862568d76661e86857779795f4f974e13) --- M includes/GlobalFunctions.php 1 file changed, 24 insertions(+), 13 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core refs/changes/31/168931/1 diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 490df24..27f7cac 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -102,19 +102,30 @@ } // hash_equals function only exists in PHP >= 5.6.0 +// http://php.net/hash_equals if ( !function_exists( 'hash_equals' ) ) { /** - * Check whether a user-provided string is equal to a fixed-length secret without - * revealing bytes of the secret through timing differences. + * Check whether a user-provided string is equal to a fixed-length secret string + * without revealing bytes of the secret string through timing differences. * - * This timing guarantee -- that a partial match takes the same time as a complete - * mismatch -- is why this function is used in some security-sensitive parts of the code. - * For example, it shouldn't be possible to guess an HMAC signature one byte at a time. + * The usual way to compare strings (PHP's === operator or the underlying memcmp() + * function in C) is to compare corresponding bytes and stop at the first difference, + * which would take longer for a partial match than for a complete mismatch. This + * is not secure when one of the strings (e.g. an HMAC or token) must remain secret + * and the other may come from an attacker. Statistical analysis of timing measurements + * over many requests may allow the attacker to guess the string's bytes one at a time + * (and check his guesses) even if the timing differences are extremely small. + * + * When making such a security-sensitive comparison, it is essential that the sequence + * in which instructions are executed and memory locations are accessed not depend on + * the secret string's value. HOWEVER, for simplicity, we do not attempt to minimize + * the inevitable leakage of the string's length. That is generally known anyway as + * a chararacteristic of the hash function used to compute the secret value. * * Longer explanation: http://www.emerose.com/timing-attacks-explained * * @codeCoverageIgnore - * @param string $known_string Fixed-length secret to compare against + * @param string $known_string Fixed-length secret string to compare against * @param string $user_string User-provided string * @return bool True if the strings are the same, false otherwise */ @@ -134,14 +145,14 @@ return false; } - // Note that we do one thing PHP doesn't: try to avoid leaking information about - // relative lengths of $known_string and $user_string, and of multiple $known_strings. - // However, lengths may still inevitably leak through, for example, CPU cache misses. $known_string_len = strlen( $known_string ); - $user_string_len = strlen( $user_string ); - $result = $known_string_len ^ $user_string_len; - for ( $i = 0; $i < $user_string_len; $i++ ) { - $result |= ord( $known_string[$i % $known_string_len] ) ^ ord( $user_string[$i] ); + if ( $known_string_len !== strlen( $user_string ) ) { + return false; + } + + $result = 0; + for ( $i = 0; $i < $known_string_len; $i++ ) { + $result |= ord( $known_string[$i] ) ^ ord( $user_string[$i] ); } return ( $result === 0 ); -- To view, visit https://gerrit.wikimedia.org/r/168931 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I1b347e69b39af3d7d8ba6673af63f1a616befbdf Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/core Gerrit-Branch: REL1_24 Gerrit-Owner: PleaseStand <pleasest...@live.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits