防止SQL注入(Preventing SQL Injections)
写在前面
刚看了篇文章,标题是《Preventing SQL Injections》,主要讲解了PostgreSQL如何防止SQL注入。
阅毕后,不免勾起了我往日对SQL注入的认识。再次估量,发现自己往日认识只知其然,而不知其所以然。
随后,花费个把小时进行SQL注入的学习后,对此有了进一步理解。
What&&Why
SQL 注入的表现:
以下网上相关资料以及相关大神的讲解
-
https://www.jianshu.com/p/078df7a35671
-
https://zh.wikipedia.org/wiki/SQL%E8%B3%87%E6%96%99%E9%9A%B1%E7%A2%BC%E6%94%BB%E6%93%8A
HOW
知乎上一个问题,名为“如何从根本上防止SQL注入”。针对此问题,提问者和回答者都对SQL注入的防治进行了探讨,探讨程度深入到了“设计层面”,个人认为很精彩。
- https://www.zhihu.com/question/22953267/answer/80141632
个人较认可这篇回答:
作者:槐磊
链接:https://www.zhihu.com/question/22953267/answer/23190015
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
第一,参数化查询是DB本身提供的功能
第二,各种语言的库在实现参数化查询时可以采用以下两种策略,姑且称之为
- 真•参数化查询:将带参数的语句及参数分别发送给DB,这种情况能100%防注入,因为对于DB来说,参数的值绝对不会作为语义要素来解析,据我所知JDBC是这种实现方式
- 伪•参数化查询:将拼接以后的plain SQL发给DB,只不过在拼接过程中会进行校验及转义,据我所知PHP的PDO默认是采用这种实现,不过可以通过设置开关切换为真•参数化查询,个人目前无法理解为什么要放着DB本身提供的正确实现而兜个大圈子,而且这样并不能完全避免风险,因为不能保证目前的校验及转义是否还有没考虑到的情况
从两位的回答来看貌似是认为DB没有提供参数化查询机制,语言层面的参数化查询都是通过伪•参数化查询来实现的 第三,回答题主的问题,答案是,不能,DB已经把能做的都做到了,一方面,DB已经提供了两种查询方式分别能正确地适用不同场景,但是DB不可能阻止程序层面错误地使用,首先,plain SQL有存在的必要,但是你无法识别程序送过来的plain SQL到底是写死的还是通过拼接来的 结论,防止SQL注入的责任仍然是在程序这边,应该正确地使用DB提供的机制.
其实这就是我所想表达的重点,“数据库提供的功能”这种说法可能会引入歧义,在此重新定义一下,所谓的“数据库提供”指的是,数据库服务端(严格来说是具体到SQL分析引擎)接收到的就是“带参数的SQL语句+参数值”二者分离,与之相对的“非数据库提供”指的是,数据库服务端接收到的是一条单独的SQL语句,其中的参数已经被处理成了具体的值,这个过程是由客户端(可能是最终程序,也可能是某些框架,或者数据库驱动)完成的,这二者是有本质区别的,在第一种实现中,SQL语法引擎明确地知道参数的位置是一个整体,不会再对其进行语法及语义处理,而第二种则不然,参数值仍然会被进行语法及语义处理,客户端所做的就是通过验证、转义等方式尽量地避免参数被解析成语义动作,不同于第一种方式,没有从根本上解决用户的输入可能被作为指令这一隐患
从题主的评论来看题主应该是清楚这个机制的,那现在我们来再次回答题主的问题,设想一下为了达到这个目标我所能想到的数据库这边可以做哪些尝试(欢迎补充)
\1. 废弃plain SQL ,强制使用参数化查询(并且是上述的真•参数化查询) 这其实没有解决任何问题,首先,使用plain SQL的需求是真实存在的,当然我们假设可以通过空的参数列表来变相进行plain SQL的查询,那这和plain SQL又有何区别,其次,参数化查询仍然是由“带参数的SQL”和参数列表两部分组成的,无法识别和避免程序员在第一部分采用拼接
\2. 完全抛弃SQL,提供结构化的接口,例如类似select(tableName, fields, condition)这种机制 这会使得接口异常复杂,且不一定应付得了复杂查询,想想LINQ吧
所以结论仍然是,数据库本身在这个问题上已经做到足够好了,前提是使用者正确地使用其提供的功能