Skip to content

Commit

Permalink
minor #11822 [Security] Use hash_equals for constant-time string comp…
Browse files Browse the repository at this point in the history
…arison (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 (#11797 (comment)).

Commits
-------

3071557 [Security] Add more tests for StringUtils::equals
03bd74b [Security] Use hash_equals for constant-time string comparison
  • Loading branch information
fabpot committed Sep 10, 2014
2 parents d4e056c + 3071557 commit a45e3da
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 4 deletions.
10 changes: 9 additions & 1 deletion src/Symfony/Component/Security/Core/Util/StringUtils.php
Expand Up @@ -27,6 +27,7 @@ private function __construct() {}
* 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
Expand All @@ -35,6 +36,13 @@ private function __construct() {}
*/
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);

Expand All @@ -45,7 +53,7 @@ public static function equals($knownString, $userInput)
$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]));
}
Expand Down
44 changes: 41 additions & 3 deletions src/Symfony/Component/Security/Tests/Core/Util/StringUtilsTest.php
Expand Up @@ -13,11 +13,49 @@

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()
{
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->assertTrue(StringUtils::equals('password', 'password'));
$this->assertFalse(StringUtils::equals('password', 'foo'));
$this->assertFalse(StringUtils::equals($known, $user));
}
}

0 comments on commit a45e3da

Please sign in to comment.