刚学web安全的时候学到sql注入防御,那些文章基本上都会说利用pdo预编译就可以近乎完美防御sql注入,或者看到一些渗透经验贴,遇到sql经过预编译的网站师傅们总是会建议赶紧换个站,那么预编译究竟能不能完美防御sql注入,或者说预编译下的sql注入有什么奇技淫巧吗?
首先是第一个问题,为什么预编译或者说参数化查询可以防止sql注入呢?我之前看过的一个面经上是这么写的:
使用参数化查询数据库服务器不会把参数的内容当作 sql 指令的一部分来执行,是在数据库完成 sql 指令的编译后才套用参数运行。简单的说: 参数化能防注入的原因在于,语句是语句,参数是参数,参数的值并不是语句的一部分,数据库只按语句的语义跑 。
回顾一下sql注入发生的原因,sql注入之所以会产生是因为服务器错误把用户的输入当作了执行的语句。假设这里有一个sql语句:
select username from test where id = $_POST[id]
如果用户正常输入1,语句则为:
select username from test where id = 1
那么显然查询出来的就只会是test表中id为1的那个username,然而如果用户输入的是1 union select version(),那么语句就变为了:
select username from test where id = 1 union select version()
最后查询出来的就会使id=1的那个username以及数据库的版本,这是因为本来理论上查询的应该是id为”1 union select version()”的这个用户,而数据库执行语句的时候把它分开了,视作了查询select username from test where id = 1以及select version()。
回看到预编译的原理,如果源码这里提前对$_POST[id]进行了处理,那么数据库相当于会提前对整个语句进行编译,把它编译成select username from test where id = 用户输入,因此整个语句的功能已经提前定死了,就是查询id = 用户输入的username,不再会像之前一样错误理解成查询id=1的用户然后再查询版本,在我看来预编译的作用,就是消除了sql语句的歧义。
那么回看最初我们提出的疑问,预编译真的能完美防御sql注入吗?有没有什么奇技淫巧能绕过预编译进行注入呢?
有次刷微信看到一篇文章:预编译真的能完美防御SQL注入吗?
这里面提到一个很有趣的点——预编译是将sql语句参数化,刚刚的例子中 where语句中的内容是被参数化的。这就是说,预编译仅仅只能防御住可参数化位置的sql注入。那么,对于不可参数化的位置,预编译将没有任何办法。
那么哪些是不可参数化的位置呢,原作者说:

为了研究原理,我找到了一篇文章,这个应该是最早提出order by后没法参数化所以可以被sql注入的(其他文章都是相互抄,我们简中是这样的)—— SQL预编译中order by后为什么不能参数化原因,文章里是这么解释的

大概就是说,order by后面的字段是不能加引号的,而预编译后会自动加上引号,因为这个矛盾所以order by的后面不能进行预编译。不过当时他解释原因是因为自动加引号的setString()方法,而这个方法似乎只是java下存在的,而这篇文章我准备从原理出发研究研究php下的注入可能(其实这种思路不同语言是共通的)
真预编译与假预编译
回到最初的问题——预编译真的能完美防御sql注入吗?有没有什么奇技淫巧能绕过预编译进行注入呢?
首先,我们开启数据库的日志功能,从数据库的角度看看预编译究竟对我们的sql语句做了什么处理。
show variables like 'general%';
</

529

被折叠的 条评论
为什么被折叠?



