Php 使SQL查询安全-从$\u POST获取值

Php 使SQL查询安全-从$\u POST获取值,php,mysql,security,pdo,Php,Mysql,Security,Pdo,我使用这段代码更新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]");

我使用这段代码更新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]
等变量非常不安全。有没有一种方法可以确保这一点,即通过
$\u POST
不会有不需要的代码进入查询

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

这是小菜一碟:

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这样的自由格式应用程序是一个罕见的例外,但情况显然并非如此

毕竟,这是一个安全问题。两种危险来自自由形式的数组:

  • 表中可能有不允许客户访问的字段。您肯定会重用此代码,让客户编辑他们的详细信息。所以,最好总是知道需要更新的字段
  • 通过命名占位符的普通SQL注入(正如您可能注意到的那样,它们直接从用户输入进行,未被触及)
    所以,如果您100%确定不能添加任何人工场-您可以忽略此检查,但您将自己置于持续的危险中。不管怎样,str_replace必须用于字段名,而位置(?)占位符必须用于代替命名占位符

    只有在确保
    $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']));
             }
    
          }    
    }
    
    更新 请注意,您正在循环中使用循环,
    为了更高效,考虑更改代码。

    你正在使用变量准备吗?重点是什么?考虑阅读准备语句的用法。(例如使用占位符)首先,您根本不能使用占位符调用列-您只能绑定值。但您可以创建已知良好列名的白名单,并确保
    $key
    在该数组中。其次,正如@OfirBaruch所说,您仍然可以绑定这些值,这也将有助于使您的代码更加安全。$key可以通过确保其具有特定的值来“保护”(查看语句前的条件)此条件不能保证任何事您是对的,添加了一个“条件”这将使我的解决方案合法化,因此您将在一个循环中运行所有这些内容?
    filterray
    也是一个循环()。是吗?谢谢您的解决方案-在这行
    $allowed=array('name','姓氏','所有要保存的字段')我必须设置列(名称)。但是,当我不知道列(名称)时,我该怎么办?