动态SQL必须用PDO预处理,结构部分(字段名、表名等)需白名单校验并反引号包裹,数据值一律参数绑定;WHERE条件动态生成占位符,ORDER BY和LIMIT须枚举校验与类型强转。

动态拼接 SQL 时,绝不能直接拼接用户输入,必须全程使用 PDO 预处理语句(Prepared Statements)配合占位符。核心原则是:字段名、表名、排序方向、LIMIT 参数等“结构部分”无法用问号占位符绑定,需通过白名单严格校验;而查询值(WHERE 条件、INSERT 值等)一律走参数绑定。
字段名与表名:只允许白名单匹配
PDO 的 ? 和 :name 占位符仅适用于数据值,不支持用于标识符(如列名、表名、ORDER BY 字段)。若需动态指定,必须预先定义合法范围,并做严格比对:
- 定义允许的字段数组:
$allowed_columns = ['id', 'title', 'status', 'created_at']; - 接收用户传入的字段名(如
$_GET['sort']),检查是否在白名单中:in_array($sort, $allowed_columns, true) - 确认通过后才拼入 SQL:
"ORDER BY `$sort` DESC"(注意用反引号包裹,防关键字冲突) - 同理,表名也需白名单控制,禁止任何形式的用户输入直接进 FROM 或 JOIN 子句
WHERE 条件:用键值对 + 动态占位符生成
根据运行时条件组合 WHERE 子句时,可构建键值映射,再动态生成占位符和参数数组:
- 例如搜索条件:
$filters = ['status' => 1, 'title' => '%PHP%']; - 遍历生成
$whereParts和$params:"status = :status AND title LIKE :title",参数自动绑定 - 避免手写
"WHERE status = ? AND title LIKE ?"后按顺序传参——易错且难维护 - 空值或未设置条件应跳过,不生成对应 WHERE 片段
ORDER BY 和 LIMIT:单独校验 + 类型强转
排序方向(ASC/DESC)和分页参数虽属结构,但可通过有限枚举+类型约束保障安全:
该系统采用多层模式开发,这个网站主要展示女装的经营,更易于网站的扩展和后期的维护,同时也根据常用的SQL注入手段做出相应的防御以提高网站的安全性,本网站实现了购物车,产品订单管理,产品展示,等等,后台实现了动态权限的管理,客户管理,订单管理以及商品管理等等,前台页面设计精致,后台便于操作等。实现了无限子类的添加,实现了动态权限的管理,支持一下一个人做的辛苦
立即学习“PHP免费学习笔记(深入)”;
- 排序方向只接受
'ASC'或'DESC',用strtoupper()+ in_array 校验 - LIMIT 的 offset 和 count 必须是整数:
(int)$_GET['offset']和max(0, (int)$_GET['limit']),并设合理上限(如最大 100) - 拼接时直接嵌入已校验变量:
"ORDER BY id $direction LIMIT $offset, $limit"
完整示例:安全的动态查询函数
(简化版,不含异常处理)
function safeDynamicSelect($pdo, $table, $columns = ['*'], $where = [], $orderBy = 'id', $orderDir = 'ASC', $limit = 20, $offset = 0) {
// 表名白名单检查
$allowed_tables = ['users', 'posts', 'comments'];
if (!in_array($table, $allowed_tables)) {
throw new InvalidArgumentException('Invalid table name');
}
<pre class="brush:php;toolbar:false;">// 列名校验(支持 * 或具体字段)
$safe_columns = [];
foreach ($columns as $col) {
if ($col === '*') {
$safe_columns[] = '*';
} else {
$allowed_cols = ['id', 'title', 'content', 'status', 'created_at'];
if (in_array($col, $allowed_cols)) {
$safe_columns[] = "`$col`";
}
}
}
// 构建 SQL 主体
$sql = "SELECT " . implode(', ', $safe_columns) . " FROM `$table`";
// WHERE 绑定
$params = [];
if (!empty($where)) {
$whereParts = [];
foreach ($where as $key => $value) {
$whereParts[] = "`$key` = :$key";
$params[$key] = $value;
}
$sql .= ' WHERE ' . implode(' AND ', $whereParts);
}
// ORDER BY 校验与拼接
$allowed_order_cols = ['id', 'title', 'created_at'];
if (!in_array($orderBy, $allowed_order_cols)) {
$orderBy = 'id';
}
$orderDir = strtoupper($orderDir) === 'DESC' ? 'DESC' : 'ASC';
$sql .= " ORDER BY `$orderBy` $orderDir";
// LIMIT 校验
$limit = max(1, min(100, (int)$limit));
$offset = max(0, (int)$offset);
$sql .= " LIMIT $offset, $limit";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);}










