公司项目重构-Web安全-注入攻击


如时间有限,可直接阅读防范手段一节。

1、背景介绍

今年开始公司准备对项目做部分重构,其中安全性问题首当其冲,除了已知的问题外,还需要发散思维,尽可能找出更多的潜在问题。期间发现一本非常好的书《白帽子讲Web安全》,感谢吴翰清大佬,读完本书让我对web安全有了更深刻的理解,强烈推荐大家阅读。

我打算记录下整个安全性重构的过程,相关知识点大部分会摘抄自《白帽子讲Web安全》,因为总结的很到位了,最后记录下公司内采用的防范手段(敏感信息不会书写)。

2、注入原理

注入攻击:把用户输入的数据当做代码执行了。有两个关键的条件:
1、用户能控制输入
2、原本程序要执行的代码,拼接了用户输入的数据
防范准则:将数据与代码分离,即在拼接输入的地方进行安全检查

下面一段端内容是已知的注入攻击的手段,了解其根本才能更好的防范。

比如有这么一个地址:http://www.test.com/area/info?city=NanJing
请求到后台,程序可能会这么拼接SQL:

select * from area where city = 'NanJing'

注意了,假如攻击者输入有语义的参数,如:?city=NanJing’; drop table user–
那么后台执行的SQL会变成:

select * from area where city = 'NanJing'; drop table user--'

输入很刁钻,–正好注释掉了最后的分号,让原本只是一条查询SQL额外执行了删库操作。
回过头来,和上面讲的两个关键条件呼应:用户能控制city的值,原本要执行的代码拼接了用户的输入数据。从而造成了严重后果。

盲注(Blind Injection)
攻击者一般会故意输入错误的数据(比如id=45’),让页面报错,如果程序开启了错误回写到页面,会为攻击者提供极大的便利,能够区分程序的语言。
如果程序关闭了错误回写(我理解的就是跳转统一错误页),攻击者就无法判断注入是否成功。但他们是很聪明的又研究出了“盲注”的技巧。
所谓盲注,就是攻击者找出能验证注入是否成功的方法,比如:

1、构造简单表达式看页面变化
还拿上面的url做示范,比如攻击者输入:?city=NanJing and 1=2

拼接后的SQL肯定不会返回任何数据的,页面会为空或者错误页,但此时并不能判断注入成功了,可能是后台对参数有判断或者引起了程序报错(比空指针)。
攻击者接着输入参数值:?city=NanJing and 1=1

此时如果页面正常了,那代表注入成功,city参数存在SQL注入漏洞了。
虽然程序关闭了错误回写,但攻击者通过简单的条件判断,对比返回的页面差异,就可以判断出SQL注入漏洞是否存在了。

2、Timing Attack (定时攻击)
大部分数据库语言都有测试函数性能的函数,MySQL中是BENCHMARk(count, expr),指定执行次数和测试的函数即可,比如:

mysql> SELECT BENCHMARK(10000000, encode('hello','goodbye'));
+------------------------------------------------+
| BENCHMARK(10000000, encode('hello','goodbye')) |
+------------------------------------------------+
|                                              0 |
+------------------------------------------------+
1 row in set, 65535 warnings (2.68 sec)

利用这个函数,使结果返回的时间比平时要长,通过时间长短的变化,可以判断注入语句是否执行成功,亲测效果如下:

mysql> use w***(脱敏)
Database changed
mysql> select if(substring(current,1,1) = CHAR(119), BENCHMARk(5000000,ENCODE('msg','by 5 sec')), null) from (select Database() as current) as tbl;
+-------------------------------------------------------------------------------------------+
| if(substring(current,1,1) = CHAR(119), BENCHMARk(5000000,ENCODE('msg','by 5 sec')), null) |
+-------------------------------------------------------------------------------------------+
|                                                                                         0 |
+-------------------------------------------------------------------------------------------+
1 row in set, 65535 warnings (1.03 sec) # if判断生效了

