致测试同仁们:让我们做安全测试吧!
今天,很多的软件并没有经过专门的安全测试就被放到互联网上,它们携带着各类安全漏洞暴露在公众面前,其中一些漏洞甚至直指软件所承载的核心敏感信息或业务逻辑。这些漏洞一旦被不怀好意者利用,很可能会给企业造成经济损失。带来负面声誉影响的同时,还可能被起诉、遭到罚款等等,细思极恐。其中的一部分原因是企业本身安全意识不强,但是很多时候虽然软件企业已经开始意识到这些问题,却苦于缺少专业的安全测试人员,他们不得不冒着极大的风险先上线赌一把运气再说。
既然如此,作为质量代言人的我们,怎能对此置之不理呢?
你也许会问?怎么理?安全测试水太深了。
安全测试并不遥远
是的,安全测试在软件测试里面是一个很特别的科目(或作“工种”),很多人都觉得这个科目应该全权交给神秘的安全测试人员来管。这个观念导致很多测试人员徘徊在安全测试的门口却迟迟不进去,包括我自己。
直到后来,我非常有幸能够在不同规模的软件开发项目上跟“神秘的安全测试人员”学习如何进行安全测试,发现“神秘的安全测试人员”不光是名字跟我们一样都有“测试”二字,所做的事情在本质上也跟我们测试人员有很多相通之处。
想想看我们都做过什么:
我们修改过url的参数,对不对?他们也是!
我们在数据输入处提供过不合法的数据,对不对?他们也是!
我们尝试过修改只读数据,对不对?他们也是!
我们也测过用户会话是否如期timeout,对不对?他们也是!
Ok,这还不是全部。他们也做测试计划、测试用例设计、bug分析与管理等等。所以,安全测试离我们并没有那么遥远。
当然,我必须承认,安全测试是非常复杂的。一个专业的安全测试专家在某种程度上来说是一个全栈工程师。所以,想要在安全测试上一夜成才不太容易。不过好消息是,作为测试人员的我们却有得天独厚的优势,使我们能够在安全测试上快速起步,帮助团队尽快展开预防并检测安全漏洞的工作。
在这里我想要跟大家分享一下在敏捷开发团队中如何利用我们的测试经验开展安全测试。
安全测试并不陌生
首先,让我们先来了解什么是安全测试,我们作为测试人员有什么可以直接用上的技能和经验。
简单来说,安全测试其实就是一个发现软件安全漏洞的过程,旨在保护软件系统的数据与功能。它跟常规测试相似的地方至少有以下几点:
1. 目标类似
不管是常规测试还是安全测试,都有一个原则:预防胜于检测。这个比较容易理解,不管是常规测试的缺陷也好,还是安全测试的漏洞也好,如果能预防使它不发生,就省了后期的修复与验证工作。如果不能成功的预防缺陷,能早一些发现的话,肯定比晚发现的修复的成本低。
2. 在软件生命周期中的过程类似
以敏捷开发团队为例,常规测试人员在各个阶段做的事情,安全测试人员也要做:
- 了解业务的需求,以避免混乱的测试优先级;
- 针对业务与系统功能设计用例:常规测试需要关注系统功能,安全测试同样也不能脱离系统功能;
- 与其他角色一起启动需求的开发:沟通测试用例,避免因为沟通不足造成返工;
- 与其他角色一起在开发环境验收需求:尽早提供反馈,发现缺陷时开发可以马上修正
- 在测试环境进行全面测试:针对端到端的场景进行测试,尽可能把第三方系统(如果有的话)也包括进来;
- 分析并总结测试结果:整理问题清单,排列优先级;
- 反馈测试结果:把测试结果反馈给团队等干系人。
3. 测试用例很多重合
在面向终端用户的测试场景上,常规测试的用例与安全测试的用例是非常类似的。比如对于登录系统的功能,不管是常规测试还是安全测试,我们都会测试用户输入正确的用户名是否可以登录,输入错误的用户名或密码系统会如何反应。
比如我曾经工作的一个搜集报税人信息的系统,不管是常规测试还是安全测试,都会测试系统的登录,系统信息的录入与编辑,文件的上传等等。因为在每一个终端用户可以操作的场景上,都可能会有安全漏洞存在。所以,有了常规测试的经验,我们就相当于有了不少安全测试用例的储备。
4. 都需要有探索的过程
测试是一个了解软件系统能否完成我们预期的过程,也是探索系统还有哪些我们没有预期的行为的过程。安全测试的过程需要把探索的目标转向安全漏洞。当我们这么做时,我们同样会得到很多探索的乐趣。
5. 都要有测试人员必备的“怀疑态度”
相信咱们测试人员都非常熟悉一个场景--开发人员说:“我只做了一个很小的代码改动。”然后我们带着友好而警惕的态度,发现这个“很小的改动”引发了很大的问题。不管是在安全测试还是非安全测试,这个警惕性是我们都需要保持的良好传统。
那么,有了这么多类似的地方,还缺什么呢?如果想要做专家,还差很多。但是如果想马上安全测试上起步,我们可以先做下面的改变。
安全测试从何做起
第一,转换视角
在我看来,不管是带着全栈的经验,还是只有部分技术知识,想要做好安全测试必须先转换我们观察软件的视角。举个例子,让我们看看下这幅画:
同样一幅画,有人一眼看过去看到的是两个人脸,而有人看到的是一个花瓶。这就是观察视角的不同造成的。
在我刚开始接触安全测试时就很深的体会到了这一点。当时我在测试一个Web应用的用户登录功能。当我输入错误的用户名来试着登陆时,浏览器上的提示信息为“该用户名不存在”。当我尝试正确的用户名而错误的密码时,提示信息变成“密码输入错误。”对于这个清晰的错误提示我非常满意。试想我若是一个真实的终端用户,这个信息有效的帮助我缩小纠错范围,提高效率,非常好。
可是,就在我身边坐着的安全测试人员马上跳了出来:“这个提示信息需要改!敏感信息暴露了!”看到我一脸茫然,这位安全测试人员告诉我,通过我们的提示信息,恶意的系统使用者可以推测出哪些用户名已经存在于系统中,然后利用这些用户名可以再进行密码的暴力破解,缩小破解的范围。所以,这个信息虽然为合法用户提供了便利,也为不怀好意的系统使用者提供了便利。而往往这种便利为恶意的系统使用者带来的好处远大于给合法用户带来的好处。
这个经历在让我受震动的同时,也使我意识到可能很多安全漏洞之前就摆在我的面前了,我却没有看出来,因为我把它们过滤了。事实证明,在后来经历的不同项目中,当我转换了视角,有些安全漏洞不需要我去找,而是自己跑到我眼前来的。真是得来全不费功夫。
第二,改变测试中模拟的对象
为了能从不同的视角来观察软件,我们必须改变我们所模拟的对象。这也是一个让我们刻意练习转换视角的有效方法。
我们在做非安全测试的时候通常把自己想象成一个合法用户,然后开始验证系统是否能完成预设的目标。比如对于一个网上商城,我们会验证系统是否能让用户完成商品的浏览与购买,我们也会测试一些异常的行为,比如购买的商品数量不是数字而是一串无意义的字母时,看系统是否能比较优雅的做出回应。我们这么测试的目的往往是为了确保用户误操作以后还能够继续他们的购买,或者说不要给系统造成什么严重的伤害。
如果要做安全测试,我们则必须去模拟系统的另一类使用者-恶意用户。他们的目的是寻找系统中可钻的漏洞。比如同样是一个网上商城,恶意用户的目标之一就是要想办法以较少的钱,甚至不付钱就能拿到商品。所以,如果恶意用户进行了“误操作”,他们不会停留在“误操作”,而是通过“误操作”来看系统是否给自己提供更多的线索。
所以,我们需要转换测试时所模拟的对象,把思维从一个合法用户的视角中拉出来,转换成一个恶意用户。这需要一点时间,就如同之前看到的画,如果我们一开始看到的是人脸,要想下一次第一眼看到的是花瓶,我们需要时间来刻意练习。
第三,使用专用的测试工具
有了思维的转换,我们可以加入新的测试想法。但是,在具体做安全测试的时候我们会发现并不是那么容易去模拟恶意用户的行为。毕竟系统的前端会给我们设置很多的屏障。而且恶意用户可不总是从系统前门进去的。这时候,使用一些工具,比如OWASP Zap(https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project)、Burp(https://portswigger.net/burp/)等是非常有帮助的。我们可以在系统界面上执行功能测试的用例,用这些工具来获取http请求,篡改后发送给后台服务器。有了这些实用又比较容易上手的工具,我们就可以执行很多恶意用户的操作场景了。
能做到这三点,起步就基本够用了。
举个栗子吧🌰
下面让我们以网上商城的买家在商品评价中上传图片这个功能来讲讲如何实践这“三板斧”。假设我们从项目初期就加入了,那么我们大致有七件事情要做:
- 识别系统中有价值的数据;
- 在需求分析阶段加入恶意用户需求;
- 针对恶意用户需求设计测试用例;
- 参与启动恶意需求的开发;
- 在开发环境验收恶意需求的实现;
- 在测试环境中进行安全测试;
- 向团队反馈所发现的安全漏洞。
不要担心,这不是7个全新的事情。只是在每个需要测试人员出现的地方增加了安全的工作而已。
1. 识别系统中有价值的数据
很多人认为执行测试才是测试,而我们的安全测试从这里就开始了。
了解业务之后,我们需要考虑系统中会有什么有价值的数据。这是为下一步加入恶意用户需求做准备。对于一个网上商城,有价值的数据可能包括产品信息、订单信息、用户信息、支付,等等。
这个环节对我们测试人员来说并没有太多额外的工作,毕竟我们做非安全测试的时候也需要了解业务。不过要注意了,我们要测试的“图片上传功能”是一个涉及有价值数据的功能。我们需要提高警惕了。
2. 在需求阶段加入恶意用户需求
恶意用户需求是用来记录恶意用户想要在系统中达到的目的。与普通用户需求的区别是,我们不是要去实现它,而是使用它来帮助我们远离对系统使用者“不恰当的信任”。通常我们需要针对每一个合法用户需求来增加一个或多个相对应的恶意用户需求。
举个例子,如果我们这个“图片上传功能”的合法用户需求为:作为一个买家,我想在对商品进行评价的时候上传图片作为买家秀,以便于参加返现营销活动。那么对应的恶意用户需求可以是:为一个恶意用户,我想破坏买家秀返现活动,以便破坏商城的营销活动。“破坏买家秀返现活动”是一个大的目标。为了设计用例方便,它可以被细分为一系列小目标。比如让用户无法上传图片、让页面无法正确显示图片等等。
有了恶意用户需求的主干信息,我们就可以开始下一步设计安全测试用例了。
3. 针对“恶意用户需求”设计测试用例
现在我们需要做的是努力把自己限制在“恶意用户”的角度做头脑风暴:“到底有什么方法可以使买家无法上传图片信息呢?”, “让页面无法正确显示买家秀图片又怎么做到?”嗯,也许最直接的办法就是让服务器所在的机房断电、断网之类的。这是些不错的想法,虽然执行难度有点大。没关系,记录下来。除此之外,我们还可以有其他测试用例,比如:
- 使存储图片的磁盘空间被占满而无法接受新的图片;
- 使处理上传图片的进程繁忙而无法接受新的上传任务;
- 上传特别大的图片使用户的客户端需要很长时间才能下载完
- 上传伪装成图片的恶意代码,进一步获取服务器权限,删除所有的买家秀图片;
- 等等
如果这个时候想到新的测试用例也同样记录下来,比如“我想不购买也上传买家秀图片以获得返现”之类的。
不用太担心这个阶段的测试用例过于“疯狂”或者不够完整,毕竟我们对于系统的实现还不是很了解。我们会在接下来的环节中完善具体的步骤。
4. 参与启动恶意需求的开发(evil story kickoff)
在开发人员开始开发合法用户需求之前,我们需要跟业务分析人员、开发人员一起沟通需求的内容。在敏捷软件开发项目中我们叫它story kickoff,即用户故事启动。当有了对应的恶意用户需求时,我们必然也要把它也加到启动的范围里。目的是把我们头脑风暴出来的测试用例跟所有的角色来沟通。预防胜于检测。
5. 在开发环境验收恶意需求的实现
100%预防软件的缺陷与漏洞是不太可能的,所以这个环节的存在是为了提早反馈。
我曾经经历过一个项目,都快上线了才决定做安全测试,结果测出来的问题之一是用户会话(user session)不能正确过期的问题,经过一番研究,发现需要对系统设计的架构进行比较大的修改,只能做个临时的修复让系统先上线,然后再把系统的架构给改了,重写这部分功能,重新测试。代价非常高。所以不管是安全测试还是非安全测试,”在开发环境验收恶意需求的实现“这个步骤都不能缺少。
而这个环节存在的第二个目的是让我们可以从开发人员那里得到支持-具体实施的细节,帮助我们完善具体的测试用例。比如在这个时间点我们若从开发人员那里得知系统的后台没有对图片上传者做身份验证,我们就可以至少增加一个测试的用例:“恶意用户以其他用户的身份上传一个风马牛不相及的图片”。有时候错误的图片比没有图片更具有杀伤力。
6. 在测试环境中进行安全测试
终于到了运行测试的阶段。可能这个时候我们之前想到的测试用例已经被开发人员给解决了。如果是这样那就太好了。但是,事实并非有这么美好。第一,可能这些用例只是在开发环境上成功通过了,但是在理想的测试环境里,也就是类产品环境里,这些用例可能并不能完全通过;第二,肯定还有其他需要探索的地方。这时我们就可以用OWASP Zap、Burp这样的工具来辅助我们把之前的安全测试用例执行一次,同时还再可以对系统的安全性做一下探索测试。
7. 向团队反馈所发现的安全漏洞
都测得差不多的时候,我们就可以向团队以及相关干系人汇报安全测试的结果了。跟非安全测试不同的地方是,当我们反馈安全漏洞的时候,要考虑不同漏洞结合起来是否会增加系统的安全风险。举个例子:如果有两个安全漏洞,一个是系统没有很强的用户账户密码规规则,另一个是系统没有对上传图片的大小做限制,那么恶意用户把这两个漏洞一结合起来,事情就比原来风险大很多。那么我们就必须建议提高这两个漏洞中任意一个的优先级。
当我们用“三板斧”走完这七步以后,我们已经可以把很多安全漏洞都挖出来了。是不是没有想象中的难?所以,测试同仁们,让我们做安全测试吧!
更多精彩洞见,请关注微信公众号:思特沃克