SQL预编译
本文章主要解决一个问题:
预编译真的能完美防御SQL注入吗???
sql注入是指攻击者拼接恶意SQL语句到接受外部参数的动态SQL查询中,程序本身未对插入的SQL语句进行过滤,导致SQL语句直接被服务端执行
对于如此危险的漏洞,有没有办法进行阻止呢?
有的兄弟,有的!
预编译就能解决大部分的SQL注入问题
何为预编译?
预编译全称Prepared Statement,就是在执行SQL前,把SQL语句告诉数据库服务器,编译好结构,然后再单独传参数进去执行
普通的拼接SQL:
1 | username = input("请输入用户名:") |
这就好像直接把“用户输入”和“SQL语句”拼成一整句话。
用户只要输入了奇怪的东西,就能控制整个 SQL 的逻辑!
使用预编译:
1 | username = input("请输入用户名:") |
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 by
、group 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注入存在两种情况,可参数化的,不可参数化的
对于可参数化没商量,直接预编译解决一切。
而对于不可参数化的,只能通过设置白名单,过滤特殊符号,通过加引号强制转为字符串等方式进行拦截
参考: