SQLI预编译处理的学习

#0x01 什么是SQL的预编译

预编译是一种在同结构SQL语句多次发送时,可以提升效率的特性。创建一个sql语句模板发送到数据库,但是参数是未确定的,例如insert into test values(?,?)
利用客户端/服务器的二进制协议,将包含占位符的语句传给数据库。数据库接收后会解析,编译,在SQL语句模板上执行查询优化,并储存执行计划加入缓存,之后在执行的时候只需要发送数据就行。

#0x02 为什么采用预编译

从效率上来说,预编译语句减少了对sql语句的解析时间,查询的准备工作只进行一次,并且绑定参数传输可以使得服务器的带宽最小化,因为每次只需要发送参数,而不是整个查询语句。

从安全性上来说,SQL之所以能被注入,就是因为其非编译性语言的性质,一个合法的语句可以被我们动态的添加数据去进行修改。像比如C这种语言的话,在语义上就不会被欺骗,而SQL中的预编译也可以产生这样的作用。
在使用了预编译之后,从其他途径传输获取的参数值就不需要绞尽脑汁的做相应的转义过滤了,只要原始语句模板不存在被外部数据控制的可能,那么就不会发生SQL注入。

顺手查了查ORACLE数据库的SQL执行顺序:查缓存(语句hash结构相同),语法检查,语义检查(数据字段在不在),获取对象解析锁,权限核对,确定执行计划,语句执行,提取数据。

#0x03 什么时候不使用预编译

虽然预编译看起来很强大很好,然而除了预编译不支持的语法,在生产环境中还会有很多不会使用预编译的情况
从效率上来讲,如果是只执行很少次数的语句,在服务器资源消耗上来看比直接执行一个语句显得更加浪费。
还有如果有类似xxxxxxx order by ?这种如果进了预编译的话是不会起效的,所以也不会使用预编译。
关于order by 不管模拟预编译还是交给mysql去做预编译,order by后的字段都会变成字符,而字符是不会起效果的,但是这种就需要做好完善的转义或过滤了。

#0x04 模拟预编译的问题

在查询预编译的资料的时候,看到一句,MYSQL虽然支持预编译了,但是MYSQL语句默认下还是由PDO做预编译处理。
PDO会为不支持预编译的驱动程序准备好模拟预编译并且默认预编译模式就是模拟预编译。而使用模拟预处理的方式是在客户端本地执行预处理的模拟,最终将拼好的sql语句发送到mysql服务器进行执行(实际上就是完成了字符串拼接)
因此,可能会有另外一个问题产生:
当设置$dbh->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );的时候,如果在prepare部分语句就出现错误,那么默认的类似database()之类的数据会被注入出来(子查询不会)。
看了看文章好像很早以前还会有宽字节注入问题。。。

#0x05 SQL注入的防御思路

从数据库本身: 安全配置、策略

函数执行权限,文件读写权限,数据操作权限

预编译机制

提前固定语义绑定变量

从数据进出的角度:

存储部分:

数据过滤,不进脏数据,变量回归变量,执行语句代码关键字过滤

读取部分:

取后的数据过滤,不读脏数据,在使用数据部分进行变量限制或对关键字过滤,不影响应用其他语言对数据调用后的安全。


发表评论 暂无评论

*