PHP字符串替换以准备PDO SQL

时间:2019-07-16 19:54:52

标签: php sql pdo parameters

我有一些SQL查询,这些查询使用PHP字符串变量在PDO prepare()之前创建查询。

$connection = new PDO(...);

// Make variable placeholder for each column.
$params = array();
foreach ($row as $col => $value) {
  $params[':' . $col] = $value;
}
$columns = implode(', ', array_keys($row));
$values = implode(', ', array_keys($params));

$query = "
  INSERT INTO my_table ($columns)
  VALUES ($values)
";

$statement = $connection->prepare($query);
$statement->execute($params);

或与SELECT类似的内容:

$query = "
  SELECT field
  FROM my_table
  WHERE id IN ($ids)
";

查询将变为

$query = "
  SELECT field
  FROM my_table
  WHERE id IN (:id0, :id1, :id2)
";

,然后execute()函数将传递类似array(':id0' => 0, ...)的参数。

如果要插入的部分只是一堆占位符,可用于查询准备,这是否容易注入?还有使用PDO在PHP中执行此操作的更好方法吗?

2 个答案:

答案 0 :(得分:1)

在绑定动态数量的参数时,我将恢复为?占位符。您可以这样做:

$placeholders = implode(',', array_fill(0, count($values), '?'));
$query = "SELECT field FROM my_table WHERE id IN ($placeholders)";
$stmt = $pdo->prepare($query);
$stmt->execute($values);

如果列名是动态的,则仍然需要进行字符串替换,例如您的INSERT示例。那些应该列入白名单以防止注射。但是您可以对插入的值使用上述机制。您需要使用

$values = array_values($params);

因为?占位符无法从关联数组中填充。

答案 1 :(得分:0)

我写的东西可以做类似的事情,但是在这里继续太复杂了。我为您编写了一个示例供您使用,我绝不会说这是100%安全的事情。我确实在其中添加了一些检查,但我认为逻辑就是开始所需的一切。我在没有PDO的RexTester上进行了宽松的测试,它的功能与我想要的一样。

$ExampleParams = array('col1'=>'val', 'col2'=>'val');
function Insert(array $Params) {
    $i = 1; // iteration counter
    $Columns = $Selectors = "";
    // All columns that can be inserted
    $ColumnWhitelist = array('col1', 'col2', 'col3', 'col4');
    // Iterate through parameters
    foreach ($Params as $Key=>$Value) {
        if (!in_array($Key, $ColumnWhitelist)) {
            return FALSE; // Bad column
        } else {
            $Columns .= sprintf('`%s`%s', $Key, sizeof($Params) != $i ? ',' : '');
            $Selectors .= sprintf('`:%s`%s', $Key, sizeof($Params) != $i ? ',' : '');
        } $i++; 
    }

    // Time to start PDO
    $dbh = new PDO(...);
    $cmd = $dbh->prepare(sprintf('INSERT INTO `table` (%s) VALUES (%s);', $Columns, $Selectors));
    // Fill in parameters
    foreach ($Params as $Key=>$Value) {
        $cmd->bindParam(sprintf(':%s', $Key), $Value);
    } return $cmd->execute();
}

这可以用于任何类型的查询。就像我之前说过的那样,我认为这不是100%安全的,但是白名单对于确保我们没有不好的查询是必要的。可以使用一维数组来完成,但是多维数组最适合IMO。