QueryBuilder 是 TypeORM 中最直接的防 SQL 注入方式,其默认使用参数化查询,只要避免原始字符串拼接(如 .where("id = ${id}")),而采用 .where("id = :id", { id }) 或对象语法,并对动态字段名做白名单校验,即可有效防御注入。

用 queryBuilder 代替字符串拼接是最直接的防注入方式
手写 SQL 字符串(比如 `SELECT * FROM user WHERE id = ${id}`)是 TypeORM 中最常见的注入入口。TypeORM 的 queryBuilder 默认使用参数化查询,只要不调用 .addSelect() 或 .where() 时传入原始字符串,就基本安全。
实操建议:
- 始终用
.where({ id: userId })或.where("user.id = :id", { id: userId }),不要用.where(`user.id = ${userId}`) - 动态字段名(如排序字段)无法参数化,必须白名单校验:
if (!['name', 'createdAt'].includes(sortField)) throw new Error('Invalid sort field') -
.setParameter()只对命名参数(:id)生效,位置参数(?)在 TypeORM v8+ 已不推荐,容易出错
原生 SQL 查询必须用 getManager().query() + 参数占位符
当你不得不写原生 SQL(比如复杂窗口函数、JSON 操作),getConnection().query()(旧版)或 getManager().query()(新版)是唯一选择,但它本身不自动防注入——你得自己守规矩。
常见错误现象:传入数组或对象直接拼进 SQL,导致语法错误或绕过参数绑定。
实操建议:
- 只用
?占位符(MySQL/PostgreSQL 均支持),例如:manager.query("SELECT * FROM user WHERE status = ? AND age > ?", ["active", 18]) - 避免把用户输入塞进 SQL 字符串里:
`WHERE name LIKE '%${name}%'`是危险的,应改用WHERE name LIKE ?+`%${name}%`作为参数值 - PostgreSQL 的
$1,$2占位符 TypeORM 不支持,强行用会报错QueryFailedError: bind message supplies 0 parameters, but prepared statement "" requires 1
find* 方法天然免疫注入,但要注意 Raw 和 Like 的陷阱
userRepository.find({ where: { email } }) 这类方法底层走参数化,完全安全。但两个例外会悄悄打开后门:
使用约定式提交(Conventional Commits)生成标准化的 Git 提交信息。适用于以下场景: (1) 暂存更改时需要提交信息; (2) 审查 diff 时希望获得结构化摘要; (3) 编写 PR 描述或变更日志(changelog)条目; (4) 用户明确要求“为本次更改编写提交信息”。 该功能会分析已暂存/未暂存的 diff,并生成按类型(type)划分范围的提交信息。
实操建议:
-
Raw是纯 SQL 注入高危区:{ status: Raw(() => "CASE WHEN ...") }如果内部拼接了用户输入,就失效了。别用Raw处理动态值 -
Like本身安全,但通配符需转义:Like(`%${escapeLikePattern(search)}%`),其中escapeLikePattern要把%、_、替换成%、_、\,并设escape: '\' - 布尔字段过滤别用字符串:
where: { isActive: req.query.active === 'true' },而不是where: `is_active = ${req.query.active}`
自定义 Repository 中的 @Query 方法需要手动校验
如果你在自定义 Repository 里写了带 @Query 装饰器的方法(比如 NestJS 场景),TypeORM 不会自动解析参数,它只是把整个方法体当 SQL 字符串执行。
这意味着:哪怕你写了 @Query() sql: string,只要最终拼进了查询,就等于裸奔。
实操建议:
- 禁用用户直接控制
@Query方法的 SQL 内容;如果必须动态,拆成固定模板 + 白名单字段 + 参数化值 - 例如搜索接口,只允许
field在['title', 'content']中选,value作为:value参数传入,SQL 固定为WHERE ${field} LIKE :value - 日志里打印出最终 SQL 时,检查是否含未绑定的
${}或+拼接痕迹——这是最简单的自查手段
真正麻烦的不是“会不会用参数化”,而是那些看似无害的字符串插值场景:模板标签、日志拼接、动态表名、JSONB 路径。这些地方 TypeORM 不管,得靠代码审查和运行时断言兜底。

















