To successfully mitigate timing attacks and not leak the actual length of the known
string, it is important that _both provided strings have the same length_ and that
the _user-supplied string is passed as a second parameter_ rather than first.
This function has the same signature and behavior as the native hash_equals() function
and will use that function if available (PHP >= 5.6).
An E_USER_WARNING will be emitted when either of the supplied parameters is not a string.
/** * Compares a password and its hashed value using PHP's `crypt()`. Rather than a simple string * comparison, this method uses a constant-time algorithm to defend against timing attacks. * * @see lithium\security\Password::hash() * @see lithium\security\Password::salt() * @param string $password The user-supplied plaintext password to check. * @param string $hash The known hashed password to compare it to. * @return boolean Returns a boolean indicating whether the password is correct. */ public static function check($password, $hash) { return String::compare($hash, crypt($password, $hash)); }
public function testCompare() { $this->assertTrue(String::compare('Foo', 'Foo')); $this->assertFalse(String::compare('Foo', 'foo')); $this->assertFalse(String::compare('1', 1)); }
/** * Read strategy method. * Validates the HMAC signature of the stored data. If the signatures match, then * the data is safe, and the 'valid' key in the returned data will be * * If the store being read does not contain a `__signature` field, a `MissingSignatureException` * is thrown. When catching this exception, you may choose to handle it by either writing * out a signature (e.g. in cases where you know that no pre-existing signature may exist), or * you can blackhole it as a possible tampering attempt. * * @param array $data the Data being read. * @param array $options Options for this method. * @return array validated data */ public function read($data, array $options = array()) { $class = $options['class']; $currentData = $class::read(null, array('strategies' => false)); if (!isset($currentData['__signature'])) { throw new MissingSignatureException('HMAC signature not found.'); } $currentSignature = $currentData['__signature']; $signature = static::_signature($currentData); if (!String::compare($signature, $currentSignature)) { $message = "Possible data tampering: HMAC signature does not match data."; throw new RuntimeException($message); } return $data; }
/** * Read strategy method. * * Validates the HMAC signature of the stored data. If the signatures match, then the data * is safe and will be passed through as-is. * * If the stored data being read does not contain a `__signature` field, a * `MissingSignatureException` is thrown. When catching this exception, you may choose * to handle it by either writing out a signature (e.g. in cases where you know that no * pre-existing signature may exist), or you can blackhole it as a possible tampering * attempt. * * @throws RuntimeException On possible data tampering. * @throws lithium\storage\session\strategy\MissingSignatureException On missing singature. * @param array $data The data being read. * @param array $options Options for this method. * @return array Validated data. */ public function read($data, array $options = array()) { if ($data === null) { return $data; } $class = $options['class']; $currentData = $class::read(null, array('strategies' => false)); if (!isset($currentData['__signature'])) { throw new MissingSignatureException('HMAC signature not found.'); } if (String::compare($currentData['__signature'], static::_signature($currentData))) { return $data; } throw new RuntimeException('Possible data tampering: HMAC signature does not match data.'); }
public function testCompare() { $this->assertTrue(String::compare('Foo', 'Foo')); $this->assertFalse(String::compare('Foo', 'foo')); $this->assertFalse(String::compare('Foo', 'Bar')); $this->assertTrue(String::compare('', '')); $this->assertFalse(String::compare('', '0')); $this->assertFalse(String::compare('0', '')); $this->assertException('/to be (a )?string/', function () { String::compare(null, null); }); $this->assertException('/to be (a )?string/', function () { String::compare(null, ''); }); $this->assertException('/to be (a )?string/', function () { String::compare('', null); }); $this->assertTrue(String::compare('1', '1')); $this->assertException('/to be (a )?string/', function () { String::compare('1', 1); }); $this->assertException('/to be (a )?string/', function () { String::compare(1, '1'); }); }
public function testCompare() { $backup = error_reporting(); error_reporting(E_ALL); $this->assertTrue(String::compare('Foo', 'Foo')); $this->assertFalse(String::compare('Foo', 'foo')); $this->assertFalse(String::compare('Foo', 'Bar')); $this->assertTrue(String::compare('', '')); $this->assertFalse(String::compare('', '0')); $this->assertFalse(String::compare('0', '')); $this->assertException('/to be (a )?string/', function () { String::compare(null, null); }); $this->assertException('/to be (a )?string/', function () { String::compare(null, ''); }); $this->assertException('/to be (a )?string/', function () { String::compare('', null); }); $this->assertTrue(String::compare('1', '1')); $this->assertException('/to be (a )?string/', function () { String::compare('1', 1); }); $this->assertException('/to be (a )?string/', function () { String::compare(1, '1'); }); error_reporting($backup); }