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::equals
03bd74b [Security] Use hash_equals for constant-time string comparison
This commit is contained in:
Fabien Potencier 2014-09-10 15:18:42 +02:00
commit a45e3da3e4
2 changed files with 50 additions and 4 deletions

View File

@ -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]));
}

View File

@ -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));
}
}