安全地将数组转换为SQL查询(PHP7 + PDO+ SQLite)



使用PHP7+PDO+SQLite,我正在寻找一种方法,使用前端生成的数组形式的用户定义过滤器过滤表中的条目。为了清楚起见,以下是我正在寻找的示例:

示例

数据库中的表:

ID             Firstname      Lastname       Age
+--------------+--------------+--------------+-------------+
| 0            | John         | Doe          | 21          |
| 1            | John         | Smith        | 35          |
| 2            | Alice        | Smith        | 35          |
| 3            | Bob          | Smith        | 40          |
+--------------+--------------+--------------+-------------+

过滤 器:

/*
Filters follow the structure:
[
"Table Column Name 1" => [Match1 OR Match2 OR ...],
AND
"TCN 2" ...
]
Any column name not provided should match any value.
*/
[
"Firstname" => ["John"]
]
// ^ fetchAll() returns rows with ID 0 and 1
[
"Firstname" => ["John", "Alice"],
"Lastname" => ["Smith"]
]
// ^ ID 1 and 2 (but not 3)

我需要找到一个函数,可以将上面的数组转换为SQL查询,以获取与过滤器匹配的所有行。我想使用 SQL(我没有太多经验(来做到这一点,而不是出于性能原因获取所有行并使用 PHP 过滤它们。

我知道我可以做一些类似的事情:

// Untested - for illustration purposes only
$filter = [
"Firstname" => ["John", "Alice"],
"Lastname" => ["Smith"]
];
$sql = "SELECT * FROM table_name WHERE ";
foreach ($filter as $column => $matching_values) {
foreach ($matching_values as $match) {
$sql .= $column . " == " . $match . " OR ";
}
// Ugly way of removing trailing ` OR `
$sql = substr($sql, 0, -4);
$sql .= " AND ";
}
// Ugly way of removing trailing ` AND `
$sql = substr($sql, 0, -5);
echo $sql;

但这引入了一个巨大的SQL注入安全漏洞。我想知道是否有一种简单而高效和安全的方法来实现这一点。如果需要,也可以更改过滤器的格式,例如更改为 RegEx(如果有人可以用 RegEx 替换上面示例中最里面的数组,则可以获得奖励积分(。

或者(或另外(,一种以可搜索的方式描述我的问题的简单方法可能会有所帮助,因为我无法对此进行太多研究,因为我无法很好地表达它。

这将是我已经在我的网站上教授的几种技术的有趣组合,即

  • 动态创建 WHERE 子句
  • 为 IN 子句创建预准备语句
  • 白名单以防止 SQL 注入

最后会是这样的

$allowed = ["Firstname", "Lastname"];
$conditions = [];
$parameters = [];
foreach ($filter as $key => $values) {
if (array_search($key, $allowed, true) === false) { 
throw new InvalidArgumentException("invalid field name!"); 
}
$conditions[] = "`$key` in (".str_repeat('?,', count($values) - 1) . '?'.")";
$parameters = array_merge($parameters, $values);
}
$sql = "SELECT * FROM table_name ";
if ($conditions)
{
$sql .= " WHERE ".implode(" AND ", $conditions);
}

将生成您正在寻找的查询,

SELECT * FROM table_name WHERE WHERE `Firstname` in (?,?) AND `Lastname` in (?)

以及要在PDO::execute()中使用的值数组,使其成为一种防弹解决方案,其中值通过参数和字段名称进行保护,以防止通过针对白名单进行过滤

我唯一的怀疑是问题中存在的情况可能过于简单化。一旦您不仅需要精确匹配,还需要部分匹配或更大/更少的比较,代码的复杂性就会飙升。

最新更新