mysql> select if(substring(current,1,1) = CHAR(120), BENCHMARk(5000000,ENCODE('msg','by 5 sec')), null) from (select Database() as current) as tbl;
+-------------------------------------------------------------------------------------------+
| if(substring(current,1,1) = CHAR(120), BENCHMARk(5000000,ENCODE('msg','by 5 sec')), null) |
+-------------------------------------------------------------------------------------------+
|                                                                                      NULL |
+-------------------------------------------------------------------------------------------+
1 row in set (0.00 sec) # if判断没生效

切换到w***数据库,执行攻击者构建的SQL语句,char(119)就是w,这个sql的意思是如果当前数据库名称的第一个字母是w,就执行benchmark函数。如果我们把char(119)换成char(120),那么sql就很快执行完了。

攻击者遍历所有的字母,就可以获取数据库的名称,还有其他函数可以获得许多有用信息:
database() # 当前连接的数据库,比如w***
system_user() # 数据库的系统用户,比如root@localhost
current_user() # 当前登录的数据库用户
last_insert_id() # 最后插入的自增ID

更可怕的是,如果当前用户(current_user)具有写权限,那么攻击者可以将信息写入本地磁盘中,比如写入web目录中,攻击者就可能下载这些文件了。还可以写入一个webshell,通过into outfile方法,比如:

union all select table_name, table_type, engine from information_schmea.tables where table_schema = 'mysql' order by table_name desc
into outfile '/path/location/www/schema.txt' # 这里换行为了看的清楚点,注入时是一行的sql。

利用outfile甚至可以写程序脚本导出到文件,然后执行。比如select 程序语句块 into outfile 可运行的目录。

开始攻击
上面的方法找到了SQL注入漏洞,这仅仅是一个开始,还有很多事情要做,我们要利用这个漏洞,可以获取数据或执行命令:

  • 查询数据库版本:?id=5 and substring(@@version,1,4)=4
  • 判断指定的表是否存在或猜出字段的具体指:比如username和password,可以通过判断字符的范围,一步步缩小范围读取出来:?id=5 and ascii(substring((select concat(username,0x3a,password) from user limit 0,1),1,1)) > 64

当然了,上面的过程可能会非常繁琐,所有有专门的自动化工具来完成,比如sqlmap.py

如果当前数据库用户有读写系统文件或目录的权限,利用数据库提供的函数,先通过load_file()将系统文件读出,再通过into dumpfile将该文件写入系统中,然后通过load data infile将文件导入创建的表中,下面就可以利用上面的方式来读取数据了。

更多内容可自行阅读书籍,由于篇幅问题,就不全部列出了。

3、防范手段

注意,对用户输入做escape处理(过滤处理),不一定能解决问题,因为基于黑名单的方式可能会些问题:
比如可以不输入空格、用十六进制代替等;
还可能会误杀常见的输入,像having,select等。

1)、预编译语句,绑定变量(重要)

预编译的SQL语句不会发生语义的改变,不论输入任何数据,都当成字段值来查询。

java使用PrepareStatement
mybatis使用#而非$,全局搜索下使用$符的sql,如果业务需要,则必须要做好足够的入参校验。公司有一两处使用$符号的,如前台传入了类似"1,2,3"字符串,开发者为了省事使用in (${idsStr}),已让其修改先转成list然后用foreach拼接SQL。

2)、存储过程(除非公司允许)

其原理和预编译语句类似,提前将存储过程定义到数据库中,也会预编译的。

我公司是禁止使用存储过程的,如果公司允许,需要注意里面不要再动态拼接SQL,不然很有可能带来问题。

3)、合理使用数据类型(重要)

有些公司为了省事,所有数据库字段类型都有使用varchar,其实是很危险的,应该合理使用int、boolean、date等,用数据类型去校验输入合法性。

4)、使用编码函数

去官网寻找字符编码函数,在拼接时,对输入数据进行编码。
我公司有自己的编码函数。

5)、最小权限原则(重要)

  • web应用避免使用root账号连接数据库,且不应该具备自定义函数、操作文件、删除操作(表、库等)的权限;
  • 不同应用或场景,使用不同账号体系,并对权限做好区分,配置好可见表范围;
  • 牢记“数据与代码分离原则”,控制用户输入并在拼接的地方做安全检查。

暂时就写到这里,如有变化,会及时更新的。如有错误,请大家及时提出。
再次感谢吴翰清大佬!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值