开发者

PHP: is there a safe way to extract($_POST)

Is there a safe way to auto assign the keys in a posted array? Below are two examples of wrong ways...

foreach( $_POST as $key => $value ) {
     $$key = $value;
}

or

extract($_POST)

Is there a better way, or is it best to code:

$foo = $_POST('foo');
$bar = $_POST('开发者_JS百科bar');
....

for all 50 inputs on my form?

(the posted info will be inserted into a database).


One more cautious way of extracting all input fields at once is:

extract( $_POST, EXTR_OVERWRITE, "form_" );

This way all your input variables will be called $form_foo and $form_bar at least. Avoid doing that in the global scope - not because global is evil, but because nobody ever cleans up there.

However, since mostly you do that in a localized scope, you can as well apply htmlentities if for example you need all fields just for output:

extract(array_map("htmlspecialchars", $_POST), EXTR_OVERWRITE, "form_");


There is not a single reason to do it.
To handle user inputs an array is 100 times better than separate variables


I like an approach where you let dynamic getters and setters in a class do all the work for you. Here's how I would code it.

First, create a bass class to hold data:

class FormParameterHandler {
  protected $parameters;
  public function __construct($associative_array) {
    $this->parameters = array();
    foreach($associative_array as $key => $value) {
      $this->{$key} = $value;
    }
  }
  public function __get($key) {
    $value = null;
    if(method_exists($this, "get_$key")) {
      $value = $this->{"get_$key"}();
    } else {
      $value = $this->parameters[$key];
    }
    return $value;
  }
  public function __set($key, $value) {
    if(method_exists($this, "set_$key")) {
      $this->{"set_$key"}($value);
    } else {
      $this->parameters[$key] = $value;
    }
  }
}

Next, create a specific class to use for some specific form where there is something special to validate. Use your freedom as a programmer here to implement it any way you want to. And remember, since we're using reflection to look for setter methods, we can write specific setter methods for known problem areas, like e.g. to check for equal passwords in a "register user" form:

class RegisterFormParameterHandler extends FormParameterHandler {
  private $passwords_are_equal = null;
  public function __construct($register_form_parameters) {
    parent::__construct($register_form_parameters);
  }
  public function has_equal_passwords() {
    return $this->passwords_are_equal;
  }
  public function set_password($password) {
    $this->parameters['password'] = $password;
    $this->compare_passwords();
  }
  public function set_password_repeat($password_repeat) {
    $this->parameters['password_repeat'] = $password_repeat;
    $this->compare_passwords();
  }
  private function compare_passwords() {
    if(isset($this->parameters['password']) && isset($this->parameters['password_repeat'])) {
      $this->passwords_are_equal = ($this->parameters['password'] === $this->parameters['password_repeat']);
    }
  }
}

Finally, use the derived class in a "register user" flow, to easily find out if the two entered passwords match:

$registerFormParameterHandler = new RegisterFormParameterHandler($_POST);
if($registerFormParameterHandler->has_equal_passwords()) {
  print "are equal";
  //register user
} else {
  print "are not equal";
}

You can test this by creating an HTML form that has one input field with the name "password", and another input field with the name "password_repeat".

To access any of the form data, use your form data object variable name, followed by the access operator "dash larger than" -> , followed by the name of the parameter. In the example above, if there was an input field named "user_name", it would be accessed through a call to

$registerFormParameterHandler->user_name

Rr, if you have defined the name of the field you want to get in some other variable, use reflection:

$registerFormParameterHandler->{$settings['form_data_user_name']}

Have fun! :)


A safe way to extract variables into the local scope is not to. You're injecting variables into your local scope, which is a problem however you do it. Even if you limit the variables to only a few select ones that won't clash with other variable names in the scope now, if you start adding elements to your form you may be in trouble later.

Arrays are specifically for holding an unlimited amount of named values without crowding the variable namespace. Use them! You may have to type a little more, but that's par for the course.


While it is best to refer to them with $_POST['variablename'], it is possible to expand only the variables you are expecting.

$expected = array('name', 'telephone', /* etc */);
foreach ($_POST as $key => $value) {
  if (!in_array($key, $expected)) {
    continue;
  }
  ${$key} = $value;
}

Or, I prefer this:

foreach ($_POST as $key => $value) {
  switch ($key) {
    case 'name':
    case 'telephone':
    /* etc. */
      ${$key} = $value;
      break;
    default:
      break;
  }
}


The answer to your question depends on the computer, language, and security knowledge of the programmer. The opening sequence of processing $_POST is kind of like the opening move in a game of chess. Many use foreach loops without realizing that foreach will make a copy of the contents of $_POST the way you have it used (Programming PHP: Chapter 5, p.128-129). Wouldn't it be funny if you caused a buffer overflow simply by using foreach!

One commenter implied that everything should just be worked with inside of the $_POST superglobal. There are some merits to this... However, forgetting cache memory for a moment, access to array values is slower than direct access to a variable.

Since you have fifty (50) controls to validate (with potentially large contents), I might not want to take that array access performance hit more than 50 times (the original access hits). Moreover, if you are concerned about writing secure input validation routines, keeping your dirty laundry (non-validated input) separate from your clean (validated input) laundry is a good idea. That said, you may need a clean array anyway (hence the $_POST advocate's response), but at least you are reducing risk in the process by keeping the hopefully good separate from the potentially bad.

Is there a safe way to auto assign the keys in a posted array?

I might start like this:

Function library for this example.


function all_controls_submitted($controls) {
  $postKeys = array_keys($_POST);
  foreach($controls as $key) {
    if(! array_key_exists($key, $postKeys)) {
      return false;
    }
  }
  return true;
}

function all_controls_set($controls) {
  foreach($controls as $key) {
    if(! isset($_POST[$key])) {
      return false;
    }
  }
  return true;
}

if(is_array($_SERVER) && isset($_SERVER['REQUEST_METHOD'], $_SERVER[REQUEST_URI]) && $_SERVER['REQUEST_METHOD'] === 'GET' && $_SERVER['REQUEST_URI'] === '/contact.php') {
  $newForm = true;
} elseif (is_array($_SERVER) && isset($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']) && $_SERVER['REQUEST_METHOD'] === 'POST' && $_SERVER['REQUEST_URI'] === '/contact.php') {
  $newForm = false;
  $controlNames = array('firstName', 'lastName', 'e-mail', 'company', 'subject', 'message', 'captcha');
  define('NUM_CONTROLS_EXPECTED', count($controlNames)); //Seven (7)
  if(is_array($_POST) && count($_POST) === NUM_CONTROLS_EXPECTED && all_controls_submitted($controlNames) && all_controls_set($controlNames)) {
    //Begin input validation
  }
} else {
  header('location: http://www.nsa.gov');
}

Notice that I prep things with the $controlNames array, therefore I do not have to ask $_POST for the keys. After all, I should know them! :-) The two user-defined functions, all_controls_submitted() and all_controls_set() are two fundamental questions that should be asked before trying to use any values in $_POST (I say, anyway). Admittedly, I do use $_POST in all_controls_submitted(), but only to obtain the names of the controls submitted, not the values.

$postKeys = array_keys($_POST);

However, who is to say that the control names themselves could not be poisonous and in need of input validation?!! Same goes for values in $_SERVER. See, a chess game.

Is there a safe way to auto assign the keys in a posted array? I cannot tell you for certain, but perhaps something like the code above could help? You'd have the keys, at least.

Programming PHP: Chapter 5, p.125


Try this inbuilt method/function filter_input

Reference URL: php.net function filter input

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