Skip to content

Commit

Permalink
Adding more tests for the CsrfProtectionMiddleware
Browse files Browse the repository at this point in the history
  • Loading branch information
burzum committed Apr 20, 2017
1 parent 0fa62a6 commit f25aa43
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 18 deletions.
15 changes: 7 additions & 8 deletions src/Http/Middleware/CsrfProtectionMiddleware.php
Expand Up @@ -14,8 +14,6 @@
*/
namespace Cake\Http\Middleware;

use Cake\Http\Response;
use Cake\Http\ServerRequest;
use Cake\I18n\Time;
use Cake\Network\Exception\InvalidCsrfTokenException;
use Cake\Utility\Security;
Expand Down Expand Up @@ -83,7 +81,7 @@ public function __construct(array $config = [])
* @param callable $next Callback to invoke the next middleware.
* @return \Psr\Http\Message\ResponseInterface A response
*/
public function __invoke(ServerRequestInterface &$request, ResponseInterface $response, $next)
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
{
$cookies = $request->getCookieParams();
$cookieData = null;
Expand All @@ -97,11 +95,12 @@ public function __invoke(ServerRequestInterface &$request, ResponseInterface $re
$request = $request->withAttribute('params', $params);
}

$method = $request->getMethod();
if ($method === 'requested') {
$requested = $request->getParam('requested');
if ($requested === 1) {
return $next($request, $response);
}

$method = $request->getMethod();
if ($method === 'GET' && $cookieData === null) {
$this->_setToken($request, $response);

Expand All @@ -119,7 +118,7 @@ public function __invoke(ServerRequestInterface &$request, ResponseInterface $re
* @param \Cake\Http\ServerRequest $request The request object.
* @return void
*/
protected function _validateAndUnsetTokenField(ServerRequest $request)
protected function _validateAndUnsetTokenField(ServerRequestInterface $request)
{
if (in_array($request->getMethod(), ['PUT', 'POST', 'DELETE', 'PATCH']) || $request->getData()) {
$this->_validateToken($request);
Expand All @@ -143,7 +142,7 @@ protected function _validateAndUnsetTokenField(ServerRequest $request)
* @param \Cake\Http\Response $response The response object.
* @return void
*/
protected function _setToken(ServerRequest &$request, Response &$response)
protected function _setToken(ServerRequestInterface &$request, ResponseInterface &$response)
{
$expiry = new Time($this->_config['expiry']);
$value = hash('sha512', Security::randomBytes(16), false);
Expand All @@ -168,7 +167,7 @@ protected function _setToken(ServerRequest &$request, Response &$response)
* @throws \Cake\Network\Exception\InvalidCsrfTokenException when the CSRF token is invalid or missing.
* @return void
*/
protected function _validateToken(ServerRequest $request)
protected function _validateToken(ServerRequestInterface $request)
{
$cookies = $request->getCookieParams();
$cookie = isset($cookies[$this->_config['cookieName']]) ? $cookies[$this->_config['cookieName']] : null;
Expand Down
106 changes: 96 additions & 10 deletions tests/TestCase/Http/Middleware/CsrfProtectionMiddlewareTest.php
Expand Up @@ -17,6 +17,7 @@
use Cake\Http\Middleware\CsrfProtectionMiddleware;
use Cake\Http\Response;
use Cake\Http\ServerRequest;
use Cake\I18n\Time;
use Cake\TestSuite\TestCase;

/**
Expand Down Expand Up @@ -97,15 +98,17 @@ public function testSettingCookie()
]);
$response = new Response();

$closure = function ($request, $response) {
$cookie = $response->cookie('csrfToken');
$this->assertNotEmpty($cookie, 'Should set a token.');
$this->assertRegExp('/^[a-f0-9]+$/', $cookie['value'], 'Should look like a hash.');
$this->assertEquals(0, $cookie['expire'], 'session duration.');
$this->assertEquals('/dir/', $cookie['path'], 'session path.');
$this->assertEquals($cookie['value'], $request->params['_csrfToken']);
};

$middleware = new CsrfProtectionMiddleware();
$response = $middleware($request, $response, $this->_getNextClosure());
$cookie = $response->cookie('csrfToken');

$this->assertNotEmpty($cookie, 'Should set a token.');
$this->assertRegExp('/^[a-f0-9]+$/', $cookie['value'], 'Should look like a hash.');
$this->assertEquals(0, $cookie['expire'], 'session duration.');
$this->assertEquals('/dir/', $cookie['path'], 'session path.');
$this->assertEquals($cookie['value'], $request->params['_csrfToken']);
$middleware($request, $response, $closure);
}

/**
Expand Down Expand Up @@ -193,10 +196,14 @@ public function testValidTokenRequestData($method)
]);
$response = new Response();

$closure = function ($request, $response) {
$this->assertNull($request->getData('_csrfToken'));
};

// No exception means everything is OK
$middleware = new CsrfProtectionMiddleware();
$middleware($request, $response, $this->_getNextClosure());
$this->assertNull($request->getData('_csrfToken'));
$middleware($request, $response, $closure);

}

/**
Expand Down Expand Up @@ -263,4 +270,83 @@ public function testInvalidTokenMissingCookie($method)
$middleware = new CsrfProtectionMiddleware();
$middleware($request, $response, $this->_getNextClosure());
}

/**
* Test that CSRF checks are not applied to request action requests.
*
* @return void
*/
public function testCsrfValidationSkipsRequestAction()
{
$request = new ServerRequest([
'environment' => ['REQUEST_METHOD' => 'POST'],
'params' => ['requested' => 1],
'post' => ['_csrfToken' => 'nope'],
'cookies' => ['csrfToken' => 'testing123']
]);
$response = new Response();

$closure = function ($request, $response) {
$this->assertEquals('testing123', $request->params['_csrfToken']);
};

$middleware = new CsrfProtectionMiddleware();
$middleware($request, $response, $closure);
}
/**
* Test that the configuration options work.
*
* @return void
*/
public function testConfigurationCookieCreate()
{
$request = new ServerRequest([
'environment' => ['REQUEST_METHOD' => 'GET'],
'webroot' => '/dir/'
]);
$response = new Response();

$closure = function ($request, $response) {
$this->assertEmpty($response->cookie('csrfToken'));
$cookie = $response->cookie('token');
$this->assertNotEmpty($cookie, 'Should set a token.');
$this->assertRegExp('/^[a-f0-9]+$/', $cookie['value'], 'Should look like a hash.');
$this->assertWithinRange((new Time('+1 hour'))->format('U'), $cookie['expire'], 1, 'session duration.');
$this->assertEquals('/dir/', $cookie['path'], 'session path.');
$this->assertTrue($cookie['secure'], 'cookie security flag missing');
$this->assertTrue($cookie['httpOnly'], 'cookie httpOnly flag missing');
};

$middleware = new CsrfProtectionMiddleware([
'cookieName' => 'token',
'expiry' => '+1 hour',
'secure' => true,
'httpOnly' => true
]);
$middleware($request, $response, $closure);
}

/**
* Test that the configuration options work.
*
* There should be no exception thrown.
*
* @return void
*/
public function testConfigurationValidate()
{
$request = new ServerRequest([
'environment' => ['REQUEST_METHOD' => 'POST'],
'cookies' => ['csrfToken' => 'nope', 'token' => 'yes'],
'post' => ['_csrfToken' => 'no match', 'token' => 'yes'],
]);
$response = new Response();

$middleware = new CsrfProtectionMiddleware([
'cookieName' => 'token',
'field' => 'token',
'expiry' => 90,
]);
$middleware($request, $response, $this->_getNextClosure());
}
}

0 comments on commit f25aa43

Please sign in to comment.