字符串拼接sql必然导致sql注入,因用户输入直接混入语句使数据库无法区分逻辑与数据,如输入' or '1'='1可查全表,'admin'; drop table users; --可删库。

为什么字符串拼接 SQL 一定会出问题
因为用户输入的内容会直接混进 SQL 语句里,数据库根本分不清哪部分是逻辑、哪部分是数据。比如用户传入 ' OR '1'='1,拼出来就是 SELECT * FROM users WHERE name = '' OR '1'='1'——整张表都查出来了。
更危险的是删库操作:admin'; DROP TABLE users; --,拼完直接执行,连分号都不拦。
- 所有用
+、format()、f-string拼WHERE、ORDER BY、IN子句的写法,都是高危 -
LIKE模糊查询时,用户输入的%和_会变成通配符,不是字面意思 - 动态表名、字段名不能用绑定变量,但也不能靠白名单以外的拼接——这是另一个层面的问题,先不展开
Python 中用 execute() 绑定变量的正确姿势
核心就一条:SQL 语句里所有外部输入,必须用 %s(MySQL/PostgreSQL)或 ?(SQLite)占位,参数单独传给 execute(),由驱动处理转义和类型适配。
错误示范:cursor.execute("SELECT * FROM users WHERE id = " + str(user_id))
正确写法:
cursor.execute("SELECT * FROM users WHERE id = %s AND status = %s", (user_id, "active"))
- 参数必须是元组或列表,单个值别漏逗号:
(value,),不是(value) - 不要在 SQL 里对绑定变量加引号:
"WHERE name = '%s'"是错的,%s会自动加引号(字符串)或不加(数字) -
executemany()同样适用,批量插入时能防注入还能提速
Java JDBC 的 PreparedStatement 怎么避坑
本质和 Python 一样,但 Java 更容易在细节上翻车:占位符索引从 1 开始、setString(1, input) 的括号里不能写变量名、null 值要用 setNull(2, Types.VARCHAR) 显式声明类型。
- 别用
Statement+executeQuery(sql)处理带用户输入的查询 -
PreparedStatement的 SQL 字符串在创建时就编译,所以不能把表名、列名塞进去——那是 SQL 注入的重灾区,得靠服务层校验白名单 - 调用
setXXX()方法前,确保参数非空;否则空指针异常会掩盖注入风险本身
Node.js 的 pg 和 mysql2 绑定语法差异
pg(PostgreSQL)用 $1、$2 占位,mysql2 用 ?,两者都不支持命名参数(如 :name)在底层驱动中真正隔离——除非你用 ORM 封装层,但那不是 SQL 层面的安全。
PostgreSQL 示例:
client.query("SELECT * FROM logs WHERE level = $1 AND ts > $2", ["ERROR", "2024-01-01"])
MySQL 示例:
connection.execute("SELECT * FROM orders WHERE user_id = ? AND status = ?", [123, "paid"])
-
pg的$1索引必须连续,不能跳着写$1和$3 -
mysql2不支持数组直接进IN子句,WHERE id IN (?)只能塞一个值;多个值得动态生成等长的?, ?, ?再展开数组 - 任何“自动拼
IN列表”的工具函数,如果底层没走绑定变量,就是假安全
防注入这事,关键不在语法多漂亮,而在每一条发给数据库的语句,是否严格区分了代码和数据。只要有一处漏掉绑定、回退到拼接,前面所有努力就归零。尤其注意日志查询、导出功能、管理后台的搜索框——这些地方最容易偷偷写回字符串拼接。










