SQL注入原理浅析
一、序言
我们通过一个实验来浅析SQL注入的原理,实验基于我编写的一个简单的书店系统,其已被我上传到了GitHub
https://github.com/pasiphae321/BookstoreSystem
预览链接
二、切换模式
在search.php中存在两种模式,一种使用PDO的prepare、bindParam方法进行防范SQL注入(下图圈一),另一种存在SQL注入漏洞(下图圈二)
我们切换到模式二
三、正式实验
1.登录系统
以任何一个用户登录系统,访问书籍搜索页面
2.进行正常搜索
由下面两图可以看到,正常搜索无论是否存在对应书籍都会有回显

3.探测是否存在SQL注入漏洞
BookName这种一看在mysql中的字段类型就是字符串,那么其在后端对应的SQL语句应该就是
1 | |
通过输入1',将SQL语句中的第一个'与1后面的'构成一对,那么其后的';就会造成SQL的语法错误,执行这个SQL语句MySQL就会报错,那么后端在得到报错后可能就不会设置回显或者设置不同的回显
由下图可以看到,因为MySQL的报错,后端返回了不同的回显,说明存在SQL注入漏洞
4.探测数据库类型
常见数据库有MySQL、PostgreSQL、SQLServer、oracle,只有MySQL有##注释
尝试输入' ##看数据库是否报错,由下图可以看到并未出现查询失败的回显,说明数据库并未报错,那么数据库就是MySQL
关于其它三个数据库的区分,可以查阅网络上的资料
5.探测select的字段数量
虽然前端的表格只有两个字段,但是后端的SQL语句有可能select了不止两个字段,那么我们通过union select进行探测
输入为什么' union select 1,2;##,可以看到回显有1和2,说明后端的SQL语句select了两个字段
输入为什么' union select 1,2,3;##,可以看到MySQL报错了,说明union前后的两个select的字段数量不一致,验证了后端的SQL语句确实select了两个字段
6.获取MySQL中的所有数据
MySQL中有一个名为information_schema的库,库中有个名为COLUMNS的表,表中存在三个字段TABLE_SCHEMA、TABLE_NAME、COLUMN_NAME,分别表示库名、表名、列名,可通过此表查询整个MySQL的信息
例如,查询整个MySQL的所有数据库名
1 | |
可以看到查到了所有数据库名
查询BookstoreSystem库的所有表名
1 | |
可以看到,查出了所有的表名
查询users表的所有字段名
1 | |
可以看到,查出了所有字段名
查询username和password字段的所有值
1 | |

通过如上方式,可获取整个数据库的数据
四、防范SQL注入的方法
1.参数化查询(parameterized query)
通过将SQL的代码和数据相分离,例如使用PHP的PDO的prepare方法和bindParam方法,本实验使用的书店系统就采用的此方式
2.使用ORM框架
ORM即Obejct Relationship Mapping对象关系映射,具体原理可查阅网络上的资料
3.在后端进行关键字过滤
后端过滤掉能够引起SQL注入的关键字如'/##/;/--/union/select等等,但这种方式会在一定程度上影响正常搜索