From a10f1478ee4f30f0f93cb23275bb5298c3f147d1 Mon Sep 17 00:00:00 2001 From: mark_story Date: Sat, 2 Oct 2010 00:20:58 -0400 Subject: [PATCH] Adding support for nonce expiry. Adding simple time based nonce expiration. This does a simple cleanup on each request, to remove stale tokens. Tests added. --- cake/libs/controller/components/security.php | 18 ++++++++++++++++++ .../controller/components/security.test.php | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/cake/libs/controller/components/security.php b/cake/libs/controller/components/security.php index c300da19f..335369cd0 100644 --- a/cake/libs/controller/components/security.php +++ b/cake/libs/controller/components/security.php @@ -705,6 +705,7 @@ class SecurityComponent extends Component { } if (!empty($tokenData['csrfTokens'])) { $token['csrfTokens'] += $tokenData['csrfTokens']; + $token['csrfTokens'] = $this->_expireTokens($token['csrfTokens']); } } $controller->request->params['_Token'] = $token; @@ -729,6 +730,23 @@ class SecurityComponent extends Component { return false; } +/** + * Expire CSRF nonces and remove them from the valid tokens. + * Uses a simple timeout to expire the tokens. + * + * @param array $tokens An array of nonce => expires. + * @return An array of nonce => expires. + */ + protected function _expireTokens($tokens) { + $tokenExpiryTime = strtotime($this->csrfExpires); + foreach ($tokens as $nonce => $expires) { + if ($expires < $tokenExpiryTime) { + unset($tokens[$nonce]); + } + } + return $tokens; + } + /** * Sets the default login options for an HTTP-authenticated request * diff --git a/cake/tests/cases/libs/controller/components/security.test.php b/cake/tests/cases/libs/controller/components/security.test.php index 30d9389ef..cd072731f 100644 --- a/cake/tests/cases/libs/controller/components/security.test.php +++ b/cake/tests/cases/libs/controller/components/security.test.php @@ -1296,4 +1296,23 @@ DIGEST; $token = $this->Security->Session->read('_Token'); $this->assertFalse(isset($token['csrfTokens']['nonce1']), 'Token was not consumed'); } + +/** + * test that expired values in the csrfTokens are cleaned up. + * + * @return void + */ + function testCsrfNonceVacuum() { + $this->Security->validatePost = false; + $this->Security->csrfCheck = true; + $this->Security->csrfExpires = '+10 minutes'; + + $this->Security->Session->write('_Token.csrfTokens', array( + 'poof' => strtotime('-11 minutes'), + 'dust' => strtotime('-20 minutes') + )); + $this->Security->startup($this->Controller); + $tokens = $this->Security->Session->read('_Token.csrfTokens'); + $this->assertEquals(1, count($tokens), 'Too many tokens left behind'); + } }