一般来说,版本功能测试完成,对应的用例也实现了自动化,性能、兼容、稳定性测试也完成了以后,我们就需要考虑到系统的安全问题,特别是涉及到交易、支付、用户账户信息的模块,安全漏洞会带来极高的风险。
一.安全测试原则与常见的安全威胁:
1.安全需求:
※认证:对认证的用户的请求返回
※访问控制:对未认证的用户的权限控制和数据保护
※完整性:用户必须准确的收到服务器发送的信息
※机密性:信息必须准确的传递给预期的用户
※可靠性:失败的频率是多少?网络从失败中恢复需要多长时间?采取什么措施来应对灾难性的失败?(个人理解这个地方应该更偏向于容错容灾测试的范畴)
※不可抵赖:用户应该能证明接收到的数据来自特定的服务器
2.常见的安全测试内容
权限控制
SQL注入
URL安全测试
XSS(跨站脚本攻击)
CSRF(跨站请求伪造)
URL跳转漏洞
其他安全方面的考量
接下来,我们以一个C#实现的下常见的MVC架构网站为例,来分析具体的各个安全测试角度。
二.权限控制
权限控制相对来说比较简单,功能测试的过程中也接触过不少,主要就是考虑以下方面:
1.用户权限:我们假设存在两个用户A,B;其中A的权限级别很高,B的权限级别则很低:
只有A能进行的操作,B能不能进行操作;
只有A能看到的页面,B能不能看到;
2.页面权限:
必须登录才能看到的页面,不登录直接访问能否看到?
必须A-B-C的页面,能否直接A-C?
通常来说单纯的权限控制页面测试不复杂,但是因为权限控制和后续的URL跳转、Session等方面结合的比较紧密,所以单独提出来。
三.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与PassWord string 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等操作的权限。
四.URL安全测试:
1.MVC下的URL构成:
1).使用Get方式的URL构造方式:
~/控制器名/调用的方法?入参1=参数1&……&入参n=参数n,例如:
可以看到,在不经过处理的情况下,Get方式生成的URL可以看到数据和参数
2).使用Post方式的URL构造方式:、
~/控制器名/调用的方法,入参则放在报文实体主体部分中,例如:http://localhost:12688/Order/OrderList
通过接口测试手段可以获取到访问的入参:
{
"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等场景。
五.XSS
跨站脚本攻击(Cross Site Scripting),为了和层叠样式表css区分,简写为XSS。
1.原理
攻击者在网页中嵌入客户端脚本(例如JavaScript), 当用户浏览此网页时,脚本就会在用户的浏览器上执行,从而达到攻击者的目的。
例:
存在一个文本控件,其中value1from来自用户的输入:
<input type="text" name="address1" value="value1from">
直接输入 "/><script>alert(document.cookie)</script><!-,那么实际执行的语句:
<input type="text" name="address1" value=""/><script>alert(document.cookie)</script><!- ">
也可以将内容放到URL中进行访问:
http://localhost:10344/AbnormalPunch/ApplySubmit?ID=<script>alert(document.cookie)</script>
我们可以看到,输入的内容直接改变了程序的执行代码,导致程序执行结果为攻击者想要的。
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会抛出一个错误,暂时不清楚其他语言是否有这种机制。
六.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攻击。
七. URL跳转漏洞
1.实现原理:
服务端未对传入的跳转URL变量进行检查和控制,可能导致可恶意构造任意一个恶意地址,诱导用户跳转到恶意网站。
一般来说,URL跳转的测试方法为修改参数中的合法URL为非法URL,然后查看是否能正常跳转或者响应包是否包含了任意的构造URL。
2.测试方法:
修改参数中的合法URL为非法URL,然后查看是否能正常跳转或者响应包是否包含了任意的构造URL
八.其他安全角度的考量
1.安装包测试:对C/S端的程序,安装包主要考虑反编译、签名、完整性、权限等
2.数据库:对于数据库中的敏感字段,如用户名、密码等应该进行加密处理后再存储;Cookie等敏感信息也要考虑设置过期时间等;
3.日志、配置文件中尽量不要包含敏感信息
4.账户安全:数据库中的密码加密存储,传输的密码加密,密码多次输入错误后进行锁定,多设备登录的处理、注销后的身份验证等
5.结合实际版本需要的考量:比如一款电商APP,提交订单接口时传入了订单信息和订单总金额,支付接口根据提交订单接口传入的总金额进行支付;问题在于支付接口完全信任了订单金额没有做验证,那么可以直接调用订单接口伪造金额从而以便宜的价格购物。