Browse Source

Adding more tests for the CsrfProtectionMiddleware

Florian Krämer 9 years ago
parent
commit
f25aa4308d

+ 7 - 8
src/Http/Middleware/CsrfProtectionMiddleware.php

@@ -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;
@@ -83,7 +81,7 @@ class CsrfProtectionMiddleware
      * @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;
@@ -97,11 +95,12 @@ class CsrfProtectionMiddleware
             $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);
 
@@ -119,7 +118,7 @@ class CsrfProtectionMiddleware
      * @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);
@@ -143,7 +142,7 @@ class CsrfProtectionMiddleware
      * @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);
@@ -168,7 +167,7 @@ class CsrfProtectionMiddleware
      * @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;

+ 96 - 10
tests/TestCase/Http/Middleware/CsrfProtectionMiddlewareTest.php

@@ -17,6 +17,7 @@ namespace Cake\Test\TestCase\Http\Middleware;
 use Cake\Http\Middleware\CsrfProtectionMiddleware;
 use Cake\Http\Response;
 use Cake\Http\ServerRequest;
+use Cake\I18n\Time;
 use Cake\TestSuite\TestCase;
 
 /**
@@ -97,15 +98,17 @@ class CsrfProtectionMiddlewareTest extends TestCase
         ]);
         $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);
     }
 
     /**
@@ -193,10 +196,14 @@ class CsrfProtectionMiddlewareTest extends TestCase
         ]);
         $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);
+
     }
 
     /**
@@ -263,4 +270,83 @@ class CsrfProtectionMiddlewareTest extends TestCase
         $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());
+    }
 }