Adding very minimal and incomplete implementation of csrf consumption.

Tests added for consuming csrf tokens on each request.
This commit is contained in:
mark_story 2010-10-01 00:13:34 -04:00
parent dc6b33f80e
commit 711e736cd3
2 changed files with 65 additions and 8 deletions

View file

@ -216,10 +216,13 @@ class SecurityComponent extends Component {
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);
}
@ -434,7 +437,7 @@ class SecurityComponent extends Component {
$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));
}
@ -709,6 +712,23 @@ class SecurityComponent extends Component {
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
*

View file

@ -153,6 +153,7 @@ class SecurityComponentTest extends CakeTestCase {
$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!');
}
@ -233,7 +234,7 @@ class SecurityComponentTest extends CakeTestCase {
$_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);
}
@ -451,8 +452,8 @@ DIGEST;
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);
}
@ -1246,6 +1247,11 @@ DIGEST;
$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;
@ -1259,4 +1265,35 @@ DIGEST;
$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');
}
}