使SQL查询安全-从$_POST获取值


Make SQL query secure - getting values from $_POST

我使用这段代码更新mysql表中的字段。

foreach ($_POST['changed'] as $SubArray) {
    foreach ($SubArray as $key => $value) {
        if ($key === 'recid') continue;
        $STH = $DBH->prepare("UPDATE clients SET $key = '$value' WHERE id = $SubArray[recid]");
        $STH->execute();
    }    
}

我认为查询中的$key$value$SubArray[recid]等变量是非常不安全的。有没有一种方法可以确保这一点的安全,即$_POST不会让不需要的代码进入查询?

(用占位符调用列是不可能的,因为我不知道表中的列名)

只有在确保$key的值是安全的情况下,这才是安全的。例如:

$approved = array('fname', 'lname' ,'email');
foreach ($_POST['changed'] as $SubArray) {
    foreach ($SubArray as $key => $value) {
       // if ($key === 'recid') continue; (Since we're using in_array - no need in this line)
         if(in_array($key , $approved))
         {
           //$key is safe: 
               $STH = $DBH->prepare("UPDATE clients SET $key = :value WHERE id = :id");
               $STH->execute(array(':value' => $value , ':id' => $SubArray['recid']));
         }
      }    
}

更新请注意,您使用的是循环中的循环,为了提高效率,请考虑更改代码。

有了safeMysql,这就是小菜一碟:

include 'safemysql.class.php';
$db = new safeMysql();
$allowed = array('name', 'surname', 'all the fields you want to save');
foreach ($_POST['changed'] as $SubArray)
{
    $insert = $db->filterArray($SubArray, $allowed);
    $db->query("UPDATE clients SET ?u WHERE id = ?s", $insert, $SubArray['recid']);
}

而有缺陷的PDO会给你带来更多的麻烦
首先,您需要使用一个函数来动态创建值列表,因为没有一个理智的开发人员会逐个更新字段:

function pdoSet($fields, &$values, $source = array()) {
    $set = '';
    $values = array();
    if (!$source) $source = &$_POST;
    foreach ($fields as $field) {
        if (isset($source[$field])) {
            $set.="`".str_replace("`","``",$field)."`". "=:$field, ";
            $values[$field] = $source[$field];
        }
    }
    return substr($set, 0, -2); 
}

现在你可以开始运行你的更新

$allowed = array('name', 'surname', 'all the fields you want to save');
foreach ($_POST['changed'] as $SubArray)
{
    $set = pdoSet($allowed, $values, $SubArray);
    $sql = "UPDATE clients SET $set WHERE id = :id";
    $stm = $dbh->prepare($sql);
    $values["id"] = $SubArray['recid'];
    $stm->execute($values);
}

回答评论中的问题:

首先,一个不了解自己领域的开发人员是胡说八道。不可能。像phpmyadmin这样的自由格式应用程序是一个罕见的例外,但事实显然并非如此。

毕竟,这是一个安全问题。自由形式阵列存在两种危险:

  1. 表中可能有客户不允许的字段。您肯定会重用这些代码,让客户编辑他们的详细信息。因此,最好总是知道必须更新的字段
  2. 通过命名占位符的纯SQL注入(正如您可能注意到的那样,它们直接从用户输入开始)

因此,如果你100%确定不能添加任何人工场,你可能会省略这个检查,但你会把自己置于持续的危险之中。无论如何,str_replace必须用于字段名称,而必须使用位置(?)占位符而不是命名占位符。