Skip to content
This repository has been archived by the owner on Jan 31, 2020. It is now read-only.

Commit

Permalink
Merge branch 'hotfix/5918' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
weierophinney committed Mar 7, 2014
165 parents 526d261 + 835664d + 56df441 + 3ddc8fa + 274bd7f + c9e433d + 99f6515 + 8d1a271 + 2c2bc35 + 16359e3 + 10b9b3c + d05ac01 + 9628c66 + 66c8763 + 943d333 + 8342f70 + 4eb4184 + 4ea0bc2 + 860b725 + 7d29287 + 49a7844 + 3836aa0 + de1fe83 + 929eb12 + 6e6902f + f823467 + f3b3f76 + 93aff47 + 5f2adec + 7674ec8 + 2200ed2 + f033c86 + d43a2da + 4434323 + 88b21e1 + b02b602 + b692de7 + 7f833d2 + 2eaf4da + a7848de + 5bd9f85 + d7a26e8 + 173f53d + bd77e8e + 45f017e + 118612c + 3a09f79 + 47d92bc + dbf56ad + a753b61 + 6467186 + 5ac4124 + 2bf68ca + e7cd709 + 7a552db + f112e0c + cec42fc + 066eb37 + 3569d8b + 00def10 + ecae47b + 0e552a5 + 4f854c2 + 2b17650 + c1c0447 + 73b1f80 + dd4a335 + a40eb42 + 9262db1 + bdbd950 + 6d50117 + 10b08a7 + f692a0d + ebe63d3 + 528260b + 5f04a7f + c0dcd12 + 224f280 + 8785f25 + 866539b + d711927 + e280213 + 002f0d4 + 5ffcbbe + 8937705 + 5803840 + 3d7cd9a + 6b14f0e + 24bd169 + 0eba870 + 4e4acfe + 9e243bd + e156354 + 2a84563 + 3f1f758 + 4e425f4 + 7030f97 + ea08a0f + 213ebd9 + 5d80740 + 800c29c + be31ff1 + 8f989d6 + 8b02a8b + 4cfd82c + c411f06 + 4229561 + b321e3b + 2e660f9 + 3fff65f + a2eb7bf + 82469ff + 22fa741 + 99819cb + 8c16404 + a7c0820 + 7d277af + da0bec5 + 33f214a + a07f208 + 963d2fe + 1100f88 + 6713bf5 + 7421758 + b64a638 + 772a2a1 + dff3231 + 56bc4ca + 463e3d7 + 14bd316 + f8b9e58 + ef0268c + 3fe91ce + 130da19 + 6d4097d + 02fa5b3 + 927b7df + 2fbc2a0 + c772270 + 1cdc0cc + 4d6040d + 852db9e + 047576f + 1b49389 + d7193b9 + 3fed016 + 082acdb + ecb5260 + 3ec34d8 + 838bdc9 + 3c0e07f + 6ac386b + ee0e7b9 + 89397ad + 0349a1b + c768b16 + d483707 + 3a30dc9 + 931d3a0 + 45b16a2 + 008b63c + 4879439 + c418e80 + abdbc4a + 90fff74 commit da00a68
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 20 deletions.
95 changes: 75 additions & 20 deletions src/Csrf.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class Csrf extends AbstractValidator

