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

写在前面

在之前面试的时候,我还是太菜了,有一道面试题还是值得研究研究的。

面试官问我,怎样完全避免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注入。那么,对于不可参数化的位置,预编译将没有任何办法。

那么不可参数化的位置都有哪些?

  1. 表名、列名

  2. order by、group by

  3. limit

  4. 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注入存在两种情况,可参数化的,不可参数化的。

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

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

这也是经过那次面试后新学到的一个知识点,总结来说还是自己太菜了,又学到了很多。

顺带推广一下个人的公众号:飞羽技术工坊,不定期分享一些计算机相关的技术知识

你可能感兴趣的:(sql,web安全,python,数据库)