|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2009 Sebastian Kurfürst <sebastian@typo3.org> 00006 * All rights reserved 00007 * 00008 * This class is a backport of the corresponding class of FLOW3. 00009 * All credits go to the v5 team. 00010 * 00011 * This script is part of the TYPO3 project. The TYPO3 project is 00012 * free software; you can redistribute it and/or modify 00013 * it under the terms of the GNU General Public License as published by 00014 * the Free Software Foundation; either version 2 of the License, or 00015 * (at your option) any later version. 00016 * 00017 * The GNU General Public License can be found at 00018 * http://www.gnu.org/copyleft/gpl.html. 00019 * 00020 * This script is distributed in the hope that it will be useful, 00021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00023 * GNU General Public License for more details. 00024 * 00025 * This copyright notice MUST APPEAR in all copies of the script! 00026 ***************************************************************/ 00027 00028 /** 00029 * This is a Service which can generate a request hash and check whether the currently given arguments 00030 * fit to the request hash. 00031 * 00032 * It is used when forms are generated and submitted: 00033 * After a form has been generated, the method "generateRequestHash" is called with the names of all form fields. 00034 * It cleans up the array of form fields and creates another representation of it, which is then serialized and hashed. 00035 * 00036 * Both serialized form field list and the added hash form the request hash, which will be sent over the wire (as an argument __hmac). 00037 * 00038 * On the validation side, the validation happens in two steps: 00039 * 1) Check if the request hash is consistent (the hash value fits to the serialized string) 00040 * 2) Check that _all_ GET/POST parameters submitted occur inside the form field list of the request hash. 00041 * 00042 * Note: It is crucially important that a private key is computed into the hash value! This is done inside the HashService. 00043 * 00044 * @version $Id: RequestHashService.php 1729 2009-11-25 21:37:20Z stucki $ 00045 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later 00046 */ 00047 class Tx_Extbase_Security_Channel_RequestHashService implements t3lib_singleton { 00048 00049 /** 00050 * @var Tx_Extbase_Security_Cryptography_HashService 00051 */ 00052 protected $hashService; 00053 00054 /** 00055 * @param Tx_Extbase_Security_Cryptography_HashService $hashService 00056 * @return void 00057 */ 00058 public function injectHashService(Tx_Extbase_Security_Cryptography_HashService $hashService) { 00059 $this->hashService = $hashService; 00060 } 00061 00062 /** 00063 * Generate a request hash for a list of form fields 00064 * 00065 * @param array $formFieldNames Array of form fields 00066 * @return string request hash 00067 * @author Sebastian Kurfürst <sebastian@typo3.org> 00068 * @todo might need to become public API lateron, as we need to call it from Fluid 00069 */ 00070 public function generateRequestHash($formFieldNames, $fieldNamePrefix = '') { 00071 $formFieldArray = array(); 00072 foreach ($formFieldNames as $formField) { 00073 $formFieldParts = explode('[', $formField); 00074 $currentPosition =& $formFieldArray; 00075 for ($i=0; $i < count($formFieldParts); $i++) { 00076 $formFieldPart = $formFieldParts[$i]; 00077 if (substr($formFieldPart, -1) == ']') $formFieldPart = substr($formFieldPart, 0, -1); // Strip off closing ] if needed 00078 00079 if (!is_array($currentPosition)) { 00080 throw new Tx_Extbase_Security_Exception_InvalidArgumentForRequestHashGeneration('The form field name "' . $formField . '" collides with a previous form field name which declared the field as string. (String overridden by Array)', 1255072196); 00081 } 00082 00083 if ($i == count($formFieldParts) - 1) { 00084 if (isset($currentPosition[$formFieldPart]) && is_array($currentPosition[$formFieldPart])) { 00085 throw new Tx_Extbase_Security_Exception_InvalidArgumentForRequestHashGeneration('The form field name "' . $formField . '" collides with a previous form field name which declared the field as array. (Array overridden by String)', 1255072587); 00086 } 00087 // Last iteration - add a string 00088 if ($formFieldPart === '') { 00089 $currentPosition[] = 1; 00090 } else { 00091 $currentPosition[$formFieldPart] = 1; 00092 } 00093 } else { 00094 if ($formFieldPart === '') { 00095 throw new Tx_Extbase_Security_Exception_InvalidArgumentForRequestHashGeneration('The form field name "' . $formField . '" is invalid. Reason: "[]" used not as last argument.', 1255072832); 00096 } 00097 if (!isset($currentPosition[$formFieldPart])) { 00098 $currentPosition[$formFieldPart] = array(); 00099 } 00100 $currentPosition =& $currentPosition[$formFieldPart]; 00101 } 00102 } 00103 } 00104 if ($fieldNamePrefix !== '') { 00105 00106 $formFieldArray = (isset($formFieldArray[$fieldNamePrefix]) ? $formFieldArray[$fieldNamePrefix] : array() ); 00107 } 00108 return $this->serializeAndHashFormFieldArray($formFieldArray); 00109 } 00110 00111 /** 00112 * Serialize and hash the form field array 00113 * 00114 * @param array $formFieldArray form field array to be serialized and hashed 00115 * @return string Hash 00116 * @author Sebastian Kurfürst <sebastian@typo3.org> 00117 */ 00118 protected function serializeAndHashFormFieldArray($formFieldArray) { 00119 $serializedFormFieldArray = serialize($formFieldArray); 00120 return $serializedFormFieldArray . $this->hashService->generateHash($serializedFormFieldArray); 00121 } 00122 00123 /** 00124 * Verify the request. Checks if there is an __hmac argument, and if yes, tries to validate and verify it. 00125 * 00126 * In the end, $request->setHmacVerified is set depending on the value. 00127 * @param \F3\FLOW3\MVC\Web\Request $request The request to verify 00128 * @return void 00129 * @author Sebastian Kurfürst <sebastian@typo3.org> 00130 */ 00131 public function verifyRequest(Tx_Extbase_MVC_Web_Request $request) { 00132 if (!$request->hasArgument('__hmac')) { 00133 $request->setHmacVerified(FALSE); 00134 return; 00135 } 00136 $hmac = $request->getArgument('__hmac'); 00137 if (strlen($hmac) < 40) { 00138 throw new Tx_Extbase_Security_Exception_SyntacticallyWrongRequestHash('Request hash too short. This is a probably manipulation attempt!', 1255089361); 00139 } 00140 $serializedFieldNames = substr($hmac, 0, -40); // TODO: Constant for hash length needs to be introduced 00141 $hash = substr($hmac, -40); 00142 if ($this->hashService->validateHash($serializedFieldNames, $hash)) { 00143 $requestArguments = $request->getArguments(); 00144 // Unset framework arguments 00145 unset($requestArguments['__referrer']); 00146 unset($requestArguments['__hmac']); 00147 if ($this->checkFieldNameInclusion($requestArguments, unserialize($serializedFieldNames))) { 00148 $request->setHmacVerified(TRUE); 00149 } else { 00150 $request->setHmacVerified(FALSE); 00151 } 00152 } else { 00153 $request->setHmacVerified(FALSE); 00154 } 00155 00156 } 00157 00158 /** 00159 * Check if every element in $requestArguments is in $allowedFields as well. 00160 * 00161 * @param array $requestArguments 00162 * @param array $allowedFiels 00163 * @return boolean TRUE if ALL fields inside requestArguments are in $allowedFields, FALSE otherwise. 00164 */ 00165 protected function checkFieldNameInclusion(array $requestArguments, array $allowedFields) { 00166 foreach ($requestArguments as $argumentName => $argumentValue) { 00167 if (!isset($allowedFields[$argumentName])) { 00168 return FALSE; 00169 } 00170 if (is_array($requestArguments[$argumentName]) && is_array($allowedFields[$argumentName])) { 00171 if (!$this->checkFieldNameInclusion($requestArguments[$argumentName], $allowedFields[$argumentName])) { 00172 return FALSE; 00173 } 00174 } elseif (!is_array($requestArguments[$argumentName]) && !is_array($allowedFields[$argumentName])) { 00175 // do nothing, as this is allowed 00176 } elseif (!is_array($requestArguments[$argumentName]) && $requestArguments[$argumentName] === '' && is_array($allowedFields[$argumentName])) { 00177 // do nothing, as this is allowed. 00178 // This case is needed for making an array of checkboxes work, in case they are fully unchecked. 00179 // Example: if the following checkbox names are defined: 00180 // foo[a] 00181 // foo[b] 00182 // then, Fluid automatically renders a hidden field "foo" with the value '' (empty string) in front of it, 00183 // to determine the case if the user un-checks all checkboxes. 00184 // in this case, the property mapping already does the right thing, but without this condition here, 00185 // the request hash checking would fail because of the strong type checks. 00186 } else { 00187 // different types - error 00188 return FALSE; 00189 } 00190 } 00191 return TRUE; 00192 } 00193 } 00194 00195 ?>
1.8.0