pg-promise默认防sql注入因其所有查询方法强制使用postgresql参数化协议,分离sql文本与参数;但拼接用户输入、误用模板字符串、未校验动态表名/字段名(须用${table:name}等格式化器)会绕过防护。

pg-promise 为什么默认就防 SQL 注入
因为 pg-promise 所有查询方法(如 query、one、many)底层强制走 PostgreSQL 的参数化协议——即把 SQL 文本和参数分开传给服务器,由 PostgreSQL 自己做类型绑定和转义。只要你不手动拼接字符串,它天然免疫绝大多数注入场景。
哪些写法会绕过防护、导致注入
以下操作会让 pg-promise 失去保护能力:
- 用
+或模板字符串拼接用户输入到 SQL 字符串里,例如:`SELECT * FROM users WHERE name = '${req.query.name}'` - 把用户输入直接塞进表名、列名、ORDER BY 子句等无法参数化的位置,且没用
:name或:csv格式化器 - 误用
raw类型或as.format()时传入未过滤的原始值
动态表名/列名必须用命名空间格式化器
PostgreSQL 不允许用 占位符代替表名或列名,所以必须显式声明可信任范围:
- 安全写法:用
${table:name}和${column:name},它们只接受字母、数字、下划线,并自动加双引号 - 错误写法:
SELECT * FROM ${req.body.table}—— 这等于裸奔 - 带条件的列名示例:
UPDATE ${table:name} SET ${column:name} = ${value:json} WHERE id = ${id}
注意::name 格式化器不校验是否存在该表/列,只做字符白名单 + 引号包裹;真正合法性仍需业务层控制(比如查白名单配置)。
批量操作和 NULL/undefined 的处理细节
pg-promise 对 null 和 undefined 默认都转成 SQL 的 NULL,这通常符合预期,但要注意:
- 如果字段不允许为 NULL,而你传了
undefined,会触发数据库约束错误 - 批量插入用
:csv(如INSERT INTO t(a,b) VALUES ${values:csv})时,数组元素里不能混入undefined,否则生成的 SQL 语法非法 - 事务内多语句失败时,未 catch 的 Promise rejection 会导致整个事务回滚,但错误堆栈可能不包含具体哪条语句出错——建议每步都加
try/catch或用task/tx的catch钩子捕获上下文
最易被忽略的一点:格式化器(如 :name、:csv)不是“魔法”,它只做字符串替换,不参与 PostgreSQL 的执行计划预编译。所以表名/列名变更仍需靠应用逻辑兜底,不能指望库自动拦截非法标识符。











