/var/www/vhosts/ihelp.ro/_OLD/vendor/cakephp/cakephp/src/Controller/Component
NameSizeModeActions
AuthComponent.php333790644editdlrm
FlashComponent.php57440644editdlrm
FormProtectionComponent.php56110644editdlrm
PaginatorComponent.php107440644editdlrm
RequestHandlerComponent.php183850644editdlrm
SecurityComponent.php205940644editdlrm
Edit: /var/www/vhosts/ihelp.ro/_OLD/vendor/cakephp/cakephp/src/Controller/Component/SecurityComponent.php (20594B)
null, 'requireSecure' => [], 'unlockedFields' => [], 'unlockedActions' => [], 'validatePost' => true, ]; /** * Holds the current action of the controller * * @var string */ protected $_action; /** * Component startup. All security checking happens here. * * @param \Cake\Event\EventInterface $event An Event instance * @return \Cake\Http\Response|null */ public function startup(EventInterface $event): ?Response { /** @var \Cake\Controller\Controller $controller */ $controller = $event->getSubject(); $request = $controller->getRequest(); $this->_action = $request->getParam('action'); $hasData = ($request->getData() || $request->is(['put', 'post', 'delete', 'patch'])); try { $this->_secureRequired($controller); if ($this->_action === $this->_config['blackHoleCallback']) { throw new AuthSecurityException(sprintf( 'Action %s is defined as the blackhole callback.', $this->_action )); } if ( !in_array($this->_action, (array)$this->_config['unlockedActions'], true) && $hasData && $this->_config['validatePost'] ) { $this->_validatePost($controller); } } catch (SecurityException $se) { return $this->blackHole($controller, $se->getType(), $se); } $request = $this->generateToken($request); if ($hasData && is_array($controller->getRequest()->getData())) { $request = $request->withoutData('_Token'); } $controller->setRequest($request); return null; } /** * Events supported by this component. * * @return array */ public function implementedEvents(): array { return [ 'Controller.startup' => 'startup', ]; } /** * Sets the actions that require a request that is SSL-secured, or empty for all actions * * @param string|string[]|null $actions Actions list * @return void */ public function requireSecure($actions = null): void { $actions = (array)$actions; $this->setConfig('requireSecure', empty($actions) ? ['*'] : $actions); } /** * Black-hole an invalid request with a 400 error or custom callback. If SecurityComponent::$blackHoleCallback * is specified, it will use this callback by executing the method indicated in $error * * @param \Cake\Controller\Controller $controller Instantiating controller * @param string $error Error method * @param \Cake\Controller\Exception\SecurityException|null $exception Additional debug info describing the cause * @return mixed If specified, controller blackHoleCallback's response, or no return otherwise * @see \Cake\Controller\Component\SecurityComponent::$blackHoleCallback * @link https://book.cakephp.org/4/en/controllers/components/security.html#handling-blackhole-callbacks * @throws \Cake\Http\Exception\BadRequestException */ public function blackHole(Controller $controller, string $error = '', ?SecurityException $exception = null) { if (!$this->_config['blackHoleCallback']) { $this->_throwException($exception); } return $this->_callback($controller, $this->_config['blackHoleCallback'], [$error, $exception]); } /** * Check debug status and throw an Exception based on the existing one * * @param \Cake\Controller\Exception\SecurityException|null $exception Additional debug info describing the cause * @throws \Cake\Http\Exception\BadRequestException * @return void */ protected function _throwException(?SecurityException $exception = null): void { if ($exception !== null) { if (!Configure::read('debug')) { $exception->setReason($exception->getMessage()); $exception->setMessage(static::DEFAULT_EXCEPTION_MESSAGE); } throw $exception; } throw new BadRequestException(static::DEFAULT_EXCEPTION_MESSAGE); } /** * Check if access requires secure connection * * @param \Cake\Controller\Controller $controller Instantiating controller * @return void * @throws \Cake\Controller\Exception\SecurityException */ protected function _secureRequired(Controller $controller): void { if ( empty($this->_config['requireSecure']) || !is_array($this->_config['requireSecure']) ) { return; } $requireSecure = $this->_config['requireSecure']; if ( ($requireSecure[0] === '*' || in_array($this->_action, $requireSecure, true) ) && !$controller->getRequest()->is('ssl') ) { throw new SecurityException( 'Request is not SSL and the action is required to be secure' ); } } /** * Validate submitted form * * @param \Cake\Controller\Controller $controller Instantiating controller * @return void * @throws \Cake\Controller\Exception\AuthSecurityException */ protected function _validatePost(Controller $controller): void { $token = $this->_validToken($controller); $hashParts = $this->_hashParts($controller); $check = hash_hmac('sha1', implode('', $hashParts), Security::getSalt()); if (hash_equals($check, $token)) { return; } $msg = static::DEFAULT_EXCEPTION_MESSAGE; if (Configure::read('debug')) { $msg = $this->_debugPostTokenNotMatching($controller, $hashParts); } throw new AuthSecurityException($msg); } /** * Check if token is valid * * @param \Cake\Controller\Controller $controller Instantiating controller * @throws \Cake\Controller\Exception\SecurityException * @return string fields token */ protected function _validToken(Controller $controller): string { $check = $controller->getRequest()->getData(); $message = '\'%s\' was not found in request data.'; if (!isset($check['_Token'])) { throw new AuthSecurityException(sprintf($message, '_Token')); } if (!isset($check['_Token']['fields'])) { throw new AuthSecurityException(sprintf($message, '_Token.fields')); } if (!is_string($check['_Token']['fields'])) { throw new AuthSecurityException("'_Token.fields' is invalid."); } if (!isset($check['_Token']['unlocked'])) { throw new AuthSecurityException(sprintf($message, '_Token.unlocked')); } if (Configure::read('debug') && !isset($check['_Token']['debug'])) { throw new SecurityException(sprintf($message, '_Token.debug')); } if (!Configure::read('debug') && isset($check['_Token']['debug'])) { throw new SecurityException('Unexpected \'_Token.debug\' found in request data'); } $token = urldecode($check['_Token']['fields']); if (strpos($token, ':')) { [$token, ] = explode(':', $token, 2); } return $token; } /** * Return hash parts for the Token generation * * @param \Cake\Controller\Controller $controller Instantiating controller * @return string[] */ protected function _hashParts(Controller $controller): array { $request = $controller->getRequest(); // Start the session to ensure we get the correct session id. $session = $request->getSession(); $session->start(); $data = (array)$request->getData(); $fieldList = $this->_fieldsList($data); $unlocked = $this->_sortedUnlocked($data); return [ Router::url($request->getRequestTarget()), serialize($fieldList), $unlocked, $session->id(), ]; } /** * Return the fields list for the hash calculation * * @param array $check Data array * @return array */ protected function _fieldsList(array $check): array { $locked = ''; $token = urldecode($check['_Token']['fields']); $unlocked = $this->_unlocked($check); if (strpos($token, ':')) { [, $locked] = explode(':', $token, 2); } unset($check['_Token']); $locked = $locked ? explode('|', $locked) : []; $unlocked = $unlocked ? explode('|', $unlocked) : []; $fields = Hash::flatten($check); $fieldList = array_keys($fields); $multi = $lockedFields = []; $isUnlocked = false; foreach ($fieldList as $i => $key) { if (is_string($key) && preg_match('/(\.\d+){1,10}$/', $key)) { $multi[$i] = preg_replace('/(\.\d+){1,10}$/', '', $key); unset($fieldList[$i]); } else { $fieldList[$i] = (string)$key; } } if (!empty($multi)) { $fieldList += array_unique($multi); } $unlockedFields = array_unique( array_merge( (array)$this->_config['unlockedFields'], $unlocked ) ); foreach ($fieldList as $i => $key) { $isLocked = in_array($key, $locked, true); if (!empty($unlockedFields)) { foreach ($unlockedFields as $off) { $off = explode('.', $off); $field = array_values(array_intersect(explode('.', $key), $off)); $isUnlocked = ($field === $off); if ($isUnlocked) { break; } } } if ($isUnlocked || $isLocked) { unset($fieldList[$i]); if ($isLocked) { $lockedFields[$key] = $fields[$key]; } } } sort($fieldList, SORT_STRING); ksort($lockedFields, SORT_STRING); $fieldList += $lockedFields; return $fieldList; } /** * Get the unlocked string * * @param array $data Data array * @return string */ protected function _unlocked(array $data): string { return urldecode($data['_Token']['unlocked']); } /** * Get the sorted unlocked string * * @param array $data Data array * @return string */ protected function _sortedUnlocked(array $data): string { $unlocked = $this->_unlocked($data); $unlocked = explode('|', $unlocked); sort($unlocked, SORT_STRING); return implode('|', $unlocked); } /** * Create a message for humans to understand why Security token is not matching * * @param \Cake\Controller\Controller $controller Instantiating controller * @param array $hashParts Elements used to generate the Token hash * @return string Message explaining why the tokens are not matching */ protected function _debugPostTokenNotMatching(Controller $controller, array $hashParts): string { $messages = []; $expectedParts = json_decode(urldecode($controller->getRequest()->getData('_Token.debug')), true); if (!is_array($expectedParts) || count($expectedParts) !== 3) { return 'Invalid security debug token.'; } $expectedUrl = Hash::get($expectedParts, 0); $url = Hash::get($hashParts, 0); if ($expectedUrl !== $url) { $messages[] = sprintf('URL mismatch in POST data (expected \'%s\' but found \'%s\')', $expectedUrl, $url); } $expectedFields = Hash::get($expectedParts, 1); $dataFields = Hash::get($hashParts, 1); if ($dataFields) { $dataFields = unserialize($dataFields); } $fieldsMessages = $this->_debugCheckFields( $dataFields, $expectedFields, 'Unexpected field \'%s\' in POST data', 'Tampered field \'%s\' in POST data (expected value \'%s\' but found \'%s\')', 'Missing field \'%s\' in POST data' ); $expectedUnlockedFields = Hash::get($expectedParts, 2); $dataUnlockedFields = Hash::get($hashParts, 2) ?: null; if ($dataUnlockedFields) { $dataUnlockedFields = explode('|', $dataUnlockedFields); } $unlockFieldsMessages = $this->_debugCheckFields( (array)$dataUnlockedFields, $expectedUnlockedFields, 'Unexpected unlocked field \'%s\' in POST data', '', 'Missing unlocked field: \'%s\'' ); $messages = array_merge($messages, $fieldsMessages, $unlockFieldsMessages); return implode(', ', $messages); } /** * Iterates data array to check against expected * * @param array $dataFields Fields array, containing the POST data fields * @param array $expectedFields Fields array, containing the expected fields we should have in POST * @param string $intKeyMessage Message string if unexpected found in data fields indexed by int (not protected) * @param string $stringKeyMessage Message string if tampered found in * data fields indexed by string (protected). * @param string $missingMessage Message string if missing field * @return string[] Messages */ protected function _debugCheckFields( array $dataFields, array $expectedFields = [], string $intKeyMessage = '', string $stringKeyMessage = '', string $missingMessage = '' ): array { $messages = $this->_matchExistingFields($dataFields, $expectedFields, $intKeyMessage, $stringKeyMessage); $expectedFieldsMessage = $this->_debugExpectedFields($expectedFields, $missingMessage); if ($expectedFieldsMessage !== null) { $messages[] = $expectedFieldsMessage; } return $messages; } /** * Manually add form tampering prevention token information into the provided * request object. * * @param \Cake\Http\ServerRequest $request The request object to add into. * @return \Cake\Http\ServerRequest The modified request. */ public function generateToken(ServerRequest $request): ServerRequest { $token = [ 'unlockedFields' => $this->_config['unlockedFields'], ]; return $request->withAttribute('formTokenData', [ 'unlockedFields' => $token['unlockedFields'], ]); } /** * Calls a controller callback method * * @param \Cake\Controller\Controller $controller Instantiating controller * @param string $method Method to execute * @param array $params Parameters to send to method * @return mixed Controller callback method's response * @throws \Cake\Http\Exception\BadRequestException When a the blackholeCallback is not callable. */ protected function _callback(Controller $controller, string $method, array $params = []) { $callable = [$controller, $method]; if (!is_callable($callable)) { throw new BadRequestException('The request has been black-holed'); } return $callable(...$params); } /** * Generate array of messages for the existing fields in POST data, matching dataFields in $expectedFields * will be unset * * @param array $dataFields Fields array, containing the POST data fields * @param array $expectedFields Fields array, containing the expected fields we should have in POST * @param string $intKeyMessage Message string if unexpected found in data fields indexed by int (not protected) * @param string $stringKeyMessage Message string if tampered found in * data fields indexed by string (protected) * @return string[] Error messages */ protected function _matchExistingFields( array $dataFields, array &$expectedFields, string $intKeyMessage, string $stringKeyMessage ): array { $messages = []; foreach ($dataFields as $key => $value) { if (is_int($key)) { $foundKey = array_search($value, (array)$expectedFields, true); if ($foundKey === false) { $messages[] = sprintf($intKeyMessage, $value); } else { unset($expectedFields[$foundKey]); } } else { if (isset($expectedFields[$key]) && $value !== $expectedFields[$key]) { $messages[] = sprintf($stringKeyMessage, $key, $expectedFields[$key], $value); } unset($expectedFields[$key]); } } return $messages; } /** * Generate debug message for the expected fields * * @param array $expectedFields Expected fields * @param string $missingMessage Message template * @return string|null Error message about expected fields */ protected function _debugExpectedFields(array $expectedFields = [], string $missingMessage = ''): ?string { if (count($expectedFields) === 0) { return null; } $expectedFieldNames = []; foreach ((array)$expectedFields as $key => $expectedField) { if (is_int($key)) { $expectedFieldNames[] = $expectedField; } else { $expectedFieldNames[] = $key; } } return sprintf($missingMessage, implode(', ', $expectedFieldNames)); } }