Avoid using data from $form_state['input']

Last updated on
20 September 2016

Drupal 7 will no longer be supported after January 5, 2025. Learn more and find resources for Drupal 7 sites

In the Form API, using data from $form_state['input'], $_POST, and/or \Drupal::request()->request (Drupal 8) are a security risk.

$form_state['input'] and \Drupal::request()->request are actually copies of the raw, unsanitized data from PHP's $_POST superglobal, so the data it contains has not been type-checked.

Use data from $form_state['values'] (Drupal 7) or $form_state->values (Drupal 8) instead.

High-level overview of the problem

Essentially, the issue comes down to the way that browsers send form data, the way that PHP interprets GET and POST data, and the fact that certain built-in PHP functions, database drivers, Drupal functions, and library functions can accept both strings or arrays (and may act in ways you do not expect when passed data of a type you did not expect).

Specifically, you may have put a textfield on the page in your form, but the raw data returned for the textfield in $form_state['input'] might not be a string: it could be an uploaded file or an array.

A more detailed example

Recall that, when you submit HTML like the following...

<form action="/" method="post">
  <input type="text" name="foo" value="bar" />
  <input type="text" name="baz" value="qux" />
  <input type="submit" value="Submit" />
</form>

... your browser will send an HTTP request that looks (roughly) like...

POST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: example.com

foo=bar&amp;baz=qux

PHP will automatically de-serialize this into the $_POST super-global as...

$_POST = array(
  'foo' => 'bar',
  'baz' => 'qux',
);

Note that the web browser does not pass any information about the type of form control, and that PHP doesn't know about the HTML that generated the response.


However, when you submit a form like the following...

<form action="/" method="post">
  <input type="checkbox" name="foo[1]" value="bar" checked="checked" />
  <input type="checkbox" name="foo[2]" value="qwe" checked="checked" />
  <input type="text" name="baz" value="qux" />
  <input type="submit" value="Submit" />
</form>

... that will result in an HTTP request like...

POST / HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: example.com

foo%5B1%5D=bar&amp;foo%5B2%5D=qwe&amp;baz=qux

But PHP will interpret that a bit differently than you might expect...

$_POST = array(
  'foo' => array(
    1 => 'bar',
    2 => 'qwe',
  ),
  'baz' => 'qux',
);

Note that, in both examples, PHP's $_POST array contains an entry named foo, but in the first example it is a string, and in the second example, it is an array.

The security issue

This can cause unexpected behavior in your PHP code if it expects $_POST['foo'] to be a string but it is actually an array, and vice-versa.

Famously, attacks for SA-CORE-2014-005 leveraged a weakness in the form API input processing which has since been fixed, because the database driver would act differently if passed a string versus an array.

The solution

The Form API's drupal_validate_form() and _form_validate() functions take care of populating a safe set of data in $form_state['values'], using sanitized data from $form_state['input'].

Notes

Help improve this page

Page status: No known problems

You can: