一般来说,版本功能测试完成,对应的用例也实现了自动化,性能、兼容、稳定性测试也完成了以后,我们就需要考虑到系统的安全问题,特别是涉及到交易、支付、用户账户信息的模块,安全漏洞会带来极高的风险。
一.安全测试原则与常见的安全威胁:
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,例如:
http://localhost:10344/AbnormalPunch/ApplySubmit?ID=13244&EmployeeID=1D2DE5AD8BC74E2A9CA70DE3567472EB
可以看到,在不经过处理的情况下,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:
http://localhost:10344/AbnormalPunch/ApplySubmit?ID=13244&EmployeeID=1D2DE5AD8BC74E2A9CA70DE3567472EB
这个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">
直接输入 "/>
<input type="text" name="address1" value=""/><script>alert(document.cookie)script>- ">
也可以将内容放到URL中进行访问:
http://localhost:10344/AbnormalPunch/ApplySubmit?ID=
我们可以看到,输入的内容直接改变了程序的执行代码,导致程序执行结果为攻击者想要的。
2.类型:
反射型XSS:需要欺骗用户进行操作才能触发XSS代码,主要威胁个体用户
持久性XSS:代码储存在服务器中,用户访问时即可直接触发XSS代码,威胁大量用户
3.解决思路:对数据进行html编码处理,保证用户输入的数据不会改变程序代码
将重要的cookie标记为http only,这样的话Javascript 中的document.cookie语句就不能获取到cookie了.
只允许用户输入我们期望的数据。 例如: 年龄的textbox中,只允许用户输入数字。 而数字之外的字符都过滤掉。
过滤或移除特殊的Html标签, 例如: