参数绑定是Doctrine查询的强制安全基线,所有用户输入必须通过setParameter()处理,DQL、QueryBuilder和原生SQL均需严格参数化,字段名等无法参数化部分须用白名单校验,并配套禁用PDO模拟预处理、最小权限账号等措施。

参数绑定不是可选项,而是 Doctrine 查询的强制安全基线。只要 SQL 中出现任何用户输入,就必须走 setParameter(),拼字符串等于主动放行 SQL 注入。
所有查询路径都必须参数化
DQL、QueryBuilder、原生 SQL 三类写法,各自有对应的安全调用方式,不能混用或跳过:
- DQL 中用
:name或?1占位,再统一调setParameter('name', $value) - QueryBuilder 写
$qb->where('u.email = :email'),后续链式传参->setParameter('email', $input) - 原生 SQL 必须用
executeQuery($sql)->setParameters($params)或executeStatement($sql, $params);绝不可写"WHERE id = " . $_GET['id']
别名与参数名要唯一且清晰
同一个 QueryBuilder 实例中,重复使用相同参数名会导致后一次覆盖前一次,查不到数据还难排查:
- 别名建议用单字母,如
createQueryBuilder('u')、leftJoin('u.posts', 'p'),简洁不易冲突 - 参数名避免通用词如
:value,改用语义化名称,如:minPrice、:status - 批量设参可用
setParameters(['status' => 'active', 'role' => 'admin']),比多次调用更可控
无法参数化的部分必须白名单兜底
字段名、表名、排序方向、IN 列表这些语法要素,数据库本身不支持占位符,只能靠校验补位:
- 字段名限制在预定义数组内:
$allowed = ['name', 'email', 'createdAt']; in_array($field, $allowed, true) - 排序方向转小写判断:
$dir = strtolower($request->query->get('dir', 'asc')) === 'desc' ? 'DESC' : 'ASC' - IN 列表先整型过滤:
$ids = array_map('intval', $ids); $qb->setParameter('ids', $ids, Connection::PARAM_INT_ARRAY)
配套措施不能少
参数绑定是核心防线,但单靠它不够。还需同步落实几项基础配置:
- 禁用 PDO 模拟预处理:
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false),确认驱动真实支持 - 数据库账号遵循最小权限原则,禁用
DROP、CREATE、LOAD_FILE等高危权限 - 路由参数加正则约束,如
{id},从入口就过滤非法字符 - 生产环境关闭详细错误提示,避免泄露表结构或字段名


















