summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabien Potencier <fabien.potencier@gmail.com>2014-09-10 15:18:42 +0200
committerFabien Potencier <fabien.potencier@gmail.com>2014-09-10 15:18:42 +0200
commiteea4a9bdfa0930002b9a4de65e3e6e7fd95941a1 (patch)
tree53601695555388dc3012a3f669004318f6e0ca7f
parentaee2d201bfe01008d293b85af075040aebeb2eac (diff)
parent6695a8e284aa75cfa2be1b1825367924febb3953 (diff)
downloadsymfony-security-eea4a9bdfa0930002b9a4de65e3e6e7fd95941a1.zip
symfony-security-eea4a9bdfa0930002b9a4de65e3e6e7fd95941a1.tar.gz
symfony-security-eea4a9bdfa0930002b9a4de65e3e6e7fd95941a1.tar.bz2
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
-rw-r--r--Core/Util/StringUtils.php10
-rw-r--r--Tests/Core/Util/StringUtilsTest.php44
2 files changed, 50 insertions, 4 deletions
diff --git a/Core/Util/StringUtils.php b/Core/Util/StringUtils.php
index 5e13037..acf8e9e 100644
--- a/Core/Util/StringUtils.php
+++ b/Core/Util/StringUtils.php
@@ -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]));
}
diff --git a/Tests/Core/Util/StringUtilsTest.php b/Tests/Core/Util/StringUtilsTest.php
index aac4139..b16cbac 100644
--- a/Tests/Core/Util/StringUtilsTest.php
+++ b/Tests/Core/Util/StringUtilsTest.php
@@ -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()
+ {
+ 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));
}
}