软件安全测试是保证软件能够安全使用的主要的手段,如何进行高效的安全测试成为业界关注的话题。多年的安全测试经验告诉我们,做好软件安全测试的必要条件是:一是充分了解软件安全漏洞,二是拥有高效的软件安全测试技术和测试工具。

  一:安全主要漏洞与测试方法分析

  软件安全保证的主要目的是为了防止黑客或者恶意的内部人员对软件进行攻击,并保证软件在受到恶意攻击的情况能够正常运行。而攻击者主要是通过对软件系统输入恶意的数据来进行攻击。因此,软件主要的安全问题来源于外部的输入数据。开放性WEB应用项目安全组织OWASP Top Ten总结每年的全球WEB应用系统十大安全漏洞,其中多的安全漏洞是来自外部输入数据的验证方面。全球大的软件安全厂商Fortify Software公司的软件安全漏洞分类中安全风险高的安全漏洞也是输入验证与表现这个方面。由外部输入恶意数据可以直接构成严重的软件安全漏洞主要有:

  命令注入(Command Injection)

  跨站脚本(Cross-Site Scripting)

  拒绝服务(Denial of Service)

  HTTP响应截断(HTTP Response Splitting)

  路径操纵(Path Manipulation)

  资源注入(Resource Injection)

  配置操纵(Setting Manipulation)

  SQL注入(SQL Injection)

  访问控制:数据库(Access Control: Database)

  日志伪造 (Log Forging)

  所以做好外部输入数据安全测试是软件安全测试中重要的工作,那么目前主要安全测试方法有:

  1.静态的代码安全测试:主要是通过对软件系统的源代码进行安全扫描,根据程序中数据流,控制流,语义等信息与其特有软件安全规则库进行匹对,从中找出代码中潜在的安全漏洞。静态的源代码安全测试是非常有用的方法,它可以在编码阶段找出所有可能存在安全风险的代码,这样开发人员可以在早期解决潜在的安全问题。而正因为如此,静态代码测试比较适用于早期的代码开发阶段,而不是测试阶段。同时,由于关系到开发部门,测试部门,管理部门等多个部门的工作,在实际的贯彻实施工作上有一定的难度。

  2.动态的渗透测试:渗透测试也是我们常用的安全测试方法。使用自动化工具或者人工的方法模拟黑客的输入,对应用系统进行攻击性测试,从中找出运行时刻所存在的安全漏洞。这种是测试的特点是真实有效,一般找出来的问题都是正确的,也是较为严重的。但渗透测试一个致命的缺点是,但由于模拟的测试数据只能到达有限的测试点,覆盖率很低。根据美国权威机构统计,渗透测试的覆盖率只能达到20%-30%。漏报率比较高。

  二:Dynamic Taint Propagation方法

  分析两种常用的安全测试的方法,对于外部输入数据的测试都有一定的不足之处,那么如何才能对外部输入数据进行较好的安全测试呢?业界提出了动态污染传播的方法(Dynamic Taint Propagation)。

  动态污染传播方法,主要通过跟踪外部输入的数据在程序中的传播过程,和终执行的情况来分析是否存在安全漏洞和存在什么类别的漏洞。如下图1,它不需要任何特殊的攻击性的测试数据,它假定由外部输入的数据都是不可信的、污染的数据,为数据打上污染的标记,在程序中传播的过程中,如果经过了严格的,我们可以依赖的安全验证,我们认真它不再是污染的,掉去污染标记,否则污染标记在整个传播过程都会被继承下来。一旦有污染标的数据被送到执行代码中执行的时候判断这里可能存在一个安全漏洞。

图1:污染数据的传播过程图

  实现动态污染传播方法来进行测试,首先要对软件程序所有二进制码进行静态地分析,主要是指定以下信息:

  Source:污染数据的来源。找出所有外部数据进行程序的入口的代码。对于WEB应用系统,主要是指读取客户端输入的数据的地方:如HTML的Form表单,用户的Request的参数,客户端的Cookie信息等这些接收外部数据方法。当数据由这些入口点进行程序内部时,数据假定是污染的,被标记上污染标记。

  Propagation:污染数据传播。数据在程序中传播的过程中,当数据从Source点传入,标记上污染标记,污染数据在程序中传播的过程中,无论是被赋给另一个数据,被拼接到另一个数据之中,污染标记都要被继承下来。

  Sink:漏洞的引爆点。找出所有可能引发安全漏洞的代码。这些代码常常是根据外部传入数据而执行特定任务的代码。如SQL的查询,HTML中输出,命令的执行等等。当数据被传到Sink代码时,Sink首先去检查此数据是否带有污染标记,如果没有,则没有安全问题。反之,则此处可能存在安全问题。然后再根据代码执行的结果来判定是什么类别的安全问题。举例来说明这一分析过程:

  userID = request.getParameter("user");

  ……

  2

  try {

  sql = "SELECT * FROM users " +"WHERE id='" + userID + "'";

  ……

  stmt.executeQuery(sql);

  }

  这段代码在静态分析过程中,request.getParameter()方法会被标记为一个“污染源”: Source。stmt.executeQuery()方法会被标记为一个“引爆点”:Sink。当用户输入数据“user”通过request.getParameter()进入程序并赋值予变量“userID”时, “userID”会被标记上“污染”标记。当用 “userID” 来构造变量“sql”时,“sql”会获取“userID”的“污染”标记而成为被“污染”的数据。当“sql”被送到Sink函数stmt.executeQuery()中执行的时候,Sink函数的检测机制检测到了“sql”有污染标记,从而判定此处可能引发一个安全问题,随后再根据Sink函数执行“污染”数据“sql”的结果,这里是执行一个SQL查询命令,依此可以判定此处可能会引发一个SQL—Injection的安全问题。