minor #11822 [Security] Use hash_equals for constant-time string comparison (again) (dunglas)
This PR was merged into the 2.3 branch. Discussion ---------- [Security] Use hash_equals for constant-time string comparison (again) | Q | A | ------------- | --- | Bug fix? | no | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | n/a | License | MIT | Doc PR | n/a Use the `hash_equals` function (introduced in PHP 5.6) for timing attack safe string comparison when available. Add in the DocBlock that length will leak (https://github.com/symfony/symfony/pull/11797#issuecomment-53990712). Commits -------3071557
[Security] Add more tests for StringUtils::equals03bd74b
[Security] Use hash_equals for constant-time string comparison
This commit is contained in:
commit
a45e3da3e4
|
@ -27,6 +27,7 @@ class StringUtils
|
|||
* Compares two strings.
|
||||
*
|
||||
* This method implements a constant-time algorithm to compare strings.
|
||||
* Regardless of the used implementation, it will leak length information.
|
||||
*
|
||||
* @param string $knownString The string of known length to compare against
|
||||
* @param string $userInput The string that the user can control
|
||||
|
@ -35,6 +36,13 @@ class StringUtils
|
|||
*/
|
||||
public static function equals($knownString, $userInput)
|
||||
{
|
||||
$knownString = (string) $knownString;
|
||||
$userInput = (string) $userInput;
|
||||
|
||||
if (function_exists('hash_equals')) {
|
||||
return hash_equals($knownString, $userInput);
|
||||
}
|
||||
|
||||
$knownLen = strlen($knownString);
|
||||
$userLen = strlen($userInput);
|
||||
|
||||
|
@ -45,7 +53,7 @@ class StringUtils
|
|||
$result = $knownLen - $userLen;
|
||||
|
||||
// Note that we ALWAYS iterate over the user-supplied length
|
||||
// This is to prevent leaking length information
|
||||
// This is to mitigate leaking length information
|
||||
for ($i = 0; $i < $userLen; $i++) {
|
||||
$result |= (ord($knownString[$i]) ^ ord($userInput[$i]));
|
||||
}
|
||||
|
|
|
@ -13,11 +13,49 @@ namespace Symfony\Component\Security\Tests\Core\Util;
|
|||
|
||||
use Symfony\Component\Security\Core\Util\StringUtils;
|
||||
|
||||
/**
|
||||
* Data from PHP.net's hash_equals tests
|
||||
*/
|
||||
class StringUtilsTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testEquals()
|
||||
public function dataProviderTrue()
|
||||
{
|
||||
$this->assertTrue(StringUtils::equals('password', 'password'));
|
||||
$this->assertFalse(StringUtils::equals('password', 'foo'));
|
||||
return array(
|
||||
array('same', 'same'),
|
||||
array('', ''),
|
||||
array(123, 123),
|
||||
array(null, ''),
|
||||
array(null, null),
|
||||
);
|
||||
}
|
||||
|
||||
public function dataProviderFalse()
|
||||
{
|
||||
return array(
|
||||
array('not1same', 'not2same'),
|
||||
array('short', 'longer'),
|
||||
array('longer', 'short'),
|
||||
array('', 'notempty'),
|
||||
array('notempty', ''),
|
||||
array(123, 'NaN'),
|
||||
array('NaN', 123),
|
||||
array(null, 123),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataProviderTrue
|
||||
*/
|
||||
public function testEqualsTrue($known, $user)
|
||||
{
|
||||
$this->assertTrue(StringUtils::equals($known, $user));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataProviderFalse
|
||||
*/
|
||||
public function testEqualsFalse($known, $user)
|
||||
{
|
||||
$this->assertFalse(StringUtils::equals($known, $user));
|
||||
}
|
||||
}
|
||||
|
|
Reference in New Issue