五年前,偶然机会进入测试行业,那个时候,实习什么都不懂,特别羡慕有三五年测试经验的人,想着,等自己也有五年经验了,也要像博客园的大神一样,给初入测试行业的同学一些有用的建议和指导,如今,已经五年了,却没有成为自己当初想成为的那种大神,这篇博文,权当是完成自己全年前的一个心愿,为这五年做个小结。
1,软件测试方法分类
小结:
根据开发阶段划分:单元测试、集成测试、系统测试、开发测试; 根据是否运行划分:静态测试、动态测试 根据是否查看源代码划分:黑盒测试、白盒测试 其他还有回归测试、冒烟测试、随机测试 其中黑盒测试包括功能测试和性能测试; 功能测试有:逻辑功能测试、界面测试、易用性测试、安装测试、兼容测试; 性能测试有:一般性能测试、稳定性测试、压力测试、负载测试
测试方法概念: 1.1,按是否查看程序内部结构分为: 1.1.1,黑盒测试(black-box testing):只关心输入和输出的结果 1.1.2,白盒测试(white-box testing):去研究里面的源代码和程序结构 1.2,按是否运行程序分为: 1.2.1,静态测试(static testing):是指不实际运行被测软件,而只是静态地检查程序代码、界面或文档可能存在的错误的过程。 静态测试包括: 对于代码测试,主要是测试代码是否符合相应的标准和规范。 对于界面测试,主要测试软件的实际界面与需求中的说明是否相符。 对于文档测试,主要测试用户手册和需求说明是否真正符合用户的实际需求。 1.2.2,动态测试(dynamic testing),是指实际运行被测程序,输入相应的测试数据,检查输出结果和预期结果是否相符的过程 1.3,按阶段划分: 1.3.1,单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。 桩模块(stud)是指模拟被测模块所调用的模块,驱动模块(driver)是指模拟被测模块的上级模块,驱动模块用来接收测试数据,启动被测模块并输出结果。 1.3.2,集成测试(integration testing),是单元测试的下一阶段,是指将通过测试的单元模块组装成系统或子系统,再进行测试,重点测试不同模块的接口部门。 集成测试就是用来检查各个单元模块结合到一起能否协同配合,正常运行。 1.3.3,系统测试(system testing),指的是将整个软件系统看做一个整体进行测试,包括对功能、性能,以及软件所运行的软硬件环境进行测试。 系统测试的主要依据是《系统需求规格说明书》文档。 1.3.4,验收测试(acceptance testing),指的是在系统测试的后期,以用户测试为主,或有测试人员等质量保障人员共同参与的测试,它也是软件正式交给用户使用的最后一道工序。 验收测试又分为a测试和beta测试,其中a测试指的是由用户、 测试人员、开发人员等共同参与的内部测试,而beta测试指的是内测后的公测,即完全交给最终用户测试。 1.4,黑盒测试分为功能测试和性能测试: 1.4.1,功能测试(function testing),是黑盒测试的一方面,它检查实际软件的功能是否符合用户的需求。 包括逻辑功能测试(logic function testing) 界面测试(UI testing)UI=User Interface 易用性测试(usability testing):是指从软件使用的合理性和方便性等角度对软件系统进行检查,来发现软件中不方便用户使用的地方。 兼容性测试(compatibility testing):包括硬件兼容性测试和软件兼容性测试 1.4.2,性能测试(performance testing) 软件的性能主要有时间性能和空间性能两种 时间性能:主要指软件的一个具体事务的响应时间(respond time)。 空间性能:主要指软件运行时所消耗的系统资源。 软件性能测试分为: 一般性能测试:指的是让被测系统在正常的软硬件环境下运行,不向其施加任何压力的性能测试。 稳定性测试也叫可靠性测试(reliability testing):是指连续运行被测系统检查系统运行时的稳定程度。 负载测试(load testing):是指让被测系统在其能忍受的压力的极限范围之内连续运行,来测试系统的稳定性。 压力测试(stress testing):是指持续不断的给被测系统增加压力,直到将被测系统压垮为止,用来测试系统所能承受的最大压力。(Validate the system or software can allowed the biggest stress.) 1.5,其他测试类型: 回归测试(regression testing)是指对软件的新的版本测试时,重复执行上一个版本测试时的用例。(When a new build or release is deployed, repeat all the test cases which has executed in the last build or release.) 冒烟测试(smoke testing),是指在对一个新版本进行大规模的测试之前,先验证一下软件的基本功能是否实现,是否具备可测性。(validate the major function is deployed or not in software of system when a new build or release is implement.) 随机测试(random testing),是指测试中所有的输入数据都是随机生成的,其目的是模拟用户的真实操作,并发现一些边缘性的错误。(means or all the test data is random, to validate the some edge bugs.)
2,几种常见测试方法详解:
2.1,文档测试
文档:安装手册,操作手册,维护手册(文档由产品和UI提供)
测试内容: 2.1.1,文档是否齐全,是否包含产品使用所需的信息和所有功能模块; 2.1.2,文档描述是否正确,是否没有歧义和错误的表达; 2.1.3,文档是否容易理解,是否通过使用适当的术语和图形等方式来表达; 2.1.4,文档对主要功能和关键操作是否提供应用实例; 2.1.5,文档是否有详细的目录表,索引表,链接; 2.1.6,文档描述与软件当前版本符合(使用方法,使用约束,FAQ等) 2.2,易用性测试小白体验+测试经验习惯+业务知识
测试内容:
2.2.1,软件是否没有完全不符合IT行业习惯的操作,完成一次业务过多的操作步骤和弹出窗口,业务逻辑不符合思维逻辑; 2.2.2,软件中是否不存在提示信息过于复杂或者简单; 2.2.3,软件中的各模块的界面风格是否一致; 2.2.4,软件的用户界面是否友好; 2.2.5,软件中的查询结果的输出方式是否比较直观,合理2.3,安全测试
软件安全性测试包括程序、网络、数据库安全性测试。根据系统安全指标不同测试策略也不同。
2.3.1,用户程序安全的测试要考虑问题包括: ① 明确区分系统中不同用户权限; ② 系统中会不会出现用户冲突; ③ 系统会不会因用户的权限的改变造成混乱; ④ 用户登陆密码是否是可见、可复制; ⑤ 是否可以通过绝对途径登陆系统(拷贝用户登陆后的链接直接进入系统); ⑥ 用户推出系统后是否删除了所有鉴权标记,是否可以使用后退键而不通过输入口令进入系统。 2.3.2,系统网络安全的测试要考虑问题包括: ① 测试采取的防护措施是否正确装配好,有关系统的补丁是否打上; ② 模拟非授权攻击,看防护系统是否坚固; ③ 采用成熟的网络漏洞检查工具检查系统相关漏洞; ④ 采用各种木马检查工具检查系统木马情况; ⑤ 采用各种防外挂工具检查系统各组程序的客外挂漏洞。 2.3.3,数据库安全考虑问题: ① 系统数据是否机密(比如对银行系统,这一点就特别重要,一般的网站就没有太高要求); ② 系统数据的完整性; ③ 系统数据可管理性; ④ 系统数据的独立性; ⑤ 系统数据可备份和恢复能力(数据备份是否完整,可否恢复,恢复是否可以完整)。
2.3.1,常见的安全测试内容
权限控制
SQL注入
URL安全测试
XSS(跨站脚本攻击)
CSRF(跨站请求伪造)
URL跳转漏洞
2.3.2,权限控制
权限控制相对来说比较简单,功能测试的过程中也接触过不少,主要就是考虑以下方面:
1.用户权限:我们假设存在两个用户A,B;其中A的权限级别很高,B的权限级别则很低:
只有A能进行的操作,B能不能进行操作;
只有A能看到的页面,B能不能看到;
2.页面权限:
必须登录才能看到的页面,不登录直接访问能否看到?
必须A-B-C的页面,能否直接A-C?
通常来说单纯的权限控制页面测试不复杂,但是因为权限控制和后续的URL跳转、Session等方面结合的比较紧密,所以单独提出来。
2.3.3,SQL注入
1,SQL注入原理
以Sql Sever为例,C#提供了两种操作数据库的方法,以实现Sql Sever的查询功能为例(Mysql只需要将Sql对象替换为MySql对象即可):
A,直接使用传入的Sql进行数据库操作:
public static DataSet QuerySql(string sSql) { DataSet ds = new DataSet(); try { Open(); SqlDataAdapter mDataAdpter = new SqlDataAdapter(sSql, conn); mDataAdpter.Fill(ds); } catch (SqlException ex) { throw new Exception(ex.Message); DataBaseException = ex.Message; } finally { conn.Close(); } return ds; }
B,使用SqlParameter对象,对参数进行格式化,分离控制语句与执行语句:
public static DataSet QuerySql(string sSql, SqlParameter[] Params) { DataSet ds = new DataSet(); SqlCommand comm = new SqlCommand(); try { Open(); comm.Connection = conn; comm.CommandType = CommandType.Text; comm.CommandText = sSql; comm.Parameters.Clear(); foreach (SqlParameter p in Params) { comm.Parameters.Add(p); } SqlDataAdapter mAdapter = new SqlDataAdapter(comm); mAdapter.Fill(ds); } catch (SqlException ex) { DataBaseException = ex.Message; throw new Exception(ex.Message); } return ds; }
接下来我们来看以下场景:
用户登录,账号密码存在User表中,该表字段为ID,Name,PassWord三个字段,验证用户登录时直接传用户输入的用户名与密码给数据库,如果我们直接采用方式一,那么代码实现如下:
//获取传入的用户UserName与PassWordstring sql = "SELECT * FROM User WHERE UserName = '"+UserName+"' AND PassWord = '"+PassWord+"'";//其他处理代码
假设我们的账户名称是admin,密码是123,那么构造出的sql就是:
SELECT * FROM User WHERE UserName = ’admin’ AND PassWord = ’123’
执行结果看起来没有问题,但是如果这个时候我们输入的密码是 ‘’or 1=1时,那么实际执行的SQL就变成了:
SELECT * FROM User WHERE UserName = ’admin’ AND PassWord = '''' OR 1=1
(这里输入两个分号是因为构造sql的时候就已经是分号了,在sql sever中查询的字符串中带有分号则是需要再加一个分号进行转义)
由于1=1恒等式的存在,这条sql会直接查询出整个表的数据;就算没有相关权限,服务器也会认为是一个有效用户进行处理。
如果我们再进一步,输入密码‘’or 1=1;drop table User
那么结果会是什么样呢?User这个表会直接被删除掉!这个时候网站就会因为User表不存在而报错,产生严重的异常。
综上所述,SQL注入的原理就是通过构造符合SQL语法的参数传入程序,通过执行SQL语句进而执行攻击的操作;原因是程序完全信任了传入的数据,致使非法数据能够入侵系统。
2,解决方案:
在了解了SQL注入原理后,我们可以做出针对性的一些措施,如:
尽量避免使用动态构造SQL,而是使用SqlParameter对参数进行格式化或使用框架提供的一些功能,分离控制语句与执行语句;
对传入的字符进行转义处理,’和%、_、[]等通配符进行转义处理,保证执行SQL的时候这些字符是正确在字符串内进行处理的;
在前端表单或控件中增加验证,保证传入后台的数据是合法的;
数据库权限控制,只给对应功能需要的权限,比如登录页面只能进行查询,不赋予其执drop、update、delete、truncate等操作的权限。
2.3.4,URL安全测试:
1,MVC下的URL构成:
1),使用Get方式的URL构造方式:
~/控制器名/调用的方法?入参1=参数1&……&入参n=参数n,例如:
可以看到,在不经过处理的情况下,Get方式生成的URL可以看到数据和参数
2),使用Post方式的URL构造方式:、
~/控制器名/调用的方法,入参则放在报文实体主体部分中,例如:
通过接口测试手段可以获取到访问的入参:
{
"OrderType": "0",
"QueryType": "1"
}
URL安全测试主要是基于URL的构成方式进行的测试,很多时候会涉及到和其他方向的交叉;了解各个原理后,就能比较全面的考虑问题了。
2,参数检查:主要就是检查URL或者主体报文中的参数
使用Get方式时,URL中显示的参数正确;敏感参数如用户名、密码不应被显示;
使用Post方式时,敏感参数应该进行加密处理
极端但是比较安全的处理方式是URL只显示网站地址,
3,根据URL的构成方式直接篡改URL:
我们回过头去看这个URL:
这个URL的内容是的查询某个用户提交的某个申请的明细,URL的传入的ID为要查询的申请,EmployeeID是从Session中取的当前登录用户的ID。
那么我们可以基于以下场景篡改URL进行验证:
用户未登录时,直接输入该网址进行访问(涉及到登录验证);
其他用户登录时,输入这个网址(涉及到权限控制)
构造ID和EmployeeID为SQL注入的参数,然后访问(涉及到SQL注入)
以及稍后会涉及到的XSS、Session、Cookie等场景。
2.3.5,XSS
跨站脚本攻击(Cross Site Scripting),为了和层叠样式表css区分,简写为XSS。
1,原理
攻击者在网页中嵌入客户端脚本(例如JavaScript), 当用户浏览此网页时,脚本就会在用户的浏览器上执行,从而达到攻击者的目的。
例:
存在一个文本控件,其中value1from来自用户的输入:
直接输入 "/><script>alert(document.cookie)</script><!-,那么实际执行的语句:
也可以将内容放到URL中进行访问:
>
我们可以看到,输入的内容直接改变了程序的执行代码,导致程序执行结果为攻击者想要的。
2,类型:
反射型XSS:需要欺骗用户进行操作才能触发XSS代码,主要威胁个体用户
持久性XSS:代码储存在服务器中,用户访问时即可直接触发XSS代码,威胁大量用户
3,解决思路:对数据进行html编码处理,保证用户输入的数据不会改变程序代码
将重要的cookie标记为http only,这样的话Javascript 中的document.cookie语句就不能获取到cookie了.
只允许用户输入我们期望的数据。 例如: 年龄的textbox中,只允许用户输入数字。 而数字之外的字符都过滤掉。
过滤或移除特殊的Html标签, 例如: <script>, <iframe> , < for <, > for >, " for
过滤JavaScript 事件的标签。
4,XSS漏洞的测试:
A,查看代码,验证变量是否经过html编码处理
B,准备测试脚本:
"/><script>alert(document.cookie)</script><!--
<script>alert(document.cookie)</script><!--
"οnclick="alert(document.cookie)
遍历TextBox或者其他文本控件,输入这些测试脚本,如果浏览器弹出窗口且窗口内容为cookie,证明存在XSS漏洞。
C,使用扫描工具如appscan
D,自行设计自动化测试脚本,用HttpWebRequest类,把包含xss 测试脚本发送给Web服务器,然后查看HttpWebResponse中,我们的XSS测试脚本是否已经注入进去了。
5,其他:
目前主流浏览器如Chrome、FireFox、IE8以上版本等都加入了安全机制用来过滤XSS;
ASP.NET中有防范XSS的机制,对提交的表单会自动检查是否存在XSS,当用户试图输入XSS代码的时候,ASP.NET会抛出一个错误,暂时不清楚其他语言是否
有这种机制。
2.3.6,CSRF
跨站请求伪造(Cross-Site Request Forgery),也被称为“One Click Attack”或者Session Riding,简写为CSRF或XSRF。。尽管听起来像跨站脚本(XSS),但它 与XSS非常不同,并且攻击方式几乎相左。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。
1,CSRF的原理:CSRF可以理解为攻击者盗用了你的身份,以你的名义发送恶意请求,具体步骤如下:
1),用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
2),在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
3),用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
4),网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
5),浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
因此,对于一个用户来说,完成给CSRF攻击必须要两个步骤:
A,登录受信任网站A,并在本地生成Cookie。
B,在不登出A的情况下,访问危险网站B。
2,解决思路:
A,验证 HTTP Referer 字段:
在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。在通常情况下,访问一个安全受限页面的请求来自于同一个网站;因此只需要验证Referer的域名,验证合法后再进行下一步操作。
然而,Referer 的值是由浏览器提供的,虽然 HTTP 协议上有明确的要求,但是每个浏览器对Referer的具体实现可能有差别,并不能保证浏览器自身没有安全漏洞。对于某些浏览器,比如 IE6 或 FF2,目前已经有一些方法可以篡改 Referer 值。
B,在请求地址中添加 token 并验证:
要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。
C,在 HTTP 头中自定义属性并验证:
把它放到 HTTP 头中自定义的属性里。通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中。这样解决了上种方法在请求中加入 token 的不便,同时,通过 XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中去。
把它放到 HTTP 头中自定义的属性里。通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中。这样解决了上种方法在请求中加入 token 的不便,同时,通过 XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中去。
3,CSRF漏洞的测试:
A.,最简单的方法就是抓取一个正常请求的数据包,去掉Referer字段后再重新提交,如果该提交还有效,那么基本上可以确定存在CSRF漏洞。
B,使用相关工具进行测试:CSRFTester,CSRF Request Builder等;原理使用CSRFTester进行测试时,首先需要抓取我们在浏览器中访问过的所有链接以及所有的表单等信息,然后通过在CSRFTester中修改相应的表单等信息,重新提交,这相当于一次伪造客户端请求。如果修改后的测试请求成功被网站服务器接受,则说明存在CSRF漏洞,当然此款工具也可以被用来进行CSRF攻击。
2.3.7,URL跳转漏洞
1,实现原理:
服务端未对传入的跳转URL变量进行检查和控制,可能导致可恶意构造任意一个恶意地址,诱导用户跳转到恶意网站。
一般来说,URL跳转的测试方法为修改参数中的合法URL为非法URL,然后查看是否能正常跳转或者响应包是否包含了任意的构造URL。
2,测试方法:
修改参数中的合法URL为非法URL,然后查看是否能正常跳转或者响应包是否包含了任意的构造URL
2.3.8,其他安全角度的考量
1,安装包测试:对C/S端的程序,安装包主要考虑反编译、签名、完整性、权限等
2,数据库:对于数据库中的敏感字段,如用户名、密码等应该进行加密处理后再存储;Cookie等敏感信息也要考虑设置过期时间等;
3,日志、配置文件中尽量不要包含敏感信息
4,账户安全:数据库中的密码加密存储,传输的密码加密,密码多次输入错误后进行锁定,多设备登录的处理、注销后的身份验证等
5,结合实际版本需要的考量:比如一款电商APP,提交订单接口时传入了订单信息和订单总金额,支付接口根据提交订单接口传入的总金额进行支付;问题在于支付接口完全信任了订单金额没有做验证,那么可以直接调用订单接口伪造金额从而以便宜的价格购物。