From c1363499191eef0a1829295ba1afb9e5f12807c7 Mon Sep 17 00:00:00 2001 From: mark_story Date: Tue, 14 Jun 2011 22:01:59 -0400 Subject: [PATCH] Updating SecurityComponent to use unlocked instead of disabled. --- .../Component/SecurityComponent.php | 42 ++++--- .../Component/SecurityComponentTest.php | 106 +++++++++--------- 2 files changed, 80 insertions(+), 68 deletions(-) diff --git a/lib/Cake/Controller/Component/SecurityComponent.php b/lib/Cake/Controller/Component/SecurityComponent.php index 4a74092ff27..2e582049ae2 100644 --- a/lib/Cake/Controller/Component/SecurityComponent.php +++ b/lib/Cake/Controller/Component/SecurityComponent.php @@ -112,14 +112,24 @@ class SecurityComponent extends Component { public $allowedActions = array(); /** - * Form fields to exclude from POST validation. Fields can be disabled - * either in the Component, or with FormHelper::disableField() + * Deprecated property, superseded by unlockedFields. * * @var array - * @access public + * @deprecated + * @see SecurityComponent::$unlockedFields */ public $disabledFields = array(); +/** + * Form fields to exclude from POST validation. Fields can be unlocked + * either in the Component, or with FormHelper::unlockField(). + * Fields that have been unlocked are not required to be part of the POST + * and hidden unlocked fields do not have their values checked. + * + * @var array + */ + public $unlockedFields = array(); + /** * Whether to validate POST data. Set to false to disable for data coming from 3rd party * services, etc. @@ -403,14 +413,14 @@ protected function _validatePost($controller) { } $data = $controller->request->data; - if (!isset($data['_Token']) || !isset($data['_Token']['fields']) || !isset($data['_Token']['disabled'])) { + if (!isset($data['_Token']) || !isset($data['_Token']['fields']) || !isset($data['_Token']['unlocked'])) { return false; } $locked = ''; $check = $controller->request->data; $token = urldecode($check['_Token']['fields']); - $disabled = urldecode($check['_Token']['disabled']); + $unlocked = urldecode($check['_Token']['unlocked']); if (strpos($token, ':')) { list($token, $locked) = explode(':', $token, 2); @@ -418,7 +428,7 @@ protected function _validatePost($controller) { unset($check['_Token']); $locked = explode('|', $locked); - $disabled = explode('|', $disabled); + $unlocked = explode('|', $unlocked); $lockedFields = array(); $fields = Set::flatten($check); @@ -435,37 +445,39 @@ protected function _validatePost($controller) { $fieldList += array_unique($multi); } - $disabledFields = array_unique(array_merge((array)$this->disabledFields, $disabled)); + $unlockedFields = array_unique( + array_merge((array)$this->disabledFields, (array)$this->unlockedFields, $unlocked) + ); foreach ($fieldList as $i => $key) { $isDisabled = false; $isLocked = (is_array($locked) && in_array($key, $locked)); - if (!empty($disabledFields)) { - foreach ($disabledFields as $off) { + if (!empty($unlockedFields)) { + foreach ($unlockedFields as $off) { $off = explode('.', $off); $field = array_values(array_intersect(explode('.', $key), $off)); - $isDisabled = ($field === $off); - if ($isDisabled) { + $isUnlocked = ($field === $off); + if ($isUnlocked) { break; } } } - if ($isDisabled || $isLocked) { + if ($isUnlocked || $isLocked) { unset($fieldList[$i]); if ($isLocked) { $lockedFields[$key] = $fields[$key]; } } } - sort($disabled, SORT_STRING); + sort($unlocked, SORT_STRING); sort($fieldList, SORT_STRING); ksort($lockedFields, SORT_STRING); $fieldList += $lockedFields; - $disabled = implode('|', $disabled); - $check = Security::hash(serialize($fieldList) . $disabled . Configure::read('Security.salt')); + $unlocked = implode('|', $unlocked); + $check = Security::hash(serialize($fieldList) . $unlocked . Configure::read('Security.salt')); return ($token === $check); } diff --git a/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php b/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php index f19c01159fd..65cdf7c8451 100644 --- a/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php +++ b/lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php @@ -444,11 +444,11 @@ public function testValidatePost() { $key = $this->Controller->request->params['_Token']['key']; $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3AModel.valid'; - $disabled = ''; + $unlocked = ''; $this->Controller->request->data = array( 'Model' => array('username' => 'nate', 'password' => 'foo', 'valid' => '0'), - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $this->assertTrue($this->Controller->Security->validatePost($this->Controller)); } @@ -462,11 +462,11 @@ public function testValidatePostFormHacking() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->params['_Token']['key']; $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3AModel.valid'; - $disabled = ''; + $unlocked = ''; $this->Controller->request->data = array( 'Model' => array('username' => 'nate', 'password' => 'foo', 'valid' => '0'), - '_Token' => compact('key', 'disabled') + '_Token' => compact('key', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); $this->assertFalse($result, 'validatePost passed when fields were missing. %s'); @@ -482,7 +482,7 @@ public function testValidatePostObjectDeserialize() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->request->params['_Token']['key']; $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877'; - $disabled = ''; + $unlocked = ''; // a corrupted serialized object, so we can see if it ever gets to deserialize $attack = 'O:3:"App":1:{s:5:"__map";a:1:{s:3:"foo";s:7:"Hacked!";s:1:"fail"}}'; @@ -490,7 +490,7 @@ public function testValidatePostObjectDeserialize() { $this->Controller->request->data = array( 'Model' => array('username' => 'mark', 'password' => 'foo', 'valid' => '0'), - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); $this->assertFalse($result, 'validatePost passed when key was missing. %s'); @@ -506,11 +506,11 @@ public function testValidatePostArray() { $key = $this->Controller->request->params['_Token']['key']; $fields = 'f7d573650a295b94e0938d32b323fde775e5f32b%3A'; - $disabled = ''; + $unlocked = ''; $this->Controller->request->data = array( 'Model' => array('multi_field' => array('1', '3')), - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $this->assertTrue($this->Controller->Security->validatePost($this->Controller)); } @@ -525,11 +525,11 @@ public function testValidatePostNoModel() { $key = $this->Controller->request->params['_Token']['key']; $fields = '540ac9c60d323c22bafe997b72c0790f39a8bdef%3A'; - $disabled = ''; + $unlocked = ''; $this->Controller->request->data = array( 'anything' => 'some_data', - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); @@ -546,11 +546,11 @@ public function testValidatePostSimple() { $key = $this->Controller->request->params['_Token']['key']; $fields = '69f493434187b867ea14b901fdf58b55d27c935d%3A'; - $disabled = ''; + $unlocked = ''; $this->Controller->request->data = $data = array( 'Model' => array('username' => '', 'password' => ''), - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); @@ -567,7 +567,7 @@ public function testValidatePostComplex() { $key = $this->Controller->request->params['_Token']['key']; $fields = 'c9118120e680a7201b543f562e5301006ccfcbe2%3AAddresses.0.id%7CAddresses.1.id'; - $disabled = ''; + $unlocked = ''; $this->Controller->request->data = array( 'Addresses' => array( @@ -580,7 +580,7 @@ public function testValidatePostComplex() { 'address' => '', 'city' => '', 'phone' => '', 'primary' => '' ) ), - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); @@ -596,25 +596,25 @@ public function testValidatePostMultipleSelect() { $key = $this->Controller->request->params['_Token']['key']; $fields = '422cde416475abc171568be690a98cad20e66079%3A'; - $disabled = ''; + $unlocked = ''; $this->Controller->request->data = array( 'Tag' => array('Tag' => array(1, 2)), - '_Token' => compact('key', 'fields', 'disabled'), + '_Token' => compact('key', 'fields', 'unlocked'), ); $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); $this->Controller->request->data = array( 'Tag' => array('Tag' => array(1, 2, 3)), - '_Token' => compact('key', 'fields', 'disabled'), + '_Token' => compact('key', 'fields', 'unlocked'), ); $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); $this->Controller->request->data = array( 'Tag' => array('Tag' => array(1, 2, 3, 4)), - '_Token' => compact('key', 'fields', 'disabled'), + '_Token' => compact('key', 'fields', 'unlocked'), ); $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); @@ -623,7 +623,7 @@ public function testValidatePostMultipleSelect() { $this->Controller->request->data = array( 'User.password' => 'bar', 'User.name' => 'foo', 'User.is_valid' => '1', 'Tag' => array('Tag' => array(1)), - '_Token' => compact('key', 'fields', 'disabled'), + '_Token' => compact('key', 'fields', 'unlocked'), ); $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); @@ -641,11 +641,11 @@ public function testValidatePostCheckbox() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->request->params['_Token']['key']; $fields = 'a5475372b40f6e3ccbf9f8af191f20e1642fd877%3AModel.valid'; - $disabled = ''; + $unlocked = ''; $this->Controller->request->data = array( 'Model' => array('username' => '', 'password' => '', 'valid' => '0'), - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); @@ -655,7 +655,7 @@ public function testValidatePostCheckbox() { $this->Controller->request->data = array( 'Model' => array('username' => '', 'password' => '', 'valid' => '0'), - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); @@ -668,7 +668,7 @@ public function testValidatePostCheckbox() { $this->Controller->request->data = $data = array( 'Model' => array('username' => '', 'password' => '', 'valid' => '0'), - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); @@ -684,14 +684,14 @@ public function testValidatePostHidden() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->request->params['_Token']['key']; $fields = '51ccd8cb0997c7b3d4523ecde5a109318405ef8c%3AModel.hidden%7CModel.other_hidden'; - $disabled = ''; + $unlocked = ''; $this->Controller->request->data = array( 'Model' => array( 'username' => '', 'password' => '', 'hidden' => '0', 'other_hidden' => 'some hidden value' ), - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); @@ -707,13 +707,13 @@ public function testValidatePostWithDisabledFields() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->request->params['_Token']['key']; $fields = 'ef1082968c449397bcd849f963636864383278b1%3AModel.hidden'; - $disabled = ''; + $unlocked = ''; $this->Controller->request->data = array( 'Model' => array( 'username' => '', 'password' => '', 'hidden' => '0' ), - '_Token' => compact('fields', 'key', 'disabled') + '_Token' => compact('fields', 'key', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); @@ -721,16 +721,16 @@ public function testValidatePostWithDisabledFields() { } /** - * test validating post data with posted disabled fields. + * test validating post data with posted unlocked fields. * * @return void */ public function testValidatePostDisabledFieldsInData() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->request->params['_Token']['key']; - $disabled = 'Model.username'; + $unlocked = 'Model.username'; $fields = array('Model.hidden', 'Model.password'); - $fields = urlencode(Security::hash(serialize($fields) . $disabled . Configure::read('Security.salt'))); + $fields = urlencode(Security::hash(serialize($fields) . $unlocked . Configure::read('Security.salt'))); $this->Controller->request->data = array( 'Model' => array( @@ -738,7 +738,7 @@ public function testValidatePostDisabledFieldsInData() { 'password' => 'sekret', 'hidden' => '0' ), - '_Token' => compact('fields', 'key', 'disabled') + '_Token' => compact('fields', 'key', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); @@ -746,7 +746,7 @@ public function testValidatePostDisabledFieldsInData() { } /** - * test that missing 'disabled' input causes failure + * test that missing 'unlocked' input causes failure * * @return void */ @@ -770,19 +770,19 @@ public function testValidatePostFailNoDisabled() { } /** - * Test that validatePost fails when disabled fields are changed. + * Test that validatePost fails when unlocked fields are changed. * * @return */ public function testValidatePostFailDisabledFieldTampering() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->request->params['_Token']['key']; - $disabled = 'Model.username'; + $unlocked = 'Model.username'; $fields = array('Model.hidden', 'Model.password'); - $fields = urlencode(Security::hash(serialize($fields) . $disabled . Configure::read('Security.salt'))); + $fields = urlencode(Security::hash(serialize($fields) . $unlocked . Configure::read('Security.salt'))); // Tamper the values. - $disabled = 'Model.username|Model.password'; + $unlocked = 'Model.username|Model.password'; $this->Controller->request->data = array( 'Model' => array( @@ -790,7 +790,7 @@ public function testValidatePostFailDisabledFieldTampering() { 'password' => 'sekret', 'hidden' => '0' ), - '_Token' => compact('fields', 'key', 'disabled') + '_Token' => compact('fields', 'key', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); @@ -806,13 +806,13 @@ public function testValidateHiddenMultipleModel() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->request->params['_Token']['key']; $fields = 'a2d01072dc4660eea9d15007025f35a7a5b58e18%3AModel.valid%7CModel2.valid%7CModel3.valid'; - $disabled = ''; + $unlocked = ''; $this->Controller->request->data = array( 'Model' => array('username' => '', 'password' => '', 'valid' => '0'), 'Model2' => array('valid' => '0'), 'Model3' => array('valid' => '0'), - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); @@ -828,7 +828,7 @@ public function testValidateHasManyModel() { $key = $this->Controller->request->params['_Token']['key']; $fields = '51e3b55a6edd82020b3f29c9ae200e14bbeb7ee5%3AModel.0.hidden%7CModel.0.valid'; $fields .= '%7CModel.1.hidden%7CModel.1.valid'; - $disabled = ''; + $unlocked = ''; $this->Controller->request->data = array( 'Model' => array( @@ -841,7 +841,7 @@ public function testValidateHasManyModel() { 'hidden' => 'value', 'valid' => '0' ) ), - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); @@ -858,7 +858,7 @@ public function testValidateHasManyRecordsPass() { $key = $this->Controller->request->params['_Token']['key']; $fields = '7a203edb3d345bbf38fe0dccae960da8842e11d7%3AAddress.0.id%7CAddress.0.primary%7C'; $fields .= 'Address.1.id%7CAddress.1.primary'; - $disabled = ''; + $unlocked = ''; $this->Controller->request->data = array( 'Address' => array( @@ -883,7 +883,7 @@ public function testValidateHasManyRecordsPass() { 'primary' => '1' ) ), - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); @@ -902,7 +902,7 @@ public function testValidateHasManyRecordsFail() { $key = $this->Controller->request->params['_Token']['key']; $fields = '7a203edb3d345bbf38fe0dccae960da8842e11d7%3AAddress.0.id%7CAddress.0.primary%7C'; $fields .= 'Address.1.id%7CAddress.1.primary'; - $disabled = ''; + $unlocked = ''; $this->Controller->request->data = array( 'Address' => array( @@ -927,7 +927,7 @@ public function testValidateHasManyRecordsFail() { 'primary' => '1' ) ), - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); @@ -943,11 +943,11 @@ public function testFormDisabledFields() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->request->params['_Token']['key']; $fields = '11842060341b9d0fc3808b90ba29fdea7054d6ad%3An%3A0%3A%7B%7D'; - $disabled = ''; + $unlocked = ''; $this->Controller->request->data = array( 'MyModel' => array('name' => 'some data'), - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); $this->assertFalse($result); @@ -958,7 +958,7 @@ public function testFormDisabledFields() { $this->Controller->request->data = array( 'MyModel' => array('name' => 'some data'), - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); @@ -974,30 +974,30 @@ public function testRadio() { $this->Controller->Security->startup($this->Controller); $key = $this->Controller->request->params['_Token']['key']; $fields = '575ef54ca4fc8cab468d6d898e9acd3a9671c17e%3An%3A0%3A%7B%7D'; - $disabled = ''; + $unlocked = ''; $this->Controller->request->data = array( - '_Token' => compact('key', 'fields', 'disabled') + '_Token' => compact('key', 'fields', 'unlocked') ); $result = $this->Controller->Security->validatePost($this->Controller); $this->assertFalse($result); $this->Controller->request->data = array( - '_Token' => compact('key', 'fields', 'disabled'), + '_Token' => compact('key', 'fields', 'unlocked'), 'Test' => array('test' => '') ); $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); $this->Controller->request->data = array( - '_Token' => compact('key', 'fields', 'disabled'), + '_Token' => compact('key', 'fields', 'unlocked'), 'Test' => array('test' => '1') ); $result = $this->Controller->Security->validatePost($this->Controller); $this->assertTrue($result); $this->Controller->request->data = array( - '_Token' => compact('key', 'fields', 'disabled'), + '_Token' => compact('key', 'fields', 'unlocked'), 'Test' => array('test' => '2') ); $result = $this->Controller->Security->validatePost($this->Controller);