SQL预编译

本文章主要解决一个问题:

预编译真的能完美防御SQL注入吗???

sql注入是指攻击者拼接恶意SQL语句到接受外部参数的动态SQL查询中,程序本身未对插入的SQL语句进行过滤,导致SQL语句直接被服务端执行

对于如此危险的漏洞,有没有办法进行阻止呢?

有的兄弟,有的!

预编译就能解决大部分的SQL注入问题

何为预编译?

预编译全称Prepared Statement,就是在执行SQL前,把SQL语句告诉数据库服务器,编译好结构,然后再单独传参数进去执行

普通的拼接SQL:

1
2
3
username = input("请输入用户名:")
sql = "SELECT * FROM users WHERE username = '" + username + "'"
cursor.execute(sql)

这就好像直接把“用户输入”和“SQL语句”拼成一整句话。
用户只要输入了奇怪的东西,就能控制整个 SQL 的逻辑!

使用预编译:

1
2
3
username = input("请输入用户名:")
sql = "SELECT * FROM users WHERE username = ?"
cursor.execute(sql, (username,))

SQL 写的时候,用占位符(?)或命名参数(:name),
参数是后面传进去的!不是拼进去的!

预编译的执行流程
发送 SQL 模板给数据库服务器
比如:

1
SELECT * FROM users WHERE username = ?

这个时候数据库就把这个 SQL 的结构编译好了,生成了“执行计划”

服务器把这个语句存起来
存的是“只差参数”的 SQL 模板
客户端发送参数
比如:

1
("admin",)

数据库执行之前编译好的 SQL
把你传进去的参数当成“纯数据”,直接放进语句执行!
为什么这样能防止 SQL 注入?
因为参数永远只是值,不会被当作 SQL 代码执行!
哪怕用户输入的是:

1
' OR '1'='1

数据库也会当成一个完整的字符串 ' OR '1'='1 来处理,它不会让它改变 SQL 语句的逻辑结构

但是,重点来了,预编译真的能完美防御SQL注入吗?

预编译之外的注入

刚刚提到,预编译是将sql语句参数化,刚刚的例子中 where语句中的内容是被参数化的。这就是说,预编译仅仅只能防御住可参数化位置的sql注入。那么,对于不可参数化的位置,预编译将没有任何办法

不可参数化的位置:

表名、列名
order bygroup by
limit
join

我们以order by举例,现在有一个sql语句如下(以下为伪代码)

1
SELECT * FROM users ORDER BY {user_input};

其中user_input是传递过来的参数,例如 id

1
SELECT * FROM users ORDER BY id;

这个语句是正确的,但是如果user_input输入 id;drop table users --

1
SELECT * FROM users ORDER BY id;drop table users -- 

这样就被成功注入了,而这种位置是不可被参数化的,所以是无法通过预编译防御的

order by后一般是接字段名,而字段名是不能带引号的,比如 order by username;如果带上引号成了order by 'username',那username就是一个字符串不是字段名了,这就产生了语法错误。

所以order by后不能参数化的本质是:一方面预编译又只有自动加引号的setString()方法,没有不加引号的方法;而另一方面order by后接的字段名不能有引号

如何防御

对于sql注入存在两种情况,可参数化的,不可参数化的

对于可参数化没商量,直接预编译解决一切。

而对于不可参数化的,只能通过设置白名单,过滤特殊符号,通过加引号强制转为字符串等方式进行拦截

参考:

预编译真的能完美防御SQL注入吗?

SQL预编译——预编译真的能完美防御SQL注入吗

浅谈预编译之于SQL注入防御


猫猫🐱



© 2025 子非鲲 使用 Stellar 创建
共发表 44 篇 Blog · 总计 109.6k 字