直接拼接$where字符串必然出问题,因字段名、操作符属sql结构不可转义或参数化,必须白名单校验;in和between需动态占位符与多值绑定,严禁字符串拼接。

为什么直接拼接 $where 字符串必然出问题
因为用户输入的字段名、操作符、值全不可信。哪怕你对值做了 mysqli_real_escape_string(),攻击者仍可通过构造 name = "id OR 1=1" 或 operator = "IN (1,2) -- " 绕过——这些根本不是“值”,而是 SQL 结构的一部分。字段名和操作符必须白名单校验,不能转义,也不能参数化。
如何安全地允许动态字段名和操作符
只接受预定义的合法字段与操作符,其余一律拒绝。不要试图“过滤”或“清理”字段名——它要么在白名单里,要么报错退出。
-
$allowedFields = ['id', 'status', 'created_at', 'email'],检查$field是否in_array($field, $allowedFields) $allowedOperators = ['=', '!=', '>', ',注意 <code>IN和BETWEEN需特殊处理(见下条)- 字段名不进
prepare()占位符,必须拼入 SQL 字符串前完成白名单校验
怎么处理 IN 和 BETWEEN 这类多值操作符
它们无法用单个 ? 占位符覆盖,必须为每个值单独绑定。不能写成 WHERE id IN (?) 并传入数组——PDO 会把它当一个字符串字面量。
- 对
IN:生成动态占位符,如WHERE id IN (?, ?, ?),再用array_fill(0, count($values), PDO::PARAM_INT)构建类型数组 - 对
BETWEEN:严格要求传入两个值,WHERE price BETWEEN ? AND ?,分别绑定 - 绝不要用
implode(',', $values)拼接,那等于开门揖盗
一个最小可用的 WhereBuilder 类骨架
重点不在“功能多”,而在“每一步都可审计”。下面这段代码能跑通,但关键逻辑已隔离:
class WhereBuilder
{
private array $allowedFields = ['id', 'status', 'email'];
private array $allowedOperators = ['=', '!=', '>', 'public function build(array $conditions): array
{
$parts = [];
$params = [];
foreach ($conditions as $cond) {
$field = $cond['field'] ?? null;
$op = $cond['operator'] ?? '=';
$value = $cond['value'] ?? null;
if (!in_array($field, $this->allowedFields) || !in_array($op, $this->allowedOperators)) {
throw new InvalidArgumentException('Invalid field or operator');
}
if ($op === 'IN' && is_array($value)) {
$placeholders = str_repeat('?,', count($value) - 1) . '?';
$parts[] = "$field IN ($placeholders)";
$params = array_merge($params, $value);
} elseif ($op === 'BETWEEN' && is_array($value) && count($value) === 2) {
$parts[] = "$field BETWEEN ? AND ?";
$params = array_merge($params, $value);
} else {
$parts[] = "$field $op ?";
$params[] = $value;
}
}
return [
'where' => $parts ? 'WHERE ' . implode(' AND ', $parts) : '',
'params' => $params,
];
}
}
真正难的不是写完这个类,而是后续每次加新字段、新操作符时,都得同步更新白名单和分支逻辑——漏一处,防线就塌了。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











