Skip to content

Commit

Permalink
Adding very minimal and incomplete implementation of csrf consumption.
Browse files Browse the repository at this point in the history
Tests added for consuming csrf tokens on each request.
  • Loading branch information
markstory committed Oct 1, 2010
1 parent dc6b33f commit 711e736
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 8 deletions.
28 changes: 24 additions & 4 deletions cake/libs/controller/components/security.php
Expand Up @@ -216,9 +216,12 @@ public function startup(&$controller) {

if ($isPost && $isRequestAction && $this->validatePost) {
if ($this->_validatePost($controller) === false) {
if (!$this->blackHole($controller, 'auth')) {
return null;
}
return $this->blackHole($controller, 'auth');
}
}
if ($isPost && $this->csrfCheck) {
if ($this->_validateCsrf($controller) === false) {
return $this->blackHole($controller, 'csrf');
}
}
$this->_generateToken($controller);
Expand Down Expand Up @@ -434,7 +437,7 @@ function blackHole(&$controller, $error = '') {
$code = 401;
$controller->header($this->loginRequest());
}
$controller->redirect(null, $code, true);
return $controller->redirect(null, $code, true);
} else {
return $this->_callback($controller, $this->blackHoleCallback, array($error));
}
Expand Down Expand Up @@ -709,6 +712,23 @@ protected function _generateToken(&$controller) {
return true;
}

/**
* Validate that the controller has a CSRF token in the POST data
* and that the token is legit/not expired.
*
* @param Controller $controller A controller to check
* @return boolean Valid csrf token.
*/
protected function _validateCsrf($controller) {
$token = $this->Session->read('_Token');
$requestToken = $controller->request->data('_Token.nonce');
if (isset($token['csrfTokens'][$requestToken])) {
$this->Session->delete('_Token.csrfTokens.' . $requestToken);
return true;
}
return false;
}

/**
* Sets the default login options for an HTTP-authenticated request
*
Expand Down
45 changes: 41 additions & 4 deletions cake/tests/cases/libs/controller/components/security.test.php
Expand Up @@ -153,6 +153,7 @@ function setUp() {
$this->Controller->Security = $this->Controller->TestSecurity;
$this->Controller->Security->blackHoleCallback = 'fail';
$this->Security = $this->Controller->Security;
$this->Security->csrfCheck = false;

Configure::write('Security.salt', 'foo!');
}
Expand Down Expand Up @@ -233,7 +234,7 @@ function testRequirePostSucceed() {
$_SERVER['REQUEST_METHOD'] = 'POST';
$this->Controller->request['action'] = 'posted';
$this->Controller->Security->requirePost('posted');
$this->Controller->Security->startup($this->Controller);
$this->Security->startup($this->Controller);
$this->assertFalse($this->Controller->failed);
}

Expand Down Expand Up @@ -451,8 +452,8 @@ function testDigestAuth() {
function testRequireGetSucceedWrongMethod() {
$_SERVER['REQUEST_METHOD'] = 'POST';
$this->Controller->request['action'] = 'posted';
$this->Controller->Security->requireGet('getted');
$this->Controller->Security->startup($this->Controller);
$this->Security->requireGet('getted');
$this->Security->startup($this->Controller);
$this->assertFalse($this->Controller->failed);
}

Expand Down Expand Up @@ -1245,7 +1246,12 @@ function testCsrfSettings() {
$this->assertEquals(count($token['csrfTokens']), 1, 'Missing the csrf token.');
$this->assertEquals(strtotime('+10 minutes'), current($token['csrfTokens']), 'Token expiry does not match');
}


/**
* Test setting multiple nonces, when startup() is called more than once, (ie more than one request.)
*
* @return void
*/
function testCsrfSettingMultipleNonces() {
$this->Security->validatePost = false;
$this->Security->csrfCheck = true;
Expand All @@ -1259,4 +1265,35 @@ function testCsrfSettingMultipleNonces() {
$this->assertEquals(strtotime('+10 minutes'), $expires, 'Token expiry does not match');
}
}

/**
* test that nonces are consumed by form submits.
*
* @return void
*/
function testCsrfNonceConsumption() {
$this->Security->validatePost = false;
$this->Security->csrfCheck = true;
$this->Security->csrfExpires = '+10 minutes';

$this->Security->Session->write('_Token.csrfTokens', array('nonce1' => strtotime('+10 minutes')));

$this->Controller->request = $this->getMock('CakeRequest', array('is'));
$this->Controller->request->expects($this->once())->method('is')
->with('post')
->will($this->returnValue(true));

$this->Controller->request->params['action'] = 'index';
$this->Controller->request->data = array(
'_Token' => array(
'nonce' => 'nonce1'
),
'Post' => array(
'title' => 'Woot'
)
);
$this->Security->startup($this->Controller);
$token = $this->Security->Session->read('_Token');
$this->assertFalse(isset($token['csrfTokens']['nonce1']), 'Token was not consumed');
}
}

0 comments on commit 711e736

Please sign in to comment.