Drupal FAPI tales - need a token?

Submitted by boaz on Tuesday, December 30, 2008.

Long time no post. No time to post. Cest la vide, eh? Lets get to the point shall we?...

Abstract:
I'm implementing a site now with nice amount of self-crafted forms. Drupal of course. Drupal 6 of course.
Now, I had this problem with one of the forms when testing how it works (with the others I didn't reach that state yet):
1. When I submitted the form initially, I got some validation error which was emitted by my own validation function. Perhaps its a good time to mention now that I do my own form building, validation and submission functions. So far so good - the form should have NOT passed validation intentionally.
2. Now, when I resubmitted the form, correcting the problem in my form inputs or not, I got an error messages stating that: "Validation error, please try again. If this error persists, please contact the site administrator". No matter what I fed into the form at this stage - it didn't pass and returned with this problem.

Huh? Ok, lets start searching. And so I searched.
I searched for info on the web, and also in "Pro Drupal development" (2nd ed.) book. There's rather little documentation on this problem, as far as I can say.

Findings:
It appears that somewhere in Drupal v4.7 a new security feature was introduced. This feature is answering a known attack method called CSRF. There is a very nice description of it in the wikipedia link just given so I'll skip its summarizing here (I warmly recommend having a look. Its rather easy to understand how it works if you have enough grasp and experience in how the web works behind the scenes).
So the feature is to have a arbitrary token in each form (as a hidden field) that is checked upon submission of the form and in case it didn't match - that error is shown and the form does not pass validation as far as Drupal FAPI is concerned.

Solution:
I tried playing with the form's cache and other properties. The recipe that did the job is as follows:
1. Make your form building function accept $form_state as its parameter. Its not a must and the forms worked nicely without it, including the inclusion of submitted values presented back to user in case of (intended) validation problem on the user's input.
2. In the form definition, have the #default_value for each element take the value from $form_state. So it should be in the general form of:

'#default_value' => $form_state['values']['FieldName'],

3. In the form validation function, when you decide that a validation problem occurred, in addition to using form_set_error(), also do:

$form_state['rebuild'] = true;

This will ensure that when you decided that the form didn't pass validation, the form will be rebuilt (using your build function including the additions introduced in step 1 and 2 above...) resulting in a fresh token produced for it.

That's it I think. FAPI is not trivial (as Drupal in general) but using cool development tools like PDT (v2 was released yesterday, BTW) with xDebug, one can get along and see its internals in a rather easy manner.

These are some resources of information I've used while checking this problem:
http://drupal.org/node/89999
http://drupal.org/node/100445

Leave a Comment

Fields with * are required.