prisma.$queryRaw拼接字符串等于开后门,因其插值本质是JS字符串拼接而非参数化,用户输入含' OR 1=1 --等即可篡改逻辑;唯一安全路径是$queryRaw配合Prisma.sql实现真正的预编译参数化。

prisma.$queryRaw 为什么拼接字符串就等于开后门?
因为 prisma.$queryRaw 接收的是模板字符串,但它的插值行为不是“参数化”,而是 JavaScript 字符串拼接——${req.query.id} 在运行时直接转成字符串插入 SQL,和 "SELECT * FROM users WHERE id = " + req.query.id 本质相同。只要用户输入含单引号、分号或注释符(如 ' OR 1=1 --),查询逻辑就可能被篡改。
真正安全的写法是用 Prisma.sql 构造参数化片段:
await prisma.$queryRaw`SELECT * FROM users WHERE email = ${email}`注意:只有用 Prisma.sql 包裹的变量才会被识别为参数;裸写 ${email} 不行,必须确保 email 是由 Prisma.sql 显式传入的值。
哪些地方容易误以为“用了 Prisma 就安全”?
常见错觉场景包括:
-
prisma.$queryRaw`SELECT * FROM ${tableName} WHERE id = ${id}`—— 表名不能参数化,此处${tableName}是拼接,必须白名单校验 -
prisma.$queryRaw(`SELECT * FROM users WHERE status = '${status}'`)—— 用反引号包裹但没用模板插值,仍是字符串拼接 - 把用户输入先做
parseInt()或trim()就认为“干净了”——类型转换不等于注入防护,parseInt("1; DROP TABLE users--")仍得 1,但后续若用于拼接就危险
动态表名/字段名怎么处理才不踩坑?
SQL 参数化只支持值(WHERE 条件、INSERT 值列表等),不支持标识符(表名、列名、ORDER BY 字段)。所以:
使用约定式提交(Conventional Commits)生成标准化的 Git 提交信息。适用于以下场景: (1) 暂存更改时需要提交信息; (2) 审查 diff 时希望获得结构化摘要; (3) 编写 PR 描述或变更日志(changelog)条目; (4) 用户明确要求“为本次更改编写提交信息”。 该功能会分析已暂存/未暂存的 diff,并生成按类型(type)划分范围的提交信息。
- 硬编码最安全:比如固定查
posts表,就别抽象成变量 - 必须动态时,只允许从预设白名单中选取:
const validTables = ["posts", "users", "comments"]; if (!validTables.includes(tableName)) throw new Error("Invalid table"); - 字段名同理,尤其警惕
ORDER BY ${sortField}—— 即使加了ASC/DESC,排序字段本身也得白名单控制
别试图用正则过滤(如 /^[a-zA-Z_][a-zA-Z0-9_]*$/),它无法阻止 users; DROP TABLE admins-- 这类绕过,且忽略关键字冲突风险。
还有哪些 Prisma 查询接口会失效参数化?
以下接口一旦混入用户输入拼接,就等于放弃防护:
-
prisma.$executeRaw`...`—— 和$queryRaw同理,同样依赖Prisma.sql -
prisma.$queryRawUnsafe("SELECT ... " + userInput)—— 函数名里就写了Unsafe,别用 - 任何手动拼接
String.raw、format模板或replace()生成的 SQL 字符串
真正能放心用的,只有两类:一是 Prisma Client 的声明式方法(findMany、update 等),二是 $queryRaw + Prisma.sql 组合。其余全是灰色地带。
实际项目里,最常被忽略的是 ORDER BY 和 GROUP BY 后的字段名校验——它们看起来不像“值”,但攻击者完全可以利用它执行盲注或拖库。

















