近来几年,很多大型网站频发安全事件,比如2011年众所周知的CSDN密码泄露事件,2014年eBay也因受到攻击造成用户密码和个人数据泄露,Web安全逐渐进入人们的视野,安全测试也逐渐成为了软件测试中非常重要的一部分。
提到安全测试,很多人应该都会想到ZAP,ZAP(Zed Attack Proxy)是OWASP提供的一款免费Web安全漏洞扫描工具,用户可以通过设置浏览器和ZAP的Proxy,在开发过程或测试过程中自动检测Web应用程序是否存在安全漏洞,ZAP还会提供扫描结果的风险等级,修复建议以及一些参考文档,下图就是ZAP扫描后的一个用户界面。
除了自动扫描功能,ZAP也支持手动安全测试,通过在数据发送到服务器之前手动修改请求信息来测试Web应用程序是否存在安全漏洞。
很多人会有这样的疑惑,ZAP能否扫描出所有的安全漏洞?ZAP扫描出的安全漏洞和安全等级是否可靠?用了ZAP,软件是不是就安全了?
ZAP局限性
首先虽然ZAP的自动扫描功能非常强大,但对于OWASP Top 10中的某些项或者Top 10以外的一些安全漏洞,想要通过ZAP扫描检测出来是非常困难的,比如Top 10中的A5 “Security Misconfiguration” 就很难通过扫描检测出来,所以ZAP所能扫描到的安全漏洞只是OWASP Top 10的一个子集。
其次,ZAP扫描后的安全报告,还是需要结合实际项目进行分析才能确定其有效性和安全等级,比如我们在项目中曾经用ZAP扫描出了 “Cookie set without HttpOnly flag” 的安全隐患,推荐的解决方案是将所有的Cookie都设置成HttpOnly,但现实的情况是项目中前端AJAX需要携带这个Cookie来给后端发送请求,如果设置了这个flag,那么我们正常的请求也会失败,所以这个漏洞对我们来说就是无效的,或者说我们是不应该修复的。
另外因为Web应用程序往往比较复杂,会有很多组成部分,比如前端、服务器端、数据库等,各层分别使用了不同的框架、语言,而且经常会引入一些第三方的库、框架或者模块,每一个环节都有可能存在安全隐患,所以仅仅依赖ZAP是不够的,比如针对第三方组件的安全测试,就可以借助OWASP提供的另外一款工具Dependency Check。
通过分析实际项目中发现的安全问题,我们发现缺陷大体上如下分布:
安全问题可以归为两大类:
- 一类是比较有共性的,即可以抛开业务上下文,软件之间共通的一些问题,常见的比较严重的安全隐患,如XSS攻击,CSRF攻击等,ZAP可以帮我们扫描出大多数的问题。
- 另一类是针对业务需求的,比如非授权的账户是否不能访问/修改他们没有权限的信息等等,对于这一类问题,离开具体的业务上下文,是很难测试的,因为什么样的用户具有什么样的权限往往是业务领域的知识,换句话说,这一类问题的测试重点是看正常的用户能否按照业务需求所期望地正常使用系统,怎么区分evil user并阻止其对系统的使用和破坏,需要很强的业务背景。
举一个简单的例子,比如一个Web系统有两种角色,管理员和普通账户,业务需求是管理员可以修改所有人的所有信息,普通账户只能看到和修改自己的信息,如果普通账户张三可以通过一些非正常手段修改李四的信息,或者非系统的用户(evil user)通过某些方式可以看到系统内账户的个人信息,这些都是严重的安全缺陷,而且这一类的缺陷所占比率比较大,但是都没有办法通过ZAP扫描出来,也没有办法脱离对业务知识的了解来进行测试。
安全内建
ZAP扫描,针对业务上下文的用户权限测试(不能局限于界面,还要通过其他一些方式比如修改请求)以及evil user的用户场景测试,可以覆盖绝大多数的Web安全缺陷,但是正如我们没有办法将质量注入一个已经成型的产品一样,安全也是同样的道理。
如果我们在软件已经编码完成之后再引入安全检查和测试,那么软件的安全质量已经确定,后期的修复只能解决已经发现的安全漏洞,不能让软件更加安全,而且对于这些安全缺陷,发现得越晚,修复的成本就会越高。
所以为了开发安全质量较高的产品,除了选择好的工具以及增加业务背景下的安全测试,还非常有必要将安全检查和测试提前,在软件开发各个过程中引入安全实践。
微软提出的SDL(Security Development Lifecycle)就是这样的理念,SDL提出了很多软件开发过程中非常好的的安全活动,比如需求分析阶段的“最小权限原则”,开发阶段“弃用不安全的函数”,以及测试阶段可以使用“模糊测试”等等,其核心理念就是将软件安全的考虑集成在软件开发的每一个阶段,从需求,设计,编码,测试,到最后的发布整个过程中,下图是一个简化版的SDL流程图,完整的SDL还包括前期的培训,发布后的响应等:
ThoughtWorks提出的BSI(Build Security In),即安全内建,强调的也是安全提前的理念,结合敏捷软件开发实践,将安全融入到用户故事的生命周期中,引入安全验收条件, 不同角色(业务人员、开发人员,测试人员以及客户)在用户故事的每个阶段对安全验收条件进行沟通,检查,验证,确保在用户故事交付之前满足了定义的安全验收条件,下图就是用户故事的生命周期图以及在各个阶段哪些角色会参与哪些安全活动的一个简单介绍:
在保证用户故事满足了安全需求的基础上,基于迭代/发布还会有一些整体的功能级别的验证,做到真正的安全内建。
以上两种理念强调的都是把安全作为跟功能一样重要的考虑因素,在软件开发的各个阶段进行沟通和反复验证,很多实践经验也表明,将安全活动作为软件开发过程的一部分来执行,其安全效益要大于临时的或者零散的安全活动。
结论
所以从安全测试的选择来看,我们不能单一依赖某种工具,比如ZAP扫描,而应该多加入一些基于业务需求的安全测试,多从攻击者的角度思考问题,更重要的是,可靠的安全测试只能避免安全漏洞造成更大的危害和影响,并不能打造安全的产品,真正的安全产品必定需要整个软件开发过程中每个环节的保障,需要从业务分析阶段就将安全作为重要的考虑因素,将安全实践融入到整个软件开发过程中,所有角色共同参与,这样才能做到真正的安全内建。