More Zend Framework Fun
Zend Framework Form Hash Ajax
This one sucked hunting down the answer.
The problem. I would include a token (hash) in the form I was producing. I set the form as a checkbox that as soon as it's checked it fires off an AJAX request with the token, and data. The first time it worked great, every other time it would tell me the Token was missing. Hmmmm....
http://codeutopia.net/blog/2008/10/16/how-to-csrf-protect-all-your-forms...
Hi Jani, I got a work around for the problem. The real problem is this, for AJAX requests, it works for first time and not for the subsequent requests. The AJAX request was having the same token for all it’s requests but the plugin was generating unique token for all the requests. So is the mismatch / problem. That was the problem.
Workaround:
What I did was, for each AJAX request, I stopped generating a new token and used the already generated one to compare. So by this way I can cover for multiple AJAX requests. It could be vulnerable for the time that it lives and after that I will eject (logout) the user from the system and then they (attacker) will have to login again and get a token and then attack (if they wanted to really). That was it.
I never did find an answer yet, I did post it to StackOverFlow
http://stackoverflow.com/questions/2474774/how-to-use-zend-framework-for...
Instead I did the whole thing manually..
Form
class Form_GroupListForm extends Zend_Form { public function __construct($options = null) { parent::__construct($options); $this->setName('grouplist'); $groupmodel = new Default_Model_Group(); $groups = new Zend_Form_Element_MultiCheckbox('groups'); $groups->addMultiOptions($groupmodel->getGroupList()) ->setLabel('Groups') ->setDescription('Groups this person belongs to:') ->setAttrib('class', 'ajaxcheckbox') ; $token = new Zend_Form_Element_Hash('grouplisttoken'); $token->setSalt('hello') ->setTimeout(3600) // Form times out after one hour ->removeDecorator('HtmlTag') ->removeDecorator('Label'); $myNamespace = new Zend_Session_Namespace('authtoken'); $myNamespace->authtoken = $hash = md5(time()); $auth = new Zend_Form_Element_Hidden('authtoken'); $auth->setValue($hash) ->setRequired('true') ->removeDecorator('HtmlTag') ->removeDecorator('Label'); $this->addElements(array($groups, $auth)); } }
Notice the session being generated then stored as a hidden field. Dumb that I have to basically replicate the hash feature, but the hash has an hop of 1, meaning that it can only be used for one page refresh then it expires. There should be a way to persist this token over several ajax calls.
Comments
There is a solution. Create,
There is a solution.
Create, besides the form that will contain the data, a form without elements. From the controller you instantiate the two forms. Also in the controller, you add the element hash to the empty form. Both forms should be sent to the vision. Then, in the condition "if ($ request-> isXmlHttpRequest ())" in the controller you render the empty form. Then, you take the hash value with the method "getValue ()". This value must be sent in response by Ajax and then use JavaScript to replace the hash value that is already obsolete. The option to create an empty form for the hash is to avoid problems with other elements such as, for example, the captcha that would have its id generated again if the form were rendered, and would also need to have the new information replaced. The validation will be done separately because there are two distinct forms. Later you can reuse the hash (empty) form whenever you want. The following are examples of the code.
//In the controller, after instantiating the empty form you add the Hash element to it: $hash = new Zend_Form_Element_Hash('no_csrf_foo'); $hash_form->addElement('hash', 'no_csrf_foo', array('salt' => 'unique')); //... //Also in the controller, within the condition "if ($request->isXmlHttpRequest())" you render the form (this will renew the session for the next attempt to send the form) and get the new id value: $hash_form->render($this->view); $hash_value['hash'] = $hash_form->getElement('no_csrf_foo')->getValue();//The value must be added to the ajax response in JSON, for example. One can use the methods Zend_Json::decode($response) and Zend_Json::encode($array) for conversions between PHP array and JSON. //--------------------------------------- //In JavaScript, the Ajax response function: document.getElementById("no_csrf_foo").value = data.hash;//Retrieves the hash value from the Json response and set it to the hash input.I acheived this by using the
I acheived this by using the following:
on form error in the controller:
then in my ajax call I use: (jQuery)
this will update the hash hidden element with the correct value.
Cheers,
Jay
@Jason, So basically you're
@Jason,
So basically you're creating a new hash if there is an error in the form, that the user needs to fix.
In the program I was writing, I was using the same form elements to edit via ajax elements on the page. For instance, I made a Zend_Form that was hidden on the page, then used javascript to populate the form, show it, allow the customer to make changes, then submit the form via AJAX. It would work for the first edit, but because I was essentially reusing form elements for subsequent edits it would fail because the hash had already been used and then unset.
Maybe that is the whole point of using a hash, so that a form cannot be submitted twice, but I manually created a check so that the hash would time out after 15 minutes.
I may need to rethink my process, we'll see.
Post new comment