diff --git a/Cake/Controller/Component/CsrfComponent.php b/Cake/Controller/Component/CsrfComponent.php new file mode 100644 index 00000000000..0a6f613f447 --- /dev/null +++ b/Cake/Controller/Component/CsrfComponent.php @@ -0,0 +1,117 @@ +Form->create(...)` is used in a view. + */ +class CsrfComponent extends Component { + +/** + * Settings for the CSRF handling. + * + * - cookieName = The name of the cookie to send. + * - expiry = How long the CSRF token should last. Defaults to browser session. + * - field = The form field to check. Changing this will also require configuring + * FormHelper. + * + * @var array + */ + public $settings = [ + 'cookieName' => 'csrfToken', + 'expiry' => 0, + 'field' => '_csrfToken', + ]; + +/** + * Startup callback. + * + * Validates the CSRF token for POST data. If + * the request is a GET request, and the cookie value + * is absent a cookie will be set. + * + * @param Cake\Event\Event $event + * @return void + */ + public function startup(Event $event) { + $controller = $event->subject(); + $request = $controller->request; + $response = $controller->response; + $cookieName = $this->settings['cookieName']; + + if ($request->is('get') && $request->cookie($cookieName) === null) { + $this->_setCookie($request, $response); + } + if ($request->is(['patch', 'put', 'post', 'delete'])) { + $this->_validateToken($request); + } + } + +/** + * Set the cookie in the response. + * + * @param Cake\Network\Request $request The request object. + * @param Cake\Network\Response $response The response object. + */ + protected function _setCookie(Request $request, Response $response) { + $settings = $this->settings; + $response->cookie([ + 'name' => $settings['cookieName'], + 'value' => Security::hash(String::uuid(), 'sha1', true), + 'expiry' => $settings['expiry'], + 'path' => $request->base, + ]); + } + +/** + * Validate the request data against the cookie token. + * + * @param Cake\Network\Request $request The request to validate against. + * @throws Cake\Error\ForbiddenException when the CSRF token is invalid or missing. + * @return void + */ + protected function _validateToken(Request $request) { + $settings = $this->settings; + + $cookie = $request->cookie($settings['cookieName']); + $post = $request->data($settings['field']); + $header = $request->header('X-CSRF-Token'); + + if ($post !== $cookie && $header !== $cookie) { + throw new ForbiddenException(__d('cake', 'Invalid CSRF token.')); + } + } + +}