预编译真正隔离SQL结构与用户数据,通过服务端固定语法树实现安全与性能双重保障:首次编译生成不可变执行计划,后续仅绑定参数,?处传入的恶意内容(如"1 OR 1=1")仅作字面量处理,彻底阻断SQL注入。

预编译真正隔离了SQL结构和用户数据
预编译不是“加了个转义函数”,而是让 MySQL 服务端在执行前就固定了语法树。比如 SELECT * FROM users WHERE id = ? 这条语句,MySQL 第一次收到时会走完整的词法分析 → 语法分析 → 语义分析流程,生成不可变的执行计划;后续所有调用都只往这个已编译结构里填数据,? 绑定的值永远不会参与解析——哪怕你传的是 "1 OR 1=1",它也只会被当做一个字符串字面量塞进 id 字段做等值匹配,不可能变成逻辑分支。
mysql2 的 execute() 才是真预编译,query() 不是
query() 看似支持 ? 占位符,但底层默认不触发服务端 PREPARE 流程,尤其在开启 multipleStatements: true 或配置宽松(如 local_infile: true)时,仍可能被绕过。而 execute() 强制走 PREPARE + EXECUTE 两步:第一次调用生成预编译语句 ID,之后复用该 ID 执行,参数纯绑定、零拼接。
- 连接池初始化必须显式设
namedPlaceholders: false(即使默认是false,也得写出来防误开) - SQL 字符串里只允许出现
?,禁止任何形式的模板字符串拼接,例如`SELECT * FROM ${table}`是硬性红线 - 事务内绝对禁用
query()处理用户输入,否则整个事务的隔离性失效
动态表名、列名、ORDER BY 字段仍是高危区
预编译机制天生不支持用 ? 替换标识符(identifier),MySQL 会把 ORDER BY ? 当成字符串排序,不是按字段值排序;SELECT * FROM ? 直接报 ER_PARSE_ERROR。这些地方必须靠白名单 + 正则双重校验:
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
- 先定义合法值集合:
const ALLOWED_TABLES = ['users', 'posts', 'comments'] - 再用正则兜底:
/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(input) - 最后用
connection.escapeId()包裹,例如:ORDER BY ${connection.escapeId(sortField)}
类型不匹配会让预编译形同虚设
mysql2 对参数类型有隐式转换,但某些边界情况会出问题。比如把字符串 "1,2,3" 直接传给 IN (?),它不会自动拆成三个参数,而是当成单个字符串查——结果为空,但攻击者可能借此探测表结构。正确做法是:
- IN 子句必须动态生成占位符:
WHERE id IN (${Array(n).fill('?').join(',')}) - 占位符数量由可信逻辑决定,绝不能基于用户输入长度计算
- 数值类字段优先用
parseInt()或Number()转型,避免"1 OR 1=1"被当字符串放过
预编译防不住的从来不是“怎么写 SQL”,而是“谁来决定 SQL 长什么样”。表名、排序字段、分组依据这些控制结构的部分,永远需要白名单或枚举,而不是依赖数据库层的参数绑定。

















