CsrfComponentTest.php 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  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. * setup
  28. *
  29. * @return void
  30. */
  31. public function setUp() {
  32. parent::setUp();
  33. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  34. $this->registry = new ComponentRegistry($controller);
  35. $this->component = new CsrfComponent($this->registry);
  36. }
  37. /**
  38. * teardown
  39. *
  40. * @return void
  41. */
  42. public function tearDown() {
  43. parent::tearDown();
  44. unset($this->component);
  45. }
  46. /**
  47. * Test setting the cookie value
  48. *
  49. * @return void
  50. * @triggers Controller.startup $controller
  51. */
  52. public function testSettingCookie() {
  53. $_SERVER['REQUEST_METHOD'] = 'GET';
  54. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  55. $controller->request = new Request(['base' => '/dir']);
  56. $controller->response = new Response();
  57. $event = new Event('Controller.startup', $controller);
  58. $this->component->startUp($event);
  59. $cookie = $controller->response->cookie('csrfToken');
  60. $this->assertNotEmpty($cookie, 'Should set a token.');
  61. $this->assertRegExp('/^[a-f0-9]+$/', $cookie['value'], 'Should look like a hash.');
  62. $this->assertEquals(0, $cookie['expiry'], 'session duration.');
  63. $this->assertEquals('/dir', $cookie['path'], 'session path.');
  64. $this->assertEquals($cookie['value'], $controller->request->params['_csrfToken']);
  65. }
  66. /**
  67. * Data provider for HTTP method tests.
  68. *
  69. * @return void
  70. */
  71. public static function httpMethodProvider() {
  72. return [
  73. ['PATCH'], ['PUT'], ['POST'], ['DELETE']
  74. ];
  75. }
  76. /**
  77. * Test that the X-CSRF-Token works with the various http methods.
  78. *
  79. * @dataProvider httpMethodProvider
  80. * @return void
  81. * @triggers Controller.startup $controller
  82. */
  83. public function testValidTokenInHeader($method) {
  84. $_SERVER['REQUEST_METHOD'] = $method;
  85. $_SERVER['HTTP_X_CSRF_TOKEN'] = 'testing123';
  86. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  87. $controller->request = new Request(['cookies' => ['csrfToken' => 'testing123']]);
  88. $controller->response = new Response();
  89. $event = new Event('Controller.startup', $controller);
  90. $result = $this->component->startUp($event);
  91. $this->assertNull($result, 'No exception means valid.');
  92. }
  93. /**
  94. * Test that the X-CSRF-Token works with the various http methods.
  95. *
  96. * @dataProvider httpMethodProvider
  97. * @expectedException \Cake\Network\Exception\ForbiddenException
  98. * @return void
  99. * @triggers Controller.startup $controller
  100. */
  101. public function testInvalidTokenInHeader($method) {
  102. $_SERVER['REQUEST_METHOD'] = $method;
  103. $_SERVER['HTTP_X_CSRF_TOKEN'] = 'nope';
  104. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  105. $controller->request = new Request([
  106. 'cookies' => ['csrfToken' => 'testing123']
  107. ]);
  108. $controller->response = new Response();
  109. $event = new Event('Controller.startup', $controller);
  110. $this->component->startUp($event);
  111. }
  112. /**
  113. * Test that request data works with the various http methods.
  114. *
  115. * @dataProvider httpMethodProvider
  116. * @return void
  117. * @triggers Controller.startup $controller
  118. */
  119. public function testValidTokenRequestData($method) {
  120. $_SERVER['REQUEST_METHOD'] = $method;
  121. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  122. $controller->request = new Request([
  123. 'post' => ['_csrfToken' => 'testing123'],
  124. 'cookies' => ['csrfToken' => 'testing123']
  125. ]);
  126. $controller->response = new Response();
  127. $event = new Event('Controller.startup', $controller);
  128. $result = $this->component->startUp($event);
  129. $this->assertNull($result, 'No exception means valid.');
  130. }
  131. /**
  132. * Test that request data works with the various http methods.
  133. *
  134. * @dataProvider httpMethodProvider
  135. * @expectedException \Cake\Network\Exception\ForbiddenException
  136. * @return void
  137. * @triggers Controller.startup $controller
  138. */
  139. public function testInvalidTokenRequestData($method) {
  140. $_SERVER['REQUEST_METHOD'] = $method;
  141. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  142. $controller->request = new Request([
  143. 'post' => ['_csrfToken' => 'nope'],
  144. 'cookies' => ['csrfToken' => 'testing123']
  145. ]);
  146. $controller->response = new Response();
  147. $event = new Event('Controller.startup', $controller);
  148. $this->component->startUp($event);
  149. }
  150. /**
  151. * Test that CSRF checks are not applied to request action requests.
  152. *
  153. * @return void
  154. * @triggers Controller.startup $controller
  155. */
  156. public function testCsrfValidationSkipsRequestAction() {
  157. $_SERVER['REQUEST_METHOD'] = 'POST';
  158. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  159. $controller->request = new Request([
  160. 'params' => ['requested' => 1],
  161. 'post' => ['_csrfToken' => 'nope'],
  162. 'cookies' => ['csrfToken' => 'testing123']
  163. ]);
  164. $controller->response = new Response();
  165. $event = new Event('Controller.startup', $controller);
  166. $result = $this->component->startUp($event);
  167. $this->assertNull($result, 'No error.');
  168. $this->assertEquals('testing123', $controller->request->params['_csrfToken']);
  169. }
  170. /**
  171. * Test that the configuration options work.
  172. *
  173. * @return void
  174. * @triggers Controller.startup $controller
  175. */
  176. public function testConfigurationCookieCreate() {
  177. $_SERVER['REQUEST_METHOD'] = 'GET';
  178. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  179. $controller->request = new Request(['base' => '/dir']);
  180. $controller->response = new Response();
  181. $component = new CsrfComponent($this->registry, [
  182. 'cookieName' => 'token',
  183. 'expiry' => 90,
  184. 'secure' => true
  185. ]);
  186. $event = new Event('Controller.startup', $controller);
  187. $component->startUp($event);
  188. $this->assertEmpty($controller->response->cookie('csrfToken'));
  189. $cookie = $controller->response->cookie('token');
  190. $this->assertNotEmpty($cookie, 'Should set a token.');
  191. $this->assertRegExp('/^[a-f0-9]+$/', $cookie['value'], 'Should look like a hash.');
  192. $this->assertEquals(90, $cookie['expiry'], 'session duration.');
  193. $this->assertEquals('/dir', $cookie['path'], 'session path.');
  194. $this->assertTrue($cookie['secure'], 'cookie security flag missing');
  195. }
  196. /**
  197. * Test that the configuration options work.
  198. *
  199. * @return void
  200. * @triggers Controller.startup $controller
  201. */
  202. public function testConfigurationValidate() {
  203. $_SERVER['REQUEST_METHOD'] = 'POST';
  204. $controller = $this->getMock('Cake\Controller\Controller', ['redirect']);
  205. $controller->request = new Request([
  206. 'cookies' => ['csrfToken' => 'nope', 'token' => 'yes'],
  207. 'post' => ['_csrfToken' => 'no match', 'token' => 'yes'],
  208. ]);
  209. $controller->response = new Response();
  210. $component = new CsrfComponent($this->registry, [
  211. 'cookieName' => 'token',
  212. 'field' => 'token',
  213. 'expiry' => 90,
  214. ]);
  215. $event = new Event('Controller.startup', $controller);
  216. $result = $component->startUp($event);
  217. $this->assertNull($result, 'Config settings should work.');
  218. }
  219. }