根本原因是用户输入的数据与SQL命令语法混同,数据库无法区分“值”和“指令”,导致单引号、OR、--、UNION等字符篡改查询逻辑;预编译使参数与SQL结构物理分离,仅作纯数据处理,从而阻断注入。

拼接SQL语句容易引发注入漏洞,根本原因是:用户输入的数据和SQL命令语法混在了一起,数据库无法区分“该输进去的值”和“该执行的指令”。只要输入里带单引号、OR、--、UNION这类字符,就可能让查询逻辑彻底跑偏。
为什么字符串拼接会让数据变成代码?
SQL语句本质是文本,数据库靠语法结构(比如引号界定字符串、分号结束语句、--表示注释)来解析执行。当你用username + "' AND password='" + password这种拼接方式构造查询时,程序只是把几个字符串连起来,不检查内容是否合法——攻击者输入admin' --,拼出来就是WHERE username='admin' -- ' AND password='xxx',后半段直接被注释掉。
- 数据库只认最终拼出的字符串,不管它来自变量还是硬编码
- 单引号、括号、分号、SQL关键字(如
UNION、SELECT)一旦出现在拼接结果里,就会触发语法解析,改变执行路径 - 哪怕输入看着像“普通文本”,比如
1' OR '1'='1,在SQL眼里就是有效表达式
数字型参数拼接也危险吗?
危险。很多人误以为id=5这种数字参数不会出问题,但只要没做类型强制或范围校验,攻击者仍可绕过。例如PHP中$sql = "SELECT * FROM users WHERE id = $id",传入id=5 UNION SELECT 1,2,3 FROM admin,拼出来就是完整可执行的联合查询。
- 未用
intval()、filter_var($id, FILTER_VALIDATE_INT)等过滤,$id可能是任意字符串 - 整数型参数若允许负数、超大值,也可能触发溢出或绕过逻辑(如
id=-1 OR 1=1) - 某些ORM或框架底层仍走拼接,表面看用了“安全方法”,实际没禁用原始SQL拼接入口
为什么预编译(参数化查询)能解决这个问题?
因为数据库在预编译阶段就固定了SQL结构,参数只作为纯数据传入,不会参与语法解析。比如用PreparedStatement(Java)、pg_query_params(PHP)、conn.execute("SELECT * FROM users WHERE id = %s", [user_id])(Python psycopg2),数据库会把%s占位符对应的实际值当作“值”处理,哪怕里面含' OR 1=1,也只会查一个叫这个字符串的ID,不会执行逻辑运算。
- 参数和SQL模板物理分离,数据库驱动层自动做转义和类型绑定
- 即使参数为空、含SQL关键字、含嵌套引号,也不会破坏语句结构
- 注意:只有真正用参数占位符才安全;用
format()或f-string拼接参数名/表名/字段名,依然高危
最容易被忽略的一点:表名、字段名、排序方向(ORDER BY后的name ASC)这些不能用参数化查询,必须白名单校验或正则严格匹配。否则,哪怕WHERE条件安全了,ORDER BY ${user_input}照样能注入。

















