Skip to content

Commit

Permalink
Making the ability to use longer shared csrf tokens possible. This sh…
Browse files Browse the repository at this point in the history
…ould make for fewer blackholed' requests when doing complicated javascript.
  • Loading branch information
markstory committed Oct 25, 2010
1 parent c6c3295 commit 22239b4
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 7 deletions.
25 changes: 18 additions & 7 deletions cake/libs/controller/components/security.php
Expand Up @@ -172,6 +172,16 @@ class SecurityComponent extends Component {
*/
public $csrfExpires = '+30 minutes';

/**
* Controls whether or not CSRF tokens are use and burn. Set to false to not generate
* new tokens on each request. One token will be reused until it expires. This reduces
* the chances of users getting invalid requests because of token consumption.
* It has the side effect of making CSRF less secure, as tokens are reusable.
*
* @var boolean
*/
public $csrfUseOnce = true;

/**
* Other components used by the Security component
*
Expand Down Expand Up @@ -677,16 +687,15 @@ protected function _generateToken(&$controller) {
'csrfTokens' => array()
);

if ($this->csrfCheck) {
$token['csrfTokens'][$authKey] = strtotime($this->csrfExpires);
}

$tokenData = array();
if ($this->Session->check('_Token')) {
$tokenData = $this->Session->read('_Token');
if (!empty($tokenData['csrfTokens'])) {
$token['csrfTokens'] += $tokenData['csrfTokens'];
$token['csrfTokens'] = $this->_expireTokens($token['csrfTokens']);
$token['csrfTokens'] = $this->_expireTokens($tokenData['csrfTokens']);
}
}
if ($this->csrfCheck && ($this->csrfUseOnce || empty($tokenData['csrfTokens'])) ) {
$token['csrfTokens'][$authKey] = strtotime($this->csrfExpires);
}
$controller->request->params['_Token'] = $token;
$this->Session->write('_Token', $token);
Expand All @@ -705,7 +714,9 @@ protected function _validateCsrf($controller) {
$token = $this->Session->read('_Token');
$requestToken = $controller->request->data('_Token.key');
if (isset($token['csrfTokens'][$requestToken]) && $token['csrfTokens'][$requestToken] >= time()) {
$this->Session->delete('_Token.csrfTokens.' . $requestToken);
if ($this->csrfUseOnce) {
$this->Session->delete('_Token.csrfTokens.' . $requestToken);
}
return true;
}
return false;
Expand Down
22 changes: 22 additions & 0 deletions cake/tests/cases/libs/controller/components/security.test.php
Expand Up @@ -1407,4 +1407,26 @@ function testCsrfBlackHoleOnExpiredKey() {
$this->Security->startup($this->Controller);
$this->assertTrue($this->Controller->failed, 'fail() was not called.');
}

/**
* test that csrfUseOnce = false works.
*
* @return void
*/
function testCsrfNotUseOnce() {
$this->Security->validatePost = false;
$this->Security->csrfCheck = true;
$this->Security->csrfUseOnce = false;
$this->Security->csrfExpires = '+10 minutes';

// Generate one token
$this->Security->startup($this->Controller);
$token = $this->Security->Session->read('_Token.csrfTokens');
$this->assertEquals(1, count($token), 'Should only be one token.');

$this->Security->startup($this->Controller);
$token2 = $this->Security->Session->read('_Token.csrfTokens');
$this->assertEquals(1, count($token2), 'Should only be one token.');
$this->assertEquals($token, $token2, 'Tokens should not be different.');
}
}

0 comments on commit 22239b4

Please sign in to comment.