写在前面
在之前面试的时候,我还是太菜了,有一道面试题还是值得研究研究的。
面试官问我,怎样完全避免sql注入?我想起了刚毕业时校招有一次也被问过一样的问题,当时我说的是:“基本上预编译就能解决了。”当时并没有对我提出质疑,所以我就一直以为预编译是无敌的,可以绝对防御sql注入。所以这次被问到,我依旧回答了预编译,不过这次面试官没有放我一马,开始对我进行追问:“有没有什么情况是预编译无法解决的?”我一听就知道完蛋了,自己才学疏浅终于还是掉坑里了,然后才意识到自己根本没有去深入研究过预编译。
所以这次就想记录一下。
预编译
预编译即将sql语句参数化,举个例子,现在有一个sql语句
select username, passwd from user where id = ?
id是我们要传入的参数,那么有sql注入的写法就是直接进行语句拼接
sql = "select username, passwd from user where id = " + num
预编译的写法是这样的(python代码)
sql = "select username, passwd from user where id = ?"
cur.execute(sql, (2))
可以看出,通过提前写好一个sql语句,将需要传入的参数值用符号进行占位,随后预编译,传参执行,这样就允许后续输入进来的内容仅仅是参数值,并不参与到sql语句的构成中。
由此可以看出,预编译是一个防御sql注入的绝佳手段,但真的能绝对防御吗?
预编译之外的注入
刚刚提到,预编译是将sql语句参数化,刚刚的例子中 where语句中的内容是被参数化的。这就是说,预编译仅仅只能防御住可参数化位置的sql注入。那么,对于不可参数化的位置,预编译将没有任何办法。
那么不可参数化的位置都有哪些?
-
表名、列名
-
order by、group by
-
limit
-
join
-
等
我们以order by举例,现在有一个sql语句如下(以下均为伪代码)
SELECT * FROM users ORDER BY {user_input};
其中user_input是传递过来的参数,例如 id
SELECT * FROM users ORDER BY id;
这个语句是没有问题的,但是如果user_input输入为 id;drop table users –
SELECT * FROM users ORDER BY id;drop table users --
这样就被成功注入了,而这种位置是不可被参数化的,所以是无法通过预编译防御的。
如何防御
所以,对于sql注入存在两种情况,可参数化的,不可参数化的。
对于可参数化没商量,直接预编译解决一切。
而对于不可参数化的,只能通过设置白名单,过滤特殊符号,通过加引号强制转为字符串等方式进行拦截。
这也是经过那次面试后新学到的一个知识点,总结来说还是自己太菜了,又学到了很多。
顺带推广一下个人的公众号:飞羽技术工坊,不定期分享一些计算机相关的技术知识