PHP:有一种安全的提取方式($ _ POST)

时间:2011-03-15 01:21:33

标签: php

是否有一种安全的方法可以在已发布的数组中自动分配密钥?以下是错误方式的两个例子......

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

extract($_POST)

是否有更好的方法,或者最好是编码:

$foo = $_POST('foo');
$bar = $_POST('bar');
....

我表单上的所有50个输入?

(发布的信息将被插入数据库中)。

7 个答案:

答案 0 :(得分:16)

一次性提取所有输入字段的另一种谨慎方法是:

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

这样,所有输入变量至少会被称为$form_foo$form_bar。避免在全球范围内这样做 - 不是因为全球是邪恶的,而是因为没有人在那里清理。

但是,由于您主要在本地化范围内执行此操作,因此您可以应用htmlentities,例如,您需要所有字段仅用于输出:

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

答案 1 :(得分:9)

没有一个理由这样做 要处理用户输入,数组比单独的变量好100倍

答案 2 :(得分:3)

我喜欢这样一种方法,让你让一个类中的动态getter和setter为你完成所有的工作。以下是我编写代码的方法。

首先,创建一个低音类来保存数据:

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;
    }
  }
}

接下来,创建一个特定的类,用于某些特定的表单,其中有一些特殊要验证的内容。使用您作为程序员的自由来以任何您想要的方式实现它。请记住,由于我们使用反射来寻找setter方法,我们可以为已知的问题区域编写特定的setter方法,例如以“注册用户”形式检查相同的密码:

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']);
    }
  }
}

最后,在“注册用户”流程中使用派生类,以便轻松找出两个输入的密码是否匹配:

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

您可以通过创建一个HTML表单来测试这一点,该表单包含一个名为“password”的输入字段,另一个输入字段的名称为“password_repeat”。

要访问任何表单数据,请使用表单数据对象变量名称,然后使用访问运算符“dash than than” - > ,后跟参数的名称。在上面的示例中,如果有一个名为“user_name”的输入字段,则可以通过调用

来访问它
$registerFormParameterHandler->user_name

Rr,如果您已经在其他变量中定义了要获取的字段的名称,请使用反射:

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

玩得开心! :)

答案 3 :(得分:2)

虽然最好使用$_POST['variablename']引用它们,但可以只展开您期望的变量。

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

或者,我更喜欢这个:

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

答案 4 :(得分:2)

安全 将变量提取到本地范围的方法是不是。您将变量注入本地范围,但这是一个问题。即使您将变量限制为几个不会与范围现在中的其他变量名称冲突的选择变量,如果您开始向表单添加元素,您可能会遇到麻烦。

数组专门用于保存无限量的命名值,而不会占用变量名称空间。使用它们!您可能需要输入更多内容,但这与课程相同。

答案 5 :(得分:1)

您的问题的答案取决于程序员的计算机,语言和安全知识。处理$ _POST的开场顺序有点像国际象棋游戏中的开场动作。许多人使用foreach循环而没有意识到foreach将以你使用它的方式复制$ _POST的内容(编程PHP:第5章,第128-129页)。如果只是使用foreach

导致缓冲区溢出,那会不会很有趣

一位评论者暗示,所有事情都应该在$_POST超全局内部进行。这有一些优点......但是,忘记缓存一段时间,访问数组值比直接访问变量要慢。

由于您有五十(50)个控件要进行验证(可能包含大量内容),因此我可能不希望该阵列访问性能达到50次以上(原始访问点击次数)。此外,如果您担心编写安全输入验证例程,将洗衣(未经验证的输入)与 clean (验证输入)洗衣分开是一个好主意。也就是说,你可能还需要一个 clean 数组(因此是$ _POST主张的响应),但至少你通过保持希望良好来降低流程中的风险可能不好的分开。

是否有一种安全的方法可以在已发布的数组中自动分配密钥?

我可能会这样开始:

此示例的函数库。


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');
}

请注意,我使用$controlNames数组进行预处理,因此我无需向$_POST询问密钥。毕竟,我应该了解他们! :-)两个用户定义的函数all_controls_submitted()all_controls_set()是在尝试使用$_POST中的任何值之前应该问的两个基本问题(我说,无论如何)。不可否认,我在$_POST中使用了all_controls_submitted(),但只是为了获取所提交控件的名称,而不是值。

$postKeys = array_keys($_POST);

但是,谁能说控件名称本身不会有毒并且需要输入验证?!! $ _SERVER中的值也是如此。参见国际象棋比赛。

是否有一种安全的方法可以在已发布的数组中自动分配密钥?我不能肯定地告诉你,但是上面的代码可能会有所帮助吗?你至少拥有钥匙。

PHP编程:第5章,第125页

答案 6 :(得分:0)

尝试使用内置方法/功能 filter_input

参考网址:php.net function filter input