form->create($object); ?> * security->requestToken(); ?> * // Other fields... * form->end(); ?> * ``` * * ``` * // controllers/CommentsController.php: * public function add() { * if ($this->request->data && !RequestToken::check($this->request)) { * // Key didn't match the CSRF token. Regenerate the session token and * // prompt the user to retry the form submission. * RequestToken::get(['regenerate' => true]); * return; * } * // Handle a normal request... * } * ``` * * @link http://shiflett.org/articles/cross-site-request-forgeries Cross-Site Request Forgeries * @see lithium\template\helper\Security::requestToken() */ class RequestToken { /** * Class dependencies. * * @var array */ protected static $_classes = [ 'session' => 'lithium\storage\Session' ]; /** * Used to get or reconfigure dependencies with custom classes. * * @param array $config When assigning new configuration, should be an array containing a * `'classes'` key. * @return array If `$config` is empty, returns an array with a `'classes'` key containing class * dependencies. Otherwise returns `null`. */ public static function config(array $config = []) { if (!$config) { return ['classes' => static::$_classes]; } foreach ($config as $key => $val) { $key = "_{$key}"; if (isset(static::${$key})) { static::${$key} = $val + static::${$key}; } } } /** * Generates (or regenerates) a cryptographically-secure token to be used for the life of the * client session, and stores the token using the `Session` class. * * @see lithium\security\Hash::calculate() * @param array $options An array of options to be used when generating or storing the token: * - `'regenerate'` _boolean_: If `true`, will force the regeneration of a the * token, even if one is already available in the session. Defaults to `false`. * - `'sessionKey'` _string_: The key used for session storage and retrieval. * Defaults to `'security.token'`. * - `'salt'` _string_: If the token is being generated (or regenerated), sets a * custom salt value to be used by `Hash::calculate()`. * - `'type'` _string_: The hashing algorithm used by `Hash::calculate()` when * generating the token. Defaults to `'sha512'`. * @return string Returns a cryptographically-secure client session token. */ public static function get(array $options = []) { $defaults = [ 'regenerate' => false, 'sessionKey' => 'security.token', 'salt' => null, 'type' => 'sha512' ]; $options += $defaults; $session = static::$_classes['session']; if ($options['regenerate'] || !($token = $session::read($options['sessionKey']))) { $token = Hash::calculate(uniqid(microtime(true)), $options); $session::write($options['sessionKey'], $token); } return $token; } /** * Generates a single-use key to be embedded in a form or used with another non-idempotent * request (a request that changes the state of the server or application), that will match * against a client session token using the `check()` method. * * @see lithium\security\validation\RequestToken::check() * @param array $options An array of options to be passed to `RequestToken::get()`. * @return string Returns a hashed key string for use with `RequestToken::check()`. */ public static function key(array $options = []) { return Password::hash(static::get($options)); } /** * Checks a single-use hash key against the session token that generated it, using * a cryptographically-secure verification method. Accepts either the request key as a string, * or a `Request` object with a `$data` property containing a `['security']['token']` key. * * For example, the following two controller code samples are equivalent: * * ``` * $key = $this->request->data['security']['token']; * * if (!RequestToken::check($key)) { * // Handle invalid request... * } * ``` * * ``` * if (!RequestToken::check($this->request)) { * // Handle invalid request... * } * ``` * * @param mixed $key Either the actual key as a string, or a `Request` object containing the * key. * @param array $options The options to use when matching the key to the token: * - `'sessionKey'` _string_: The key used when reading the token from the session. * @return boolean Returns `true` if the hash key is a cryptographic match to the stored * session token. Returns `false` on failure, which indicates a forged request attempt. */ public static function check($key, array $options = []) { $defaults = ['sessionKey' => 'security.token']; $options += $defaults; $session = static::$_classes['session']; if (is_object($key) && isset($key->data)) { $result = Set::extract($key->data, '/security/token'); $key = $result ? $result[0] : null; } return Password::check($session::read($options['sessionKey']), (string) $key); } } ?>