SecurityComponent.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. <?php
  2. /**
  3. * Security Component
  4. *
  5. * PHP 5
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8. * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * Redistributions of files must retain the above copyright notice.
  12. *
  13. * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
  14. * @link http://cakephp.org CakePHP(tm) Project
  15. * @package cake.libs.controller.components
  16. * @since CakePHP(tm) v 0.10.8.2156
  17. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  18. */
  19. App::uses('Component', 'Controller');
  20. App::uses('String', 'Utility');
  21. App::uses('Security', 'Utility');
  22. /**
  23. * SecurityComponent
  24. *
  25. * @package cake.libs.controller.components
  26. * @link http://book.cakephp.org/view/1296/Security-Component
  27. */
  28. class SecurityComponent extends Component {
  29. /**
  30. * The controller method that will be called if this request is black-hole'd
  31. *
  32. * @var string
  33. * @access public
  34. */
  35. public $blackHoleCallback = null;
  36. /**
  37. * List of controller actions for which a POST request is required
  38. *
  39. * @var array
  40. * @access public
  41. * @see SecurityComponent::requirePost()
  42. */
  43. public $requirePost = array();
  44. /**
  45. * List of controller actions for which a GET request is required
  46. *
  47. * @var array
  48. * @access public
  49. * @see SecurityComponent::requireGet()
  50. */
  51. public $requireGet = array();
  52. /**
  53. * List of controller actions for which a PUT request is required
  54. *
  55. * @var array
  56. * @access public
  57. * @see SecurityComponent::requirePut()
  58. */
  59. public $requirePut = array();
  60. /**
  61. * List of controller actions for which a DELETE request is required
  62. *
  63. * @var array
  64. * @access public
  65. * @see SecurityComponent::requireDelete()
  66. */
  67. public $requireDelete = array();
  68. /**
  69. * List of actions that require an SSL-secured connection
  70. *
  71. * @var array
  72. * @access public
  73. * @see SecurityComponent::requireSecure()
  74. */
  75. public $requireSecure = array();
  76. /**
  77. * List of actions that require a valid authentication key
  78. *
  79. * @var array
  80. * @access public
  81. * @see SecurityComponent::requireAuth()
  82. */
  83. public $requireAuth = array();
  84. /**
  85. * List of actions that require an HTTP-authenticated login (basic or digest)
  86. *
  87. * @var array
  88. * @access public
  89. * @see SecurityComponent::requireLogin()
  90. */
  91. public $requireLogin = array();
  92. /**
  93. * Login options for SecurityComponent::requireLogin()
  94. *
  95. * @var array
  96. * @access public
  97. * @see SecurityComponent::requireLogin()
  98. */
  99. public $loginOptions = array('type' => '', 'prompt' => null);
  100. /**
  101. * An associative array of usernames/passwords used for HTTP-authenticated logins.
  102. *
  103. * @var array
  104. * @access public
  105. * @see SecurityComponent::requireLogin()
  106. */
  107. public $loginUsers = array();
  108. /**
  109. * Controllers from which actions of the current controller are allowed to receive
  110. * requests.
  111. *
  112. * @var array
  113. * @access public
  114. * @see SecurityComponent::requireAuth()
  115. */
  116. public $allowedControllers = array();
  117. /**
  118. * Actions from which actions of the current controller are allowed to receive
  119. * requests.
  120. *
  121. * @var array
  122. * @access public
  123. * @see SecurityComponent::requireAuth()
  124. */
  125. public $allowedActions = array();
  126. /**
  127. * Form fields to disable
  128. *
  129. * @var array
  130. * @access public
  131. */
  132. public $disabledFields = array();
  133. /**
  134. * Whether to validate POST data. Set to false to disable for data coming from 3rd party
  135. * services, etc.
  136. *
  137. * @var boolean
  138. * @access public
  139. */
  140. public $validatePost = true;
  141. /**
  142. * Whether to use CSRF protected forms. Set to false to disable CSRF protection on forms.
  143. *
  144. * @var boolean
  145. * @see http://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
  146. * @see SecurityComponent::$csrfExpires
  147. */
  148. public $csrfCheck = true;
  149. /**
  150. * The duration from when a CSRF token is created that it will expire on.
  151. * Each form/page request will generate a new token that can only be submitted once unless
  152. * it expires. Can be any value compatible with strtotime()
  153. *
  154. * @var string
  155. */
  156. public $csrfExpires = '+30 minutes';
  157. /**
  158. * Controls whether or not CSRF tokens are use and burn. Set to false to not generate
  159. * new tokens on each request. One token will be reused until it expires. This reduces
  160. * the chances of users getting invalid requests because of token consumption.
  161. * It has the side effect of making CSRF less secure, as tokens are reusable.
  162. *
  163. * @var boolean
  164. */
  165. public $csrfUseOnce = true;
  166. /**
  167. * Other components used by the Security component
  168. *
  169. * @var array
  170. * @access public
  171. */
  172. public $components = array('Session');
  173. /**
  174. * Holds the current action of the controller
  175. *
  176. * @var string
  177. */
  178. protected $_action = null;
  179. /**
  180. * Request object
  181. *
  182. * @var CakeRequest
  183. */
  184. public $request;
  185. /**
  186. * Component startup. All security checking happens here.
  187. *
  188. * @param object $controller Instantiating controller
  189. * @return void
  190. */
  191. public function startup($controller) {
  192. $this->request = $controller->request;
  193. $this->_action = strtolower($this->request->params['action']);
  194. $this->_methodsRequired($controller);
  195. $this->_secureRequired($controller);
  196. $this->_authRequired($controller);
  197. $this->_loginRequired($controller);
  198. $isPost = ($this->request->is('post') || $this->request->is('put'));
  199. $isRequestAction = (
  200. !isset($controller->request->params['requested']) ||
  201. $controller->request->params['requested'] != 1
  202. );
  203. if ($isPost && $isRequestAction && $this->validatePost) {
  204. if ($this->_validatePost($controller) === false) {
  205. return $this->blackHole($controller, 'auth');
  206. }
  207. }
  208. if ($isPost && $this->csrfCheck) {
  209. if ($this->_validateCsrf($controller) === false) {
  210. return $this->blackHole($controller, 'csrf');
  211. }
  212. }
  213. $this->_generateToken($controller);
  214. }
  215. /**
  216. * Sets the actions that require a POST request, or empty for all actions
  217. *
  218. * @return void
  219. * @link http://book.cakephp.org/view/1299/requirePost
  220. */
  221. public function requirePost() {
  222. $args = func_get_args();
  223. $this->_requireMethod('Post', $args);
  224. }
  225. /**
  226. * Sets the actions that require a GET request, or empty for all actions
  227. *
  228. * @return void
  229. */
  230. public function requireGet() {
  231. $args = func_get_args();
  232. $this->_requireMethod('Get', $args);
  233. }
  234. /**
  235. * Sets the actions that require a PUT request, or empty for all actions
  236. *
  237. * @return void
  238. */
  239. public function requirePut() {
  240. $args = func_get_args();
  241. $this->_requireMethod('Put', $args);
  242. }
  243. /**
  244. * Sets the actions that require a DELETE request, or empty for all actions
  245. *
  246. * @return void
  247. */
  248. public function requireDelete() {
  249. $args = func_get_args();
  250. $this->_requireMethod('Delete', $args);
  251. }
  252. /**
  253. * Sets the actions that require a request that is SSL-secured, or empty for all actions
  254. *
  255. * @return void
  256. * @link http://book.cakephp.org/view/1300/requireSecure
  257. */
  258. public function requireSecure() {
  259. $args = func_get_args();
  260. $this->_requireMethod('Secure', $args);
  261. }
  262. /**
  263. * Sets the actions that require an authenticated request, or empty for all actions
  264. *
  265. * @return void
  266. * @link http://book.cakephp.org/view/1301/requireAuth
  267. */
  268. public function requireAuth() {
  269. $args = func_get_args();
  270. $this->_requireMethod('Auth', $args);
  271. }
  272. /**
  273. * Sets the actions that require an HTTP-authenticated request, or empty for all actions
  274. *
  275. * @return void
  276. * @link http://book.cakephp.org/view/1302/requireLogin
  277. */
  278. public function requireLogin() {
  279. $args = func_get_args();
  280. $base = $this->loginOptions;
  281. foreach ($args as $i => $arg) {
  282. if (is_array($arg)) {
  283. $this->loginOptions = $arg;
  284. unset($args[$i]);
  285. }
  286. }
  287. $this->loginOptions = array_merge($base, $this->loginOptions);
  288. $this->_requireMethod('Login', $args);
  289. if (isset($this->loginOptions['users'])) {
  290. $this->loginUsers =& $this->loginOptions['users'];
  291. }
  292. }
  293. /**
  294. * Attempts to validate the login credentials for an HTTP-authenticated request
  295. *
  296. * @param string $type Either 'basic', 'digest', or null. If null/empty, will try both.
  297. * @return mixed If successful, returns an array with login name and password, otherwise null.
  298. * @link http://book.cakephp.org/view/1303/loginCredentials-string-type
  299. */
  300. public function loginCredentials($type = null) {
  301. switch (strtolower($type)) {
  302. case 'basic':
  303. $login = array('username' => env('PHP_AUTH_USER'), 'password' => env('PHP_AUTH_PW'));
  304. if (!empty($login['username'])) {
  305. return $login;
  306. }
  307. break;
  308. case 'digest':
  309. default:
  310. $digest = null;
  311. if (version_compare(PHP_VERSION, '5.1') != -1) {
  312. $digest = env('PHP_AUTH_DIGEST');
  313. } elseif (function_exists('apache_request_headers')) {
  314. $headers = apache_request_headers();
  315. if (isset($headers['Authorization']) && !empty($headers['Authorization']) && substr($headers['Authorization'], 0, 7) == 'Digest ') {
  316. $digest = substr($headers['Authorization'], 7);
  317. }
  318. } else {
  319. // Server doesn't support digest-auth headers
  320. trigger_error(__('SecurityComponent::loginCredentials() - Server does not support digest authentication'), E_USER_WARNING);
  321. }
  322. if (!empty($digest)) {
  323. return $this->parseDigestAuthData($digest);
  324. }
  325. break;
  326. }
  327. return null;
  328. }
  329. /**
  330. * Generates the text of an HTTP-authentication request header from an array of options.
  331. *
  332. * @param array $options Set of options for header
  333. * @return string HTTP-authentication request header
  334. * @link http://book.cakephp.org/view/1304/loginRequest-array-options
  335. */
  336. public function loginRequest($options = array()) {
  337. $options = array_merge($this->loginOptions, $options);
  338. $this->_setLoginDefaults($options);
  339. $auth = 'WWW-Authenticate: ' . ucfirst($options['type']);
  340. $out = array('realm="' . $options['realm'] . '"');
  341. if (strtolower($options['type']) == 'digest') {
  342. $out[] = 'qop="auth"';
  343. $out[] = 'nonce="' . uniqid("") . '"';
  344. $out[] = 'opaque="' . md5($options['realm']) . '"';
  345. }
  346. return $auth . ' ' . implode(',', $out);
  347. }
  348. /**
  349. * Parses an HTTP digest authentication response, and returns an array of the data, or null on failure.
  350. *
  351. * @param string $digest Digest authentication response
  352. * @return array Digest authentication parameters
  353. * @link http://book.cakephp.org/view/1305/parseDigestAuthData-string-digest
  354. */
  355. public function parseDigestAuthData($digest) {
  356. if (substr($digest, 0, 7) == 'Digest ') {
  357. $digest = substr($digest, 7);
  358. }
  359. $keys = array();
  360. $match = array();
  361. $req = array('nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1);
  362. preg_match_all('/(\w+)=([\'"]?)([a-zA-Z0-9@=.\/_-]+)\2/', $digest, $match, PREG_SET_ORDER);
  363. foreach ($match as $i) {
  364. $keys[$i[1]] = $i[3];
  365. unset($req[$i[1]]);
  366. }
  367. if (empty($req)) {
  368. return $keys;
  369. }
  370. return null;
  371. }
  372. /**
  373. * Generates a hash to be compared with an HTTP digest-authenticated response
  374. *
  375. * @param array $data HTTP digest response data, as parsed by SecurityComponent::parseDigestAuthData()
  376. * @return string Digest authentication hash
  377. * @access public
  378. * @see SecurityComponent::parseDigestAuthData()
  379. * @link http://book.cakephp.org/view/1306/generateDigestResponseHash-array-data
  380. */
  381. function generateDigestResponseHash($data) {
  382. return md5(
  383. md5($data['username'] . ':' . $this->loginOptions['realm'] . ':' . $this->loginUsers[$data['username']]) .
  384. ':' . $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' .
  385. md5(env('REQUEST_METHOD') . ':' . $data['uri'])
  386. );
  387. }
  388. /**
  389. * Black-hole an invalid request with a 404 error or custom callback. If SecurityComponent::$blackHoleCallback
  390. * is specified, it will use this callback by executing the method indicated in $error
  391. *
  392. * @param object $controller Instantiating controller
  393. * @param string $error Error method
  394. * @return mixed If specified, controller blackHoleCallback's response, or no return otherwise
  395. * @access public
  396. * @see SecurityComponent::$blackHoleCallback
  397. * @link http://book.cakephp.org/view/1307/blackHole-object-controller-string-error
  398. */
  399. function blackHole($controller, $error = '') {
  400. if ($this->blackHoleCallback == null) {
  401. $code = 404;
  402. if ($error == 'login') {
  403. $code = 401;
  404. $controller->header($this->loginRequest());
  405. }
  406. return $controller->redirect(null, $code, true);
  407. } else {
  408. return $this->_callback($controller, $this->blackHoleCallback, array($error));
  409. }
  410. }
  411. /**
  412. * Sets the actions that require a $method HTTP request, or empty for all actions
  413. *
  414. * @param string $method The HTTP method to assign controller actions to
  415. * @param array $actions Controller actions to set the required HTTP method to.
  416. * @return void
  417. */
  418. protected function _requireMethod($method, $actions = array()) {
  419. if (isset($actions[0]) && is_array($actions[0])) {
  420. $actions = $actions[0];
  421. }
  422. $this->{'require' . $method} = (empty($actions)) ? array('*'): $actions;
  423. }
  424. /**
  425. * Check if HTTP methods are required
  426. *
  427. * @param object $controller Instantiating controller
  428. * @return bool true if $method is required
  429. */
  430. protected function _methodsRequired($controller) {
  431. foreach (array('Post', 'Get', 'Put', 'Delete') as $method) {
  432. $property = 'require' . $method;
  433. if (is_array($this->$property) && !empty($this->$property)) {
  434. $require = array_map('strtolower', $this->$property);
  435. if (in_array($this->_action, $require) || $this->$property == array('*')) {
  436. if (!$this->request->is(strtolower($method))) {
  437. if (!$this->blackHole($controller, strtolower($method))) {
  438. return null;
  439. }
  440. }
  441. }
  442. }
  443. }
  444. return true;
  445. }
  446. /**
  447. * Check if access requires secure connection
  448. *
  449. * @param object $controller Instantiating controller
  450. * @return bool true if secure connection required
  451. */
  452. protected function _secureRequired($controller) {
  453. if (is_array($this->requireSecure) && !empty($this->requireSecure)) {
  454. $requireSecure = array_map('strtolower', $this->requireSecure);
  455. if (in_array($this->_action, $requireSecure) || $this->requireSecure == array('*')) {
  456. if (!$this->request->is('ssl')) {
  457. if (!$this->blackHole($controller, 'secure')) {
  458. return null;
  459. }
  460. }
  461. }
  462. }
  463. return true;
  464. }
  465. /**
  466. * Check if authentication is required
  467. *
  468. * @param object $controller Instantiating controller
  469. * @return bool true if authentication required
  470. */
  471. protected function _authRequired($controller) {
  472. if (is_array($this->requireAuth) && !empty($this->requireAuth) && !empty($this->request->data)) {
  473. $requireAuth = array_map('strtolower', $this->requireAuth);
  474. if (in_array($this->request->params['action'], $requireAuth) || $this->requireAuth == array('*')) {
  475. if (!isset($controller->request->data['_Token'] )) {
  476. if (!$this->blackHole($controller, 'auth')) {
  477. return null;
  478. }
  479. }
  480. if ($this->Session->check('_Token')) {
  481. $tData = $this->Session->read('_Token');
  482. if (!empty($tData['allowedControllers']) && !in_array($this->request->params['controller'], $tData['allowedControllers']) || !empty($tData['allowedActions']) && !in_array($this->request->params['action'], $tData['allowedActions'])) {
  483. if (!$this->blackHole($controller, 'auth')) {
  484. return null;
  485. }
  486. }
  487. } else {
  488. if (!$this->blackHole($controller, 'auth')) {
  489. return null;
  490. }
  491. }
  492. }
  493. }
  494. return true;
  495. }
  496. /**
  497. * Check if login is required
  498. *
  499. * @param object $controller Instantiating controller
  500. * @return bool true if login is required
  501. */
  502. protected function _loginRequired($controller) {
  503. if (is_array($this->requireLogin) && !empty($this->requireLogin)) {
  504. $requireLogin = $this->requireLogin;
  505. if (in_array($this->_action, $this->requireLogin) || $this->requireLogin == array('*')) {
  506. $login = $this->loginCredentials($this->loginOptions['type']);
  507. if ($login == null) {
  508. $controller->header($this->loginRequest());
  509. if (!empty($this->loginOptions['prompt'])) {
  510. $this->_callback($controller, $this->loginOptions['prompt']);
  511. } else {
  512. $this->blackHole($controller, 'login');
  513. }
  514. } else {
  515. if (isset($this->loginOptions['login'])) {
  516. $this->_callback($controller, $this->loginOptions['login'], array($login));
  517. } else {
  518. if (strtolower($this->loginOptions['type']) == 'digest') {
  519. if ($login && isset($this->loginUsers[$login['username']])) {
  520. if ($login['response'] == $this->generateDigestResponseHash($login)) {
  521. return true;
  522. }
  523. }
  524. $this->blackHole($controller, 'login');
  525. } else {
  526. if (
  527. !(in_array($login['username'], array_keys($this->loginUsers)) &&
  528. $this->loginUsers[$login['username']] == $login['password'])
  529. ) {
  530. $this->blackHole($controller, 'login');
  531. }
  532. }
  533. }
  534. }
  535. }
  536. }
  537. return true;
  538. }
  539. /**
  540. * Validate submitted form
  541. *
  542. * @param object $controller Instantiating controller
  543. * @return bool true if submitted form is valid
  544. */
  545. protected function _validatePost($controller) {
  546. if (empty($controller->request->data)) {
  547. return true;
  548. }
  549. $data = $controller->request->data;
  550. if (!isset($data['_Token']) || !isset($data['_Token']['fields'])) {
  551. return false;
  552. }
  553. $locked = null;
  554. $check = $controller->request->data;
  555. $token = urldecode($check['_Token']['fields']);
  556. if (strpos($token, ':')) {
  557. list($token, $locked) = explode(':', $token, 2);
  558. }
  559. unset($check['_Token']);
  560. $locked = explode('|', $locked);
  561. $lockedFields = array();
  562. $fields = Set::flatten($check);
  563. $fieldList = array_keys($fields);
  564. $multi = array();
  565. foreach ($fieldList as $i => $key) {
  566. if (preg_match('/\.\d+$/', $key)) {
  567. $multi[$i] = preg_replace('/\.\d+$/', '', $key);
  568. unset($fieldList[$i]);
  569. }
  570. }
  571. if (!empty($multi)) {
  572. $fieldList += array_unique($multi);
  573. }
  574. foreach ($fieldList as $i => $key) {
  575. $isDisabled = false;
  576. $isLocked = (is_array($locked) && in_array($key, $locked));
  577. if (!empty($this->disabledFields)) {
  578. foreach ((array)$this->disabledFields as $disabled) {
  579. $disabled = explode('.', $disabled);
  580. $field = array_values(array_intersect(explode('.', $key), $disabled));
  581. $isDisabled = ($field === $disabled);
  582. if ($isDisabled) {
  583. break;
  584. }
  585. }
  586. }
  587. if ($isDisabled || $isLocked) {
  588. unset($fieldList[$i]);
  589. if ($isLocked) {
  590. $lockedFields[$key] = $fields[$key];
  591. }
  592. }
  593. }
  594. sort($fieldList, SORT_STRING);
  595. ksort($lockedFields, SORT_STRING);
  596. $fieldList += $lockedFields;
  597. $check = Security::hash(serialize($fieldList) . Configure::read('Security.salt'));
  598. return ($token === $check);
  599. }
  600. /**
  601. * Add authentication key for new form posts
  602. *
  603. * @param object $controller Instantiating controller
  604. * @return bool Success
  605. */
  606. protected function _generateToken($controller) {
  607. if (isset($controller->request->params['requested']) && $controller->request->params['requested'] === 1) {
  608. if ($this->Session->check('_Token')) {
  609. $tokenData = $this->Session->read('_Token');
  610. $controller->request->params['_Token'] = $tokenData;
  611. }
  612. return false;
  613. }
  614. $authKey = Security::generateAuthKey();
  615. $token = array(
  616. 'key' => $authKey,
  617. 'allowedControllers' => $this->allowedControllers,
  618. 'allowedActions' => $this->allowedActions,
  619. 'disabledFields' => $this->disabledFields,
  620. 'csrfTokens' => array()
  621. );
  622. $tokenData = array();
  623. if ($this->Session->check('_Token')) {
  624. $tokenData = $this->Session->read('_Token');
  625. if (!empty($tokenData['csrfTokens'])) {
  626. $token['csrfTokens'] = $this->_expireTokens($tokenData['csrfTokens']);
  627. }
  628. }
  629. if ($this->csrfCheck && ($this->csrfUseOnce || empty($tokenData['csrfTokens'])) ) {
  630. $token['csrfTokens'][$authKey] = strtotime($this->csrfExpires);
  631. }
  632. $controller->request->params['_Token'] = $token;
  633. $this->Session->write('_Token', $token);
  634. return true;
  635. }
  636. /**
  637. * Validate that the controller has a CSRF token in the POST data
  638. * and that the token is legit/not expired. If the token is valid
  639. * it will be removed from the list of valid tokens.
  640. *
  641. * @param Controller $controller A controller to check
  642. * @return boolean Valid csrf token.
  643. */
  644. protected function _validateCsrf($controller) {
  645. $token = $this->Session->read('_Token');
  646. $requestToken = $controller->request->data('_Token.key');
  647. if (isset($token['csrfTokens'][$requestToken]) && $token['csrfTokens'][$requestToken] >= time()) {
  648. if ($this->csrfUseOnce) {
  649. $this->Session->delete('_Token.csrfTokens.' . $requestToken);
  650. }
  651. return true;
  652. }
  653. return false;
  654. }
  655. /**
  656. * Expire CSRF nonces and remove them from the valid tokens.
  657. * Uses a simple timeout to expire the tokens.
  658. *
  659. * @param array $tokens An array of nonce => expires.
  660. * @return An array of nonce => expires.
  661. */
  662. protected function _expireTokens($tokens) {
  663. $now = time();
  664. foreach ($tokens as $nonce => $expires) {
  665. if ($expires < $now) {
  666. unset($tokens[$nonce]);
  667. }
  668. }
  669. return $tokens;
  670. }
  671. /**
  672. * Sets the default login options for an HTTP-authenticated request
  673. *
  674. * @param array $options Default login options
  675. * @return void
  676. */
  677. protected function _setLoginDefaults(&$options) {
  678. $options = array_merge(array(
  679. 'type' => 'basic',
  680. 'realm' => env('SERVER_NAME'),
  681. 'qop' => 'auth',
  682. 'nonce' => String::uuid()
  683. ), array_filter($options));
  684. $options = array_merge(array('opaque' => md5($options['realm'])), $options);
  685. }
  686. /**
  687. * Calls a controller callback method
  688. *
  689. * @param object $controller Controller to run callback on
  690. * @param string $method Method to execute
  691. * @param array $params Parameters to send to method
  692. * @return mixed Controller callback method's response
  693. */
  694. protected function _callback($controller, $method, $params = array()) {
  695. if (is_callable(array($controller, $method))) {
  696. return call_user_func_array(array(&$controller, $method), empty($params) ? null : $params);
  697. } else {
  698. return null;
  699. }
  700. }
  701. }