/**
* Static cache of the session names to generated hashes
* @todo unused, left here to avoid BC breaks
*
* @var array
*/
Expand Down Expand Up @@ -117,9 +118,10 @@ public function isValid($value, $context = null)
{
$this->setValue((string) $value);

$hash = $this->getValidationToken();
$tokenId = $this->getTokenIdFromHash($value);
$hash = $this->getValidationToken($tokenId);

if ($value !== $hash) {
if ($this->getTokenFromHash($value) !== $this->getTokenFromHash($hash)) {
$this->error(self::NOT_SAME);
return false;
}
Expand Down Expand Up @@ -214,14 +216,7 @@ public function getSalt()
public function getHash($regenerate = false)
{
if ((null === $this->hash) || $regenerate) {
if ($regenerate) {
$this->hash = null;
} else {
$this->hash = $this->getValidationToken();
}
if (null === $this->hash) {
$this->generateHash();
}
$this->generateHash();
}
return $this->hash;
}
Expand Down Expand Up @@ -270,12 +265,20 @@ public function getTimeout()
protected function initCsrfToken()
{
$session = $this->getSession();
//$session->setExpirationHops(1, null);
$timeout = $this->getTimeout();
if (null !== $timeout) {
$session->setExpirationSeconds($timeout);
}
$session->hash = $this->getHash();

$hash = $this->getHash();
$token = $this->getTokenFromHash($hash);
$tokenId = $this->getTokenIdFromHash($hash);

if (! $session->tokenList) {
$session->tokenList = array();
}
$session->tokenList[$tokenId] = $token;
$session->hash = $hash; // @todo remove this, left for BC
}

/**
Expand All @@ -288,29 +291,81 @@ protected function initCsrfToken()
*/
protected function generateHash()
{
if (isset(static::$hashCache[$this->getSessionName()])) {
$this->hash = static::$hashCache[$this->getSessionName()];
} else {
$this->hash = md5($this->getSalt() . Rand::getBytes(32) . $this->getName());
static::$hashCache[$this->getSessionName()] = $this->hash;
}
$token = md5($this->getSalt() . Rand::getBytes(32) . $this->getName());

$this->hash = $this->formatHash($token, $this->generateTokenId());

$this->setValue($this->hash);
$this->initCsrfToken();
}

/**
* @return string
*/
protected function generateTokenId()
{
return md5(Rand::getBytes(32));
}

/**
* Get validation token
*
* Retrieve token from session, if it exists.
*
* @param string $tokenId
* @return null|string
*/
protected function getValidationToken()
protected function getValidationToken($tokenId = null)
{
$session = $this->getSession();
if (isset($session->hash)) {

/**
* if no tokenId is passed we revert to the old behaviour
* @todo remove, here for BC
*/
if (! $tokenId && isset($session->hash)) {
return $session->hash;
}

if ($tokenId && isset($session->tokenList[$tokenId])) {
return $this->formatHash($session->tokenList[$tokenId], $tokenId);
}

return null;
}

/**
* @param $token
* @param $tokenId
* @return string
*/
protected function formatHash($token, $tokenId)
{
return sprintf('%s-%s', $token, $tokenId);
}

/**
* @param $hash
* @return string
*/
protected function getTokenFromHash($hash)
{
$data = explode('-', $hash);
return $data[0] ?: null;
}

/**
* @param $hash
* @return string
*/
protected function getTokenIdFromHash($hash)
{
$data = explode('-', $hash);

if (! isset($data[1])) {
return null;
}

return $data[1];
}
}
73 changes: 73 additions & 0 deletions test/CsrfTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,4 +187,77 @@ public function testSettingNewSessionContainerSetsHashInNewContainer()
$test = $container->hash; // Doing this, as expiration hops are 1; have to grab on first access
$this->assertEquals($hash, $test);
}

public function testMultipleValidatorsSharingContainerGenerateDifferentHashes()
{
$validatorOne = new Csrf();
$validatorTwo = new Csrf();

$containerOne = $validatorOne->getSession();
$containerTwo = $validatorOne->getSession();

$this->assertSame($containerOne, $containerTwo);

$hashOne = $validatorOne->getHash();
$hashTwo = $validatorTwo->getHash();
$this->assertNotEquals($hashOne , $hashTwo);
}

public function testCanValidateAnyHashWithinTheSameContainer()
{
$validatorOne = new Csrf();
$validatorTwo = new Csrf();

$hashOne = $validatorOne->getHash();
$hashTwo = $validatorTwo->getHash();

$this->assertTrue($validatorOne->isValid($hashOne));
$this->assertTrue($validatorOne->isValid($hashTwo));
$this->assertTrue($validatorTwo->isValid($hashOne));
$this->assertTrue($validatorTwo->isValid($hashTwo));
}

public function testCannotValidateHashesOfOtherContainers()
{
$validatorOne = new Csrf();
$validatorTwo = new Csrf(array('name' => 'foo'));

$containerOne = $validatorOne->getSession();
$containerTwo = $validatorTwo->getSession();

$this->assertNotSame($containerOne, $containerTwo);

$hashOne = $validatorOne->getHash();
$hashTwo = $validatorTwo->getHash();

$this->assertTrue($validatorOne->isValid($hashOne));
$this->assertFalse($validatorOne->isValid($hashTwo));
$this->assertFalse($validatorTwo->isValid($hashOne));
$this->assertTrue($validatorTwo->isValid($hashTwo));
}

public function testCannotReValidateAnExpiredHash()
{
$hash = $this->validator->getHash();

$this->assertTrue($this->validator->isValid($hash));

$this->sessionManager->getStorage()->setMetadata(
$this->validator->getSession()->getName(),
array('EXPIRE' => $_SERVER['REQUEST_TIME'] - 18600)
);

$this->assertFalse($this->validator->isValid($hash));
}

public function testCanValidateHasheWithoutId()
{
$method = new \ReflectionMethod(get_class($this->validator), 'getTokenFromHash');
$method->setAccessible(true);

$hash = $this->validator->getHash();
$bareToken = $method->invoke($this->validator, $hash);

$this->assertTrue($this->validator->isValid($bareToken));
}
}

0 comments on commit da00a68

Please sign in to comment.