Web安全——盾与矛
作为 Web 开发者,我们应当知道自己开发的系统有哪些风险漏洞可能会被攻击,这篇文章将讲述在 Web 中经常使用的攻击手段,以及相应的防御策略。
信息探测
第一步,我们需要踩点,搜集目标资料,找到哪些网站可能存在漏洞。
子域名收集
为什么要枚举子域名?
- 子域名枚举可以再测试范围内发现更多的域,这将增大漏洞发现的机会。
- 有些隐藏的、被忽略的子域上运行的应用程序可能帮助我们发现重大漏洞。
如何枚举?
- 利用搜索引擎,如 site:baidu.com ,但这样并不靠谱,可能前几页都是一个子域名。
- DNS信息收集,借助一些网站,如这是百度的子域收集结果:https://www.virustotal.com/gui/domain/baidu.com/relations
- 一些其他开源工具,如 https://github.com/shmilylty/OneForAll
更多工具详情,可参考 https://zhuanlan.zhihu.com/p/31334686
借助搜索搜索敏感信息
可以根据 Google 提供的语法进行信息查询。常用语法可以参考https://en.wikipedia.org/wiki/Google_hacking。
如搜索存在敏感信息的网站:
使用Nmap
探测主机信息
Nmap是一个开源的网络连接端扫描软件,用于扫描计算机开放的网络连接端,确定哪些服务运行在哪些连接端,推断计算机运行的操作系统。
- 扫描指定 IP 所开放的端口(这里指定了端口范围是1-65535)
nmap -sS -p 1-65535 -v 192.168.1.100
- 全面的系统检测,默认扫描主机1000个高危端口
nmap -v -A www.xxser.com
- 扫描指定端口
nmap -p 80,22,3306 www.xxser.com
- 穿透防火墙进行扫描(被禁用ping)
nmap -Pn -A www.xxser.com
更多指令参数查看 -help
使用Nmap脚本
nmap还提供了强大的脚本功能,是最好的功能之一,可以利用脚本快速探测服务器
- 扫描Web敏感目录
nmap -p 80 --script=http-enum.nse www.xxser.com
- 暴力破解mysql密码,只能欺负欺负弱密码
➜ nmap -p 3306 --script=mysql-brute 127.0.0.1
更多脚本请查看https://nmap.org/nsedoc/
常见漏洞
SQL注入漏洞
原理
在用户登录时,可能会这样写SQL:select * from user where username = '${name}' and password = '${passwd}';
。在一般情况下没有问题,但用户如果输入了这样一个“ ' or 1=1;--
”特殊的用户名会如何?最终执行的sql将会变成 select * from user where username = '' or 1=1;--' and password = '';
这条SQL会将所有的用户都查询出来,后面的password判断彻底失效,因为已经被注释掉了,如果在 加个 limit 1
,几乎可以登录任意一个用户的账号了,后果非常严重。
解决办法
检查数据类型和格式
如果sql语句是类似 where id = ${id}
,那么在执行前就应该确保 id 是数字类型。如果使用的 Java 这样的强类型语言,没抛异常就说明是没问题的了。
特殊字符串转义
除了检查类型,还应该检查入参中,是否包含一些特殊的字符,如系统的用户名如果不允许用 ‘-‘,就应该排除掉。对于这些特殊字符,如单引号、双引号、反斜杠、中横线,都应该根据业务考虑禁止,如果不可,那么只能对特殊字符串进行转义。
预编译语句
上面说的可能是比较low的方法,最佳方案是使用预编译语句来预防SQL注入。
使用预编译后注入的参数将不会再进行SQL编译。也就是说其后注入进来的参数系统将不会认为它会是一条SQL语句,而默认其是一个参数,参数中的or或者and 等就不是SQL语法保留字了。
数据库提供了预编译技术,Mybatis中打印SQL的话,会看见这种带问号的,如select * from users where name = ?
,? 就是填充符。
在Mysql中进行实验:
准备数据
1
2
3
4
5
6CREATE TABLE `user` (
`username` varchar(20) DEFAULT NULL,
`password` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into user values("a","a1");对sql进行预编译
1
prepare sqltpl from 'select * from user where username = ? and password = ?';
设置参数
1
set @para1='a',@para2='a1';
使用该参数来执行预编译语句
1
execute sqltpl using @para1, @para2;