洞见101之软件安全测试
介绍
在黑客攻击软件系统出现之前,软件行业没有安全测试这个概念。但是在兴趣和金钱等利益的驱使下,一部分人开始通过各种技术手段对软件进行攻击,从而获得非法信息,并从中获利,然后这部分人被称之为黑帽(注:黑帽用来比喻进行网络入侵、使用计算机病毒等阴谋诡计的恶意攻击者,他可能是拥有黑客水平的一些专业人士,也可以是只会用一些攻击工具和脚本的一些孩子。而黑客这个词本身是指十分热心钻研计算机技术,水平高超,在一般的程序员眼里差不多是无所不能的资深专家,所以这里不用黑客来描述坏人)。
为了阻止黑帽者对软件系统的攻击,反制措施一般有两种。第一种就是在软件系统前面加设一层护盾,对于WEB服务器可以是WAF(Web Application Firewall)或者入侵检测系统,对于本地应用可以是病毒扫描软件或者加固(加壳)。第二种方法就是对软件系统做安全测试,从而减少软件系统的漏洞,减少被攻击的可能,增加攻击的困难,从而减少软件系统由于安全漏洞而带来的损失。业界把这种专业的负责的安全测试者成为白帽,与黑帽针锋相对。
虽然白帽和黑帽做着互相针对的工作,不过他们使用的技术,技巧和方法都是相似的,很多时候甚至是相同的,所以软件安全测试也可以理解为白帽对系统进行的无害的安全攻击。
软件安全测试的目标就是通过安全攻击验证被测软件系统是不是满足其安全需求,而安全需求需要在软件设计的过程中或者开发之前定义好,让开发人员能将安全需求内建到开发过程中,尽可能防止不满足安全需求的安全漏洞被产生出来。当软件开发完进入测试阶段的时候,这些安全需求就是安全测试中的最为重要的攻击面和攻击向量。下面是一些安全测试需求的例子:
- 所有用户都必须通过认证授权后才获取“机密数据”
- 互联网用户不能直接搜索数据库里面的数据
- 所有用户存储到数据库中的数据必须经过安全验证
攻击面(Attack Surface)和攻击向量(Attack Vector)是软件安全领域的两个专业术语。攻击面是指被攻击软件系统中的某一个或多个攻击点,本质上就是被攻击系统中可以执行并进而被攻击的代码,比如邮件服务器的协议解析器和客户端程序的处理代码等。当找到可以攻击的攻击面以后,还需要确定攻击向量,而攻击向量就是执行攻击的方法,比如发送一封电子邮件或者发送一个HTTP POST等。CAPEC(Common Attack Pattern Enumeration and Classification)就是一个总结了很多软件系统的攻击面和攻击向量的模型库。
首先安全测试首先需要找到并确定攻击面以及攻击向量(注:下一章会介绍如何通过威胁建模找到并确定被测系统的攻击面和攻击向量)。在确定攻击面和攻击向量之后,就可以通过各种安全攻击的方法,工具以及实践对被测系统进行测试。当完成安全测试之后,还需要对其结果进行度量,确定安全测试的有效性。度量的指标包括以下关键项:
- 发现的安全漏洞的数量
这个是最为重要的安全测试指标之一,它的数量在一定程度上决定了安全测试的有效性。理论上安全漏洞发现得越多,证明安全测试有效性越高。但是反过来安全测试漏洞少,却不能说明安全测试有效性不好。所以这个指标是一个双刃剑,可以在安全漏洞多的情况下判断安全测试的有效性。但是在安全漏洞少的情况下,虽然不能使用这个指标来判断安全测试的有效性,但是在配合其他指标的情况下,可以度量被测系统的安全性。
- 发现的安全漏洞的危险级别
这个是另外一个重要的安全测试指标,其中安全漏洞的危险级别越高,可以度量安全测试的有效性越高。安全漏洞的危险级别可以由公司内部的安全专家来制定,也可以根据一些安全社区或者咨询公司定义的级别来确定。比如《OWASP Top 10》以及《CWE TOP 25》。
等级 | 名字 |
---|---|
1 | 注入 |
2 | 失效的身份认证 |
3 | 敏感信息泄漏 |
4 | XML外部实体(XXE) |
5 | 失效的访问控制 |
… |
《OWASP Top 10》 2017
等级 | 评分 | 名字 |
---|---|---|
1 | 93.8 | Improper Neutralization of Special Elements used in an SQL Command (‘SQL Injection’) |
2 | 83.3 | Improper Neutralization of Special Elements used in an OS Command (‘OS Command Injection’) |
3 | 79.0 | Buffer Copy without Checking Size of Input (‘Classic Buffer Overflow’) |
4 | 77.7 | Improper Neutralization of Input During Web Page Generation (‘Cross-site Scripting’) |
5 | 76.9 | Missing Authentication for Critical Function |
… |
《CWE TOP 25》
- 整个安全测试的成本:人力,时间等
除了安全测试的有效性,安全测试的成本也非常重要。因为在很多产品开发周期中,安全测试成本是非常有限的,所以安全测试很难覆盖完所有的攻击面和攻击向量。所以根据安全测试的成本,可以大概的度量安全测试的覆盖率。
- 安全漏洞修复的成本
修复成本是安全测试有效性的又一个重要指标,因为一般情况下,修复成本越高,说明这个安全漏洞越复杂越困难。结合安全漏洞的优先级,可以有效的制定安全漏洞的修复的计划。并且在一定程度上可以度量安全测试的有效性。
除了以上这些关键指标,不同的团队还可以根据自己情况制定一些度量指标,比如攻击面的覆盖率和优先级,用户满意度的等。不管使用什么度量方法,最终的目的就是辅助团队进行安全测试,并度量安全测试的有效性。
软件安全漏洞的分类
软件安全漏洞不仅是安全测试有效性最为重要的指标之一,也是制定软件安全测试策略和测试用例设计的重要指导信息。而软件安全漏洞可以分为两大类,见下图:
安全漏洞金字塔
首先底层基础系统漏洞是指那些操作系统,数据库,CPU内置位内核等底层基础系统漏洞。而这类漏洞基本上很难被中低级的安全测试人员发现,并且几乎不能被这些底层基础系统供应商以外的人修复,所以绝大部分这类漏洞只能等待供应商的补丁。不过这类漏洞一般都会被供应商及时发布的补丁所修复,所以及时更新各种基础系统是修复这类漏洞的最好方法。而这类漏洞相对于基础软件和应用层漏洞却是比例相对较小。
然后是已知常规基础软件漏洞和应用层业务和技术漏洞。这些漏洞的数量不仅很多,而且危害巨大。但是这类漏洞中的大部分却是可以由软件开发商自己发现和修复的。比如《OWASP Top 10》和《CWE TOP 25》中的多种类型的漏洞都是可以通过各种不同类型的安全测试来发现,比如第三方依赖安全扫描,系统动态扫描,安全功能测试等,然后其中大部分都是可以由自己的开发人员进行修复的。许多安全社区都在总结或者记录这类安全漏洞,比如最为出名的是以下三个漏洞库:
-
CWE(Common Weakness Enumeration),软/硬件漏洞库
-
CVE(Common Vulnerabilities and Exposures),公共漏洞库
-
NVD(National Vulnerability Database),美国国家漏洞库
安全测试的分类
当前大量的安全测试主要是指手动或者使用某些工具进行安全扫描,渗透测试等。由于渗透测试和安全攻击需要丰富的经验和技术,而使用特定的工具来进行安全扫描和渗透测试也需要特殊的技能,导致安全测试只有少量专业人员才可以做。其实常规的安全测试是可以借助大量的工具以自动化的方式进行,并且可以集成入CI服务器,从而让开发团队中的所有人员都可以在CI流水线完成之后第一时间发现软件系统的常规安全漏洞,从而不必等到上线前由安全专家来发现安全漏洞,再来加班修复。
代码静态扫描
通过人工评审来发现代码中的安全漏洞的成本非常高,并且随着项目代码规模的增加,评审难度和成本也随之增加。所以可以利用静态代码扫描工具自动的对代码进行扫描,在静态代码层面上发现各种漏洞,其中包括安全漏洞。而且自动化扫描工具可以加入CI服务器,能伴随着流水线进行自动扫描,保证每天提交的代码都能经过安全检查,从而实现快速反馈,降低漏洞发现的成本和维修成本。
比如可以使用Fortify统一扫描Android,iOS和Web系统的所有代码。但是由于Fortify等类似的静态代码安全扫描工具会发现很多各种级别的安全漏洞,所以评审漏洞需要花费一些时间。如果项目成本优先,可以先只关注高危漏洞,有些低优先级的漏洞可以暂缓修复。
第三方依赖安全扫描
由于当前应用依赖的第三方库和框架越来越多、越来越复杂,比如SSL、Spring、Rails、Hibernate、.Net,以及各种第三方认证系统等,而且系统开发的时候一般选定某个版本后,在很长一段时间内都不会主动去更新,因为更新的成本一般都比较高,比如新库和新框架更改了API的使用方法和使用流程,可能导致系统进行大规模重构。但是往往这些依赖为了添加新的功能和修复各种当前的漏洞——包括安全漏洞,却会经常发布新版本。这些依赖库和框架的安全漏洞只要被发现,通常都会被公布到网上,比如CVE、CWE、乌云等,导致很多人都可以利用这些漏洞去攻击使用这些依赖的系统。
依赖扫描就是通过扫描当前应用使用到的所有依赖(包括间接依赖),并和网上公布的安全漏洞库进行匹配,如果当前某个依赖存在某种危险级别(需要自己定义)的漏洞,就立即发出警告(比如阻止CI编译成功等)来通知开发人员或者系统管理员,从而在最短的时间内启动应对措施,修复这个漏洞,达到防止攻击,避免或者减少损失的目的。
比如可以使用OWASP Dependency Check来自动扫描Android应用和Web服务器系统的第三方依赖库是否存在安全漏洞,然后加入到流水线中,并配置为只要检测到高危漏洞,流水线就会失败,阻止生成应用的编译和构建,并发出警告。
持续性的自动化安全扫描很主要是替代把以前人工效率最低的那部分,以达到高效的目的。虽然当前绝大部分安全扫描工具并不能发现所有的安全漏洞,但是它可以在较小投入的情况下持续发现大部分系统的基础安全漏洞,从而防止大部分中级和几乎所有初级黑客的攻击。但是BSI去不能完全省去人工的工作,比如人工审查自动化安全测试的报告,如果有安全漏洞,还需要人工分析安全漏洞等。
系统动态扫描
静态代码扫描可以发现代码中的安全漏洞,但是当软件系统的各个组件集成到一起之后或者系统部署到测试环境后,仍然可能会产生系统级别的安全漏洞,比如XSS,CSRF等安全漏洞,所以在这个时候对系统进行动态安全扫描可以在最短的时间内发现安全漏洞。动态扫描一般分为两种类型:主动扫描和被动扫描。
- 主动扫描是首先给定需要扫描的系统地址,扫描工具通过某种方式访问这个地址,如使用各种已知漏洞模型进行访问,并根据系统返回的结果判定系统存在哪些漏洞;或者在访问请求中嵌入各种随机数据(模糊测试)进行一些简单的渗透性测试和弱口令测试等。对于一些业务流程比较复杂的系统,主动扫描并不适用。比如一个需要登录和填写大量表单的支付系统,这个时候就需要使用被动扫描。
- 被动扫描的基本原理就是设置扫描工具为一个代理服务器,功能测试通过这个代理服务访问系统,扫描工具可以截获所有的交互数据并进行分析,通过与已知安全漏洞进行模式匹配,从而发现系统中可能的安全缺陷。一般在实践中,为了更容易地集成到CI,会在运行自动化功能测试的时候使用被动扫描方法,从而实现持续安全扫描。
虽然自动扫描工具可以发现大部分基本的安全漏洞,比如XSS,CSRF等,但是它不能发现业务逻辑、身份认证以及权限验证等相关的安全漏洞,而对于这些类型的漏洞则需要开发相应的自动化安全功能测试。
安全功能测试
安全漏洞中有一部分是由于业务设计漏洞、流程逻辑错误、或者在某些场景下没有做身份认证以及权限验证等造成的。这部分漏洞是很难通过代码静态扫描、第三方依赖扫描或者系统动态扫描来发现的。而针对这部分漏洞的测试用例(Evil Scenarios),可以由业务分析人员在业务分析的时候进行设计,或者由开发人员在架构设计或者DDD事件风暴工作坊中进行设计,或者测试人员通过威胁建模的信息进行设计,然后作为验收测试的一部分由开发或者测试人员进行测试的。最后还可以将这些测试用例加入自动化测试中。最后,在项目流水线里面嵌入这些自动化扫描和测试,从而保证代码提交以后可以持续性的自动运行这些安全扫描和测试。
渗透测试(漏洞攻击,模糊测试等)
经过了以上的各种扫描以及安全功能测试并修复其发现的中高危险级别的漏洞后,一个软件系统可以达到中等级别的安全程度,可以抵御大部分中低级别黑帽的攻击。但是要抵御中高级别黑帽的攻击,仍然还需要做渗透测试,并且做渗透测试的也需要是中高级的白帽。
渗透测试需要依赖大量的经验以及专业的工具,以及对被测试系统的深入了解和分析。传统的渗透测试是将被测系统当作一个黑盒来进行测试。但是由于现在系统的复杂大越来越大,成本控制也越来越严。所以为了提升渗透测试的效率,需要测试人员在开始测试前对系统进行深入的了解,其中包括业务流程,软件架构,基础软件的信息,威胁建模的信息以及已经做过的各种安全测试的信息等,这样可以大量节约渗透测试的系统系统调查的时间,以及帮助测试人员进行更快的找到更容易攻击的攻击面和攻击向量。
渗透测试人员一般会通过使用以后的漏洞库对系统进行扫描,或者通过模糊测试的方法攻击系统,并结合系统的各种详细信息筛选攻击面和攻击向量,然后尝试使用不同的方法对系统进行攻击,并尝试获取未授权数据,或者通过提升权限来控制系统等。
以上这些安全测试方法只能找到大部分的安全漏洞金字塔中“已知常规基础软件漏洞和应用层业务和技术漏洞”,仍然有少部分很难以发现的,或者没有在漏洞库中的漏洞发现不了。而对于“底层基础系统漏洞”几乎是发现不了的,只有少量的可能会被资深的渗透测试安全专家发现。
安全测试的一些实践
不同类型的项目,安全测试的相关实践也是有所不同的,一般可以分为服务器端系统安全测试和客户端系统安全测试。它们安全测试的攻击面和攻击向量也是有所不同的。
服务器端系统安全测试,其攻击面主要是服务器端的各种服务以及其操作系统上的各种基础软件,使用的主要攻击向量就是网络访问。其中一些攻击类型包括:
- SQL/Command注入攻击
- 会话重用
- 内存溢出
- DOS/DDOS
客户端系统安全测试,其攻击面主要是本地文件系统和内存系统以及应用本身的代码(其中代码包括源代码,中间代码以及二进制代码)等。其中一些攻击类型包括:
- 认证失窃
- 会话劫持
- 敏感数据泄露
安全测试领域还有一个比较特殊的领域,社会工程学。它是社会学和计算机安全的一个交叉领域,主要是指通过人与人之间的合法交流,使特定的人受到特定的心理影响或者欺骗,然后自愿或者无意的泄漏一些机密信息,然后让攻击者可以入侵其计算机系统的行为。利用社会工程学进行攻击的类型有:
- 钓鱼攻击
- 中间人攻击
- 弱密码攻击
安全测试的一些建议
安全测试应该是软件测试中对于技术覆盖面最广和最深,难度系数最高的一种测试。不过对于普通的测试人员也不是遥不可及的,因为安全测试也是包含多种类型,它们难度程度也有所不同,可以由浅入深的进行学习,并应用它们进行测试。对于一个通用的软件系统,可以参考以下建议的步骤进行安全测试:
- 一定要参与威胁建模或者审查威胁建模结果
- 了解被测软件的技术架构和业务流程
- 分析并制定系统的攻击向量和攻击面
- 针对重要的攻击面一定做相对应安全测试(攻击)
- 尽量做代码静态扫描,第三方依赖安全扫描,系统动态扫描以及渗透测试
安全测试只是软件安全保证体系中其中一步,当软件系统上线后,还有许多安全实践可以帮助保护软件系统安全,比如关闭不需要的服务;对于打开的服务需要限制可以访问的IP等各种安全实践。不过做好了安全测试,可以最大程度的从软件本身角度去防止漏洞发布到产品环境,从而帮助软件和团队实现软件内建安全开发。