PHP - 具有未知数量参数的安全 PDO 准备语句

Posted

技术标签:

【中文标题】PHP - 具有未知数量参数的安全 PDO 准备语句【英文标题】:PHP - secure PDO prepared statement with an unknown number of parameters 【发布时间】:2018-02-20 22:44:36 【问题描述】:

*我在 mysqli 或 PDO 的边缘案例中看到了关于这个主题的问题,但没有找到这个特定的问题(这让我感到惊讶,但也许我不知道如何搜索)

在尝试使用 PDO 之前,我使用了简单的、不受保护的查询。我的准备函数看起来像这样,经过简化:

static function prepareUpdate($table, array $arguments, array $filter) 
    $argumentsList = self::associateArguments($arguments);
    $filterList = self::associateArguments($filter);

    $query = "UPDATE `$table` SET $argumentsList WHERE $filterList;";
    return $query;

$arguments 和 $filter 是关联数组:例如,['name' => 'John', 'occupation' => 'Carpenter'] 用于 $arguments,['employeeId' => 518] 用于过滤器。假设 $table 是“工人”。通过一些简单的函数,就变成了:

argumentsList 变成

`name` = 'John', `occupation` = 'Carpenter'

filterList变成

`employeeId` = 518

所以最后的字符串变成:

UPDATE `workers` SET `name` = 'John', `occupation` = 'Carpenter' WHERE `employeeId` = 518;

这对我来说似乎很简单。然后,我尝试在 PDO 的准备语句中使用类似的逻辑。但是我遇到了麻烦。也许是因为我不了解它背后的预期哲学?或者只是技术细节。

如果我理解正确的话,PDO 需要在 prepare() 之后使用 bindValue() 等方法来防止注入。而且,我必须使用占位符(:name、:occupation 等)。

问题是,如果我不知道要查找哪些列,甚至不知道要查找多少列,我该怎么做?

我看到的大多数示例都包含一个非常预定义的语句:UPDATE tableX SET `name` = :name WHERE `id` = :id - 或类似的内容。

如果我想使用可变数量的参数怎么办?我的理解是,我只能用一个属性替换一个 :placeholder,而不是更多(因此不需要引号),所以我不能完全写出类似的东西:

UPDATE :table SET :allParametersHere ;

我该怎么做呢?我没有要在其上使用此函数的明确表,更不用说要检查的有效列的合理列表。我将如何创建一个可以处理不同参数数量的包装器?

【问题讨论】:

您是否尝试过这里的任何答案? ***.com/questions/12344741/… -- 看起来很有希望 几乎@Drew 这个答案:***.com/a/23298742/1194525 由于您使用的是 PDO,因此无需使用 bindParambindValue:只需将标记的值数组传递给 execute 方法即可。 @aendeerei Dwarf 的模板有问题,用于可变数量的参数,而不是绑定。 相关:An SQL injection against which prepared statements won't help 【参考方案1】:

对于此类问题,database query builder 的解决方案会更好。

回到问题:你知道参数的数量,所以你可以创建查询模板。

<?php
$wq = $pq = [];
$allowed_keys = ['name', 'occupation', '...'];

foreach ($arguments AS $key => $value) 
  if(in_array($key, $allowed_keys))
    $pq[] = "$key = :$key" ;
  else throw new Exception('Go away hacker!');

foreach ($filter AS $key => $value)
  if(in_array($key, $allowed_keys))
    $wq[] = "$key = :$key" ;

$q = "UPDATE $table SET ". join(', ', $pq);
if(!empty($wq))
  $q .= 'WHERE '. join(' AND ', $wq);

在结果中,您会得到如下查询模式:

UPDATE workers SET name = :name, occupation = :occupation WHERE employeeId = :employeeId;

允许这样的折叠(如果你有这些表的“安全”密钥)

$allowed_keys 或更好的$allowed_column_names

【讨论】:

我试图做这样的事情,但你的解决方案更优雅。谢谢你的回答。我的问题是,这是否会损害此 PDO 的安全性,因为 $key 变量不会被转义?当然,从技术上讲,它应该来自开发人员,因此应该受到信任,但我有点假设一切都需要安全。除非我们手动转义它。不信任自己的代码听起来很有趣,但如果这是一个接口原型,例如呢? @DwarfVader 只需确保它来自开发人员,或者更确切地说,来自代码。从代码中预定义的列表中获取被认为足够安全的密钥。尽管您仍然可以格式化标识符。如果键基本上是由用户定义的,例如条件 UPDATE 或 WHERE,只需再次将它们过滤掉,against a predefined and hardcoded list 但这还是让人有点失望。你有没有想过使用 ORM?然后代码可能如下所示:Model::factory('Workers', ['employeeId' =&gt; 518])-&gt;values($arguments)-&gt;save()(来自 Kohana 的示例 docs.koseven.ga/guide/orm/examples/validation)

以上是关于PHP - 具有未知数量参数的安全 PDO 准备语句的主要内容,如果未能解决你的问题,请参考以下文章

PHP PDO 连接到具有集成安全性的 SQL Server?

获取传递给具有未知数量参数的方法的参数数量

调用具有未知数量参数Python的函数

正则表达式具有未知数量参数的字符串

在PHP中与未知数量的数组相交

使用 PDO 准备参数化查询