CsrfComponentTest.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Controller\Component;
  16. use Cake\Controller\ComponentRegistry;
  17. use Cake\Controller\Component\CsrfComponent;
  18. use Cake\Event\Event;
  19. use Cake\Network\Request;
  20. use Cake\Network\Response;
  21. use Cake\TestSuite\TestCase;
  22. /**
  23. * CsrfComponent test.
  24. */
  25. class CsrfComponentTest extends TestCase
  26. {
  27. /**
  28. * setup
  29. *
  30. * @return void
  31. */
  32. public function setUp()
  33. {
  34. parent::setUp();
  35. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  36. $this->registry = new ComponentRegistry($controller);
  37. $this->component = new CsrfComponent($this->registry);
  38. }
  39. /**
  40. * teardown
  41. *
  42. * @return void
  43. */
  44. public function tearDown()
  45. {
  46. parent::tearDown();
  47. unset($this->component);
  48. }
  49. /**
  50. * Test setting the cookie value
  51. *
  52. * @return void
  53. * @triggers Controller.startup $controller
  54. */
  55. public function testSettingCookie()
  56. {
  57. $_SERVER['REQUEST_METHOD'] = 'GET';
  58. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  59. $controller->request = new Request(['webroot' => '/dir/']);
  60. $controller->response = new Response();
  61. $event = new Event('Controller.startup', $controller);
  62. $this->component->startup($event);
  63. $cookie = $controller->response->cookie('csrfToken');
  64. $this->assertNotEmpty($cookie, 'Should set a token.');
  65. $this->assertRegExp('/^[a-f0-9]+$/', $cookie['value'], 'Should look like a hash.');
  66. $this->assertEquals(0, $cookie['expiry'], 'session duration.');
  67. $this->assertEquals('/dir/', $cookie['path'], 'session path.');
  68. $this->assertEquals($cookie['value'], $controller->request->params['_csrfToken']);
  69. }
  70. /**
  71. * Data provider for HTTP method tests.
  72. *
  73. * @return void
  74. */
  75. public static function httpMethodProvider()
  76. {
  77. return [
  78. ['PATCH'], ['PUT'], ['POST'], ['DELETE']
  79. ];
  80. }
  81. /**
  82. * Test that the X-CSRF-Token works with the various http methods.
  83. *
  84. * @dataProvider httpMethodProvider
  85. * @return void
  86. * @triggers Controller.startup $controller
  87. */
  88. public function testValidTokenInHeader($method)
  89. {
  90. $_SERVER['REQUEST_METHOD'] = $method;
  91. $_SERVER['HTTP_X_CSRF_TOKEN'] = 'testing123';
  92. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  93. $controller->request = new Request(['cookies' => ['csrfToken' => 'testing123']]);
  94. $controller->response = new Response();
  95. $event = new Event('Controller.startup', $controller);
  96. $result = $this->component->startup($event);
  97. $this->assertNull($result, 'No exception means valid.');
  98. }
  99. /**
  100. * Test that the X-CSRF-Token works with the various http methods.
  101. *
  102. * @dataProvider httpMethodProvider
  103. * @expectedException \Cake\Network\Exception\ForbiddenException
  104. * @return void
  105. * @triggers Controller.startup $controller
  106. */
  107. public function testInvalidTokenInHeader($method)
  108. {
  109. $_SERVER['REQUEST_METHOD'] = $method;
  110. $_SERVER['HTTP_X_CSRF_TOKEN'] = 'nope';
  111. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  112. $controller->request = new Request([
  113. 'cookies' => ['csrfToken' => 'testing123']
  114. ]);
  115. $controller->response = new Response();
  116. $event = new Event('Controller.startup', $controller);
  117. $this->component->startup($event);
  118. }
  119. /**
  120. * Test that request data works with the various http methods.
  121. *
  122. * @dataProvider httpMethodProvider
  123. * @return void
  124. * @triggers Controller.startup $controller
  125. */
  126. public function testValidTokenRequestData($method)
  127. {
  128. $_SERVER['REQUEST_METHOD'] = $method;
  129. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  130. $controller->request = new Request([
  131. 'post' => ['_csrfToken' => 'testing123'],
  132. 'cookies' => ['csrfToken' => 'testing123']
  133. ]);
  134. $controller->response = new Response();
  135. $event = new Event('Controller.startup', $controller);
  136. $result = $this->component->startup($event);
  137. $this->assertNull($result, 'No exception means valid.');
  138. }
  139. /**
  140. * Test that request data works with the various http methods.
  141. *
  142. * @dataProvider httpMethodProvider
  143. * @expectedException \Cake\Network\Exception\ForbiddenException
  144. * @return void
  145. * @triggers Controller.startup $controller
  146. */
  147. public function testInvalidTokenRequestData($method)
  148. {
  149. $_SERVER['REQUEST_METHOD'] = $method;
  150. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  151. $controller->request = new Request([
  152. 'post' => ['_csrfToken' => 'nope'],
  153. 'cookies' => ['csrfToken' => 'testing123']
  154. ]);
  155. $controller->response = new Response();
  156. $event = new Event('Controller.startup', $controller);
  157. $this->component->startup($event);
  158. }
  159. /**
  160. * Test that CSRF checks are not applied to request action requests.
  161. *
  162. * @return void
  163. * @triggers Controller.startup $controller
  164. */
  165. public function testCsrfValidationSkipsRequestAction()
  166. {
  167. $_SERVER['REQUEST_METHOD'] = 'POST';
  168. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  169. $controller->request = new Request([
  170. 'params' => ['requested' => 1],
  171. 'post' => ['_csrfToken' => 'nope'],
  172. 'cookies' => ['csrfToken' => 'testing123']
  173. ]);
  174. $controller->response = new Response();
  175. $event = new Event('Controller.startup', $controller);
  176. $result = $this->component->startup($event);
  177. $this->assertNull($result, 'No error.');
  178. $this->assertEquals('testing123', $controller->request->params['_csrfToken']);
  179. }
  180. /**
  181. * Test that the configuration options work.
  182. *
  183. * @return void
  184. * @triggers Controller.startup $controller
  185. */
  186. public function testConfigurationCookieCreate()
  187. {
  188. $_SERVER['REQUEST_METHOD'] = 'GET';
  189. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  190. $controller->request = new Request(['webroot' => '/dir/']);
  191. $controller->response = new Response();
  192. $component = new CsrfComponent($this->registry, [
  193. 'cookieName' => 'token',
  194. 'expiry' => 90,
  195. 'secure' => true
  196. ]);
  197. $event = new Event('Controller.startup', $controller);
  198. $component->startup($event);
  199. $this->assertEmpty($controller->response->cookie('csrfToken'));
  200. $cookie = $controller->response->cookie('token');
  201. $this->assertNotEmpty($cookie, 'Should set a token.');
  202. $this->assertRegExp('/^[a-f0-9]+$/', $cookie['value'], 'Should look like a hash.');
  203. $this->assertEquals(90, $cookie['expiry'], 'session duration.');
  204. $this->assertEquals('/dir/', $cookie['path'], 'session path.');
  205. $this->assertTrue($cookie['secure'], 'cookie security flag missing');
  206. }
  207. /**
  208. * Test that the configuration options work.
  209. *
  210. * @return void
  211. * @triggers Controller.startup $controller
  212. */
  213. public function testConfigurationValidate()
  214. {
  215. $_SERVER['REQUEST_METHOD'] = 'POST';
  216. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  217. $controller->request = new Request([
  218. 'cookies' => ['csrfToken' => 'nope', 'token' => 'yes'],
  219. 'post' => ['_csrfToken' => 'no match', 'token' => 'yes'],
  220. ]);
  221. $controller->response = new Response();
  222. $component = new CsrfComponent($this->registry, [
  223. 'cookieName' => 'token',
  224. 'field' => 'token',
  225. 'expiry' => 90,
  226. ]);
  227. $event = new Event('Controller.startup', $controller);
  228. $result = $component->startup($event);
  229. $this->assertNull($result, 'Config settings should work.');
  230. }
  231. }