面向Java EE 程序的SQLIA 漏洞分析和验证方法

2021-02-05 18:10范威威
计算机与生活 2021年2期
关键词:污点静态语句

郭 帆,范威威

江西师范大学计算机信息工程学院,南昌 330022

虽然Web 应用提供了方便性、灵活性、可用性和互操作性,但是它们也容易受到各种各样的安全威胁。在《OWASP Top 10-2017》发布的十大Web 应用安全风险[1]中,处于A1 级别的注入漏洞是其中最重要的一种,而SQL 注入攻击(structured query language injection attack,SQLIA)漏洞最为常见并且使用范围最广。SQLIA 会让攻击者无限制地访问底层数据库,一旦攻击成功,结果往往是灾难性的。

污点分析(taint analysis)可以有效防御SQLIA,它是一种信息流分析技术,通过对敏感数据进行标记进而跟踪标记数据在程序中的传播过程和路径,以找到系统中的安全漏洞[2],主要分为静态分析和动态分析。

静态污点分析从源代码中抽取语法、语义特征并记录数据流向,判断污染数据的产生、传递和使用[3]。Medeiros等[4]结合多种方法发现源代码中的漏洞并使其具有较少误报,首先使用污点分析发现候选漏洞,然后采用数据挖掘方法来预测假阳,误报率较低。Maltitz 等[2]提出了一种基于静态污点分析的形式化的隐私分析方法,用来简化软件体系结构的高级隐私属性的设计和审核过程。Dahse 等[5]通过对数据库、文件名和会话变量的字符串分析来得到二阶数据库访问信息流,可以检测二阶SQLIA 和多阶漏洞。Hauzar 等[6]提出了一个PHP(hypertext preprocessor)静态分析框架,能够自动解析动态语言通用的特性,能够独立地定义动态语言的值和堆分析,并能自动组合它们来查找实际存在的安全漏洞,假阳性率较低。Stiévenart 等[7]开发了一个高度模块化的静态分析框架SCALA-AM,该框架通过分离操作语义实现模块化,能够扩展支持多种语言且包含多种机器抽象,能够按需实现不同的污染分析方案。王蕾等[8]创新地将数据流分析框架扩展成稀疏形式,利用稀疏优化方法对静态污点分析中无关联的污点传播进行消除,以此达到时间和空间的开销优化,但未实现别名间的强更新和保守子域的精确计算。马金鑫等[9]提出并实现了一种基于执行踪迹离线索引的污点分析方法,以字节为粒度,首次描述并解决了即时翻译执行导致的污点丢失问题。Tripp 等[10]实现了一种精确且可扩展的新型静态污点分析方法,将污点分析看作需求驱动问题,只需计算易受攻击性信息流,而不是计算完整的数据流解决方案。对易受影响的信息流进行需求驱动的跟踪和计算,以提升分析的精确度和效率,同时可以将分析扩展到大型代码库。

动态污点分析在程序执行时即时检测数据是否来自外部的非可信输入,通过实时监控程序的污点数据在系统程序中的传播来检测数据能否从污点源传播到污点汇聚点。Amir-Mohammadian 等[11]为Java开发了一个动态完整性污染分析模型,该模型以一种in-depth 方法解决了不完善的消毒问题,结合预期措施和追溯措施,对消毒结果是前瞻性认可的且回顾性污染的情况进行完全消毒,很好地避免了假阳性的出现,但模型扩展性有待加强和开发。Cai 等[12]开发了一个二进制的动态污点分析工具SwordDTA,以期最大化地检测软件漏洞。该工具适用于硬件和软件,通过漏洞建模和污点检查来发现漏洞,对缓冲区溢出、整数溢出、被零除、UAF(use after free)等漏洞有很好的效果,但对其他类型的漏洞暂时无能为力,且路径覆盖率不高。Karim 等[13]为JavaScript提供了一个独立于平台的动态污染分析,将污点传播过程编码为抽象机器指令,执行这些指令就可得到关于应用的污染流报告,但是没有对隐式流进行跟踪,且污点传播策略不够灵活,不支持用户自定义操作。

由于动态数据的不可判定性以及源代码语义的描述能力有限,静态分析结果的精确度有限。而动态分析只能观测到程序的有限次执行,有效性依赖于测试集的完备性。研究人员进而提出了结合静态分析与动态分析的混合分析方法,文献[3]提出了一种编译级的软件脆弱性检测和预防解决方案,首先使用静态分析排除不可执行路径,然后在问题点进行基于插桩的数据验证。Balzarotti 等[14]基于静态分析对应用程序沿着执行路径修改输入的行为进行建模,基于动态分析,重构应用程序用于修改输入的代码,然后执行代码并使用大量恶意输入值来识别消毒过程中可利用的缺陷,以此来分析消毒过程的正确性。

为了降低静态污点分析假阳性率高的问题,提高静态分析的精确度,本文提出了一种静态分析与动态验证相结合的污点分析方法。首先静态提取所有的Source 和Sink 语句,并且对Source 集合进行预处理,剔除不是外部输入的Source。然后,应用基于同方法、Request 请求、Session 会话和方法调用的匹配规则,结合静态污点分析和活跃变量分析,剔除不可能存在污点传播路径的Source 和Sink 对。动态验证首先对程序进行插桩,然后应用自动化测试工具自动生成测试路径,在程序执行时结合动态污点传播分析对Source 和Sink 之间的潜在污点传播路径进行验证。如果漏洞真实存在,则对相应的污点传播路径进行消毒处理,检测是否正确清除SQLIA 漏洞。

本文的贡献包括以下两点:

(1)提出一种高效的面向Java Web 程序的SQL注入漏洞静态检测方法,基于匹配规则裁剪无效Source和Sink 对,然后综合污点分析和活跃变量分析裁剪不可能存在污点传播漏洞的Source和Sink 对;

(2)实现了静态检测原型工具TASAT(taint analysis static analysis tool),并结合程序插桩和Selenium 自动测试工具对静态检测结果进行动态验证,针对真实Web 程序的实验结果验证了方法的有效性。

1 分析方法

1.1 静态分析

静态分析阶段的总体框架如图1 所示。首先根据预定义策略提取所有可能的Source 和Sink 语句,然后对每条Source 语句进行分析,排除不是读取外部输入的Source,然后根据自定义匹配规则排除不可能发生SQLIA 的Source 和Sink 对,最后应用静态污点分析并结合活跃变量分析对剩余的Source 和Sink对进行反向分析,进一步排除不存在污点传播路径的Source 和Sink 对。原型系统TASAT 实现了静态分析阶段的预处理Source、过滤无关Source 和Sink、活跃变量分析这三个方法组件。

Fig.1 Overall framework of static analysis图1 静态分析的总体框架

1.1.1 预处理Source集合

根据Source 方法读取的参数是否对应实际的外部输入,将Source 语句分为原生Source(参数对应外部输入)和非原生Source(参数不对应外部输入)。

原生Source 的判断方法包括:基于Servlet 映射寻找来源action、基于action 确定来源Jsp 及对应表单、基于表单内容确定对应来源,如果来源为用户外部输入就可以判断Source为原生Source。

把所有原生Source 放入原生Source 集合,对于非原生Source,通过匹配方法、匹配Request 和匹配Session 寻找对应的原生Source,然后建立与原生Source的映射关系并加入非原生Source集合。

预处理Source 方法能够捕捉所有的原生Source以及和原生Source 形成映射关系的非原生Source,目前没有处理来自其他源(如Cookie)的非原生Source。

图2 的代码片段示例1 展示了原生Source 的判断过程,通过HandleLogin.java 的两条Source 语句获取Jsp 页面传递的值,第1 句获取参数“loginname”的对应值,同时赋值给loginname 变量,第2 句为获取参数“password”的对应值,赋值给password 变量。

首先基于Servlet 寻找来源action,将Servlet Information 的Servletclass 字段和Source 所在Java 类进行匹配,得知HandleLogin 类与Servlet 类名“myservlet.control.HandleLogin”相匹配,推导出Web 程序通过名为“login”的action 跳转到HandleLogin.java。

然后基于action 寻找来源Jsp,在Jsp_action 中匹配actions 字段包含“login”的记录,得到相应jsp_class字段值为org.apache.jsp.login_jsp(即login.jsp 编译后的class),推导出HandleLogin.java 中Source 的参数来自login.jsp。

最后搜索login_jsp 类的jspSevice 方法,寻找与login.jsp 中action 为“login”的表单内容对应的语句,发现名为“loginname”和“password”的input 语句并且相应value 值为隐含表示,说明找到Source 对应的原始外部输入,因此判断HandleLogin 类的两条Source都是原生Source。

示例2 展示了一个非原生Source 找到对应原生Source 的过程。首先按照与示例1 相同的方式找到Source 方法的参数对应的输入,位于lookShopCar.jsp中action 为“deletegood”的表单中,对应名为“delete”的input 语句,value 为“car.getNumber()”,表明Source为非原生Source。

Fig.2 Example of Source pre-process图2 Source预处理示例

接着搜索方法内其他Source 语句,读取Request对象属性语句和读取Session 对象属性语句,寻找可能的原生Source。示例中Session_get 集合为预先遍历程序得到的读取Session 对象属性的语句集合,其中lookShopCar.jsp 中的读取Session 对象属性“login”语句根据Session 对象属性匹配关系,将变量login 与HandleLogin.java 中的login 对象相关联,进一步在Handle-Login.java 中找到两个Source,第4 和第5 行都是原生Source,因此建立input 语句与这两个原生Source的映射关系。

1.1.2 过滤无关Source和Sink

如果直接简单地对每对Source 和Sink 进行数据流分析,会浪费大量计算资源,因为许多Source 和Sink 之间不存在任何的可执行路径。

根据Web 应用中Source 和Sink 所处位置和跨文件传播的特点,本文提出了一种多重匹配的预处理方法,在进行复杂数据流分析之前首先排除不可能存在可执行路径的Source 和Sink 对,从而减少了无谓的资源消耗。

多重匹配预处理方法包括:(1)相同方法匹配,判断Source 和Sink 是否位于相同文件的相同方法;(2)相同的Request 信息匹配,判断Source 和Sink 是否可以根据相同请求信息的Get 方法返回值和Set 方法参数匹配;(3)Session 信息匹配,判断Source 和Sink 是否可以通过Session 对象的属性匹配;(4)方法参数匹配,判断Source 是否作为方法调用语句的参数传递给Sink 所在方法。目前,匹配方法可以支持Request 和Session 对象属性的常量传播。

另外,针对匹配成功的Source 和Sink 对,如果Source 不是原生Source,那么需要将该Source映射的原生Source与Sink 进行匹配。

Fig.3 Example of multiple matching图3 多重匹配示例

图3 的第1~2 行给出了相同方法匹配的例子,Source 方法getParameter 和Sink 方法executeQuery 都位于login.jsp 文件的_jsp-Service 方法中。第3~6 行给出相同Request 信息匹配的例子,Source(第3 行)在Search-Cosmetic.java 中,而Sink(第6 行)在Search-Cosmetic.jsp 中,根据req 对象的getRequest-Dispatcher 方法调用的参数信息,Source 可以与SearchCosmetic.jps文件中的Sink进行关联匹配。第7~12 行给出Session 信息匹配的例子,Session信息匹配支持键值对中的常量传播,将变量lg 替换为对应的常量字符串“login”,根据session 对象信息的“login”属性,可以将HandleLogin.java 中的Source(第7 行)和GenerateOrder.java 中的Sink(第12 行)进行匹配。第13~15 行给出方法参数匹配的例子,Source 变量password 作为DaAccessMethods 类的getUserRole 方法调用的参数,而Sink 位于getUser-Role方法中,因此匹配。

1.1.3 污点传播规则

污点传播规则是后续活跃变量分析和动态验证的基础,在活跃变量分析中需要结合污点传播规则生成活跃的污点变量。

给出形式化描述污点变量信息使用的符号,status表示变量的污点状态取值集合,包括污染(tainted)和可信(trusted),locs 表示在语句中污点变量可能出现的位置集合:等式左值Lvalue、等式右值Rvalue、调用方法对象base 和方法参数arg_i,i 表示参数的下标顺序。vars 表示程序中出现的变量及访问路径集合,如a、a.f、a[i]、class.f 等。taint_info 记录各个位置上的变量是否被污染,taint_set 记录每条语句的所有污点变量的位置集合。Г函数跟踪记录每个变量和当前语句不同位置的污点状态,操作符∪计算两个污点状态值的并,两个相同值的并值不变,而trusted∪tainted=tainted。

污点状态信息使用的符号:

定义1Unit 使用了变量v,即use(Unit,v)成立当且仅当以下任意条件成立。

(1)Unit是AssignStmt且Rvalue不是InvokeExpr,v出现在Unit的Rvalue中。

(2)Unit 是AssignStmt 且Rvalue 是InvokeExpr:①Rvalue ∈InterfaceInvoke,v出现在Unit 的arg_i;②Rvalue ∈SpecialInvoke,v出现在Unit 的arg_i;③Rvalue ∈VirtualInvoke,v出现在Unit 中的base 或arg_i;④Rvalue ∈StaticInvoke,v出现在Unit的base或arg_i。

(3)Unit 是InvokeStmt,v出现在Unit 的base 或arg_i。

定义2Unit 定义了变量d,def(Unit,d)成立当且仅当以下任意条件成立。(1)Unit 是AssignStmt,d出现在Unit 的Lvalue;(2)Unit 是InvokeStmt,d没出现在Unit中且d是base对象的某个属性。

使用符号Use 存储所有use(Unit,v)为真的变量v,表示Unit 使用的变量集合,符号Def 存储def(Unit,d)为真的变量d,表示Unit 定义的变量集合。下面是一些在污传播规则中使用的变量。

(1)system_method:系统类方法集合。

(2)custom_method:自定义方法集合。

(3)method:被调用方法。

(4)taint_custom(method):处理自定义方法的方法内污点传播,如果method 有返回值,那么返回该值的污点状态。

(5)xX:对象的属性。

(6)SetXX:属性xX 的Set方法集合。

(7)GetXX:属性xX 的Get方法集合。

污点传播规则分为单文件传播和跨文件传播。单文件传播包括两类指令:

(1)赋值语句

污点信息由Rvalue 传播到Lvalue,即Г(Lvalue)=Г(Rvalue),Lvalue 和Rvalue 都包括多种类型,具体如下。

①a=C,Lvalue 是变量a,Rvalue 是常量C。常量赋值可以消除左值的污点状态,将污点变量验证为可信变量,对应式(1)。

②a=b,Rvalue 为变量b;a=bbinopc,Rvalue为二元表达式;a=unopb,Rvalue 为一元表达式;a=b.f,Rvalue 为对象实例的成员变量;a=class.f,Rvalue 为全局静态变量;a.f=b,Lvalue 为对象实例的成员变量;class.f=b,Lvalue 为全局静态变量。在上述任意情形,Rvalue的污点状态为所有当前Unit使用变量的污点状态值的并值,对应式(2)。

③a=b[i],Rvalue 为数组元素且索引为变量;a=b[constant],Rvalue 为数组元素且索引为常量。当右值为数组元素时,污点传播采用保守策略,Rvalue的污点状态值为所有在程序中出现的数组元素的污点状态值的并值,对应式(3)。

④b[i]=a,Lvalue 为数组元素且索引为变量;b[constant]=a,Lvalue 为数组元素且索引为常量。当Lvalue为数组元素时,只需要把Rvalue的污点状态值赋予Lvalue 即可,不需要修改其他数组元素。如果该元素是tainted,那么所有数组元素的并值一定也是tainted,如果该元素被验证为trusted,那么不影响其他元素的污点状态,从而保证污点传播的可靠性,对应式(4)。

⑤a=invoke_expr b.f(arg_1,arg_2...),将方法调用语句分为三类,一是对象属性的Get方法即GetXX,二是库方法system_method,三是自定义方法custom_method。GetXX 方法没有参数,直接返回属性b.xX的值,因此Lvalue 的污点状态等于b.xX 的污点状态值。如果system_method 的实例对象b 或任意参数的污点状态为tainted,Lvalue 的污点状态保守地设置为tainted。对于自定义方法,根据方法内污点传播taint_custom 计算返回值的污点状态,对应式(5)~式(7)。

⑥a=new_expr(arg_1,arg_2...),Rvalue 为创建对象表达式,如果表达式中的任意参数的污点状态为tainted,那么Lvalue 为tainted,否则为trusted,对应式(8)。

(2)方法调用语句

形如invoke b.f(arg_1,arg_2...),分为三类,一是对象属性的Set 方法即SetXX,二是库方法system_method,三是自定义方法custom_method。对于Set方法,修改对象b 的属性b.xX 为相应参数的污点状态。对于没有返回值的system_method,没有给出任何定义,忽略了库方法可能改变全局变量的污点状态,可能改变对象b 和参数对象的属性的污点状态,忽略了对象b 可能受到参数的污点传播而改变污点状态。对于custom_method,根据方法内污点传播taint_custom 改变其他变量的污点状态,对应式(9)。

对于传播方法taint_custom,静态分析和动态分析有所不同。静态分析采用标准的方法内数据流分析,根据赋值语句和方法调用语句定义的相应传播规则在控制流图中传播污点信息,遇见分支语句时进行变量的污点状态并值计算,如果存在环(即循环),则迭代分析至污点状态集合为最小不动点。动态分析伴随着指令的顺序执行,根据每条指令的污点传播规则动态修改相应变量的污点状态即可。

跨文件污点传播发生在待分析语句是Request和Session 对象的属性读写语句时,如getParameter 和setAttribute 等,根据属性的键值对信息传播污点。在单个文件污点传播过程结束后,根据跨文件传播规则获取目标文件的污点信息集合,然后对目标文件开启新一轮污点传播分析。跨文件污点传播在目标文件中仅保留和传播被读写的属性所对应的污点状态值,并以此作为单文件传播的初始taintset,开始目标文件的污点传播分析。

1.1.4 活跃变量分析

如果在每条从Source 到Sink 的执行路径上都存在一个程序点位置,在该位置上所有的活跃变量都是可信变量,或者在Source 点的活跃变量集合不包含Source 方法返回值变量,那么每条路径都不可能有污点信息传播到Sink,即Source 与Sink 之间不可能存在真实污点传播路径。

活跃变量分析与预定义污点传播规则相结合,仅记录可能是污点的活跃变量,即在标准活跃变量分析基础上,按照1.1.3 小节定义的污点传播语义,计算每条指令的活跃污点变量。如果污点变量通过方法调用参数传递,根据预定义的实参和形参的映射关系计算新的活跃变量;如果通过Request方法传递,根据Request 对象的键值对计算新的活跃变量;如果通过Session 对象的属性传递,根据Session 对象的属性和活跃变量的匹配关系计算新的活跃变量。

图4 给出了两个代码片段示例,说明活跃变量分析过程。在第5 行的Sink 语句位置,方法参数query是活跃污点变量,在第4 行活跃污点变量是query 和name,进而得知在第3 行只有name 是活跃污点变量,然后可以计算出在第2 行,s 是活跃污点变量,最后s是Source 方法的返回值,说明示例1 存在潜在的污点传播路径。

Fig.4 Example of live variable analysis图4 活跃变量分析示例

示例2 给出了3 种不存在污点传播执行路径的Source 和Sink 对,示例污点传播过程中的污点验证、Session 属性匹配和Request属性匹配。在第9 行Sink语句,方法参数s 是活跃污点变量,在第8 行,根据属性方法getLoginname 的污点传播语义,活跃污点变量变为lg.loginname,在第7 行,sanitize 方法对login对象进行了消毒处理后赋予lg 变量,此时不存在任何活跃污点变量,活跃变量分析中止,表明第1行Source 和第9 行的Sink 之间不可能存在污点传播路径。

在第14 行Sink 语句,query 是活跃污点变量,在第13 和第12 行,活跃污点变量分别是{query,name}和name。在第11 行,方法调用login.getLoginname 获取login 对象的loginname 属性,因此活跃污点变量是login.loginname,而不是login 对象。在第10 行,根据Session 对象属性名“login”匹配,污点变量追溯至文件1 中的login 对象的属性login.loginname(第4 行)。在第4 行,活跃污点变量还是login.loginname,第2 行设置login 对象的“password”属性为password 变量,活跃污点变量依然是login.loginname,与第1 行的Source 返回值变量password 无关,表明password 在Source 位置不活跃,即第1 行的Source 无法将污点变量传播到第14 行的Sink。

在第17 行的Sink 语句,login.password 为活跃污点变量,在第16 行对login 对象执行污点验证,此时活跃污点变量为空集,活跃变量分析中止,login 对象在第15 行根据Request 属性“login”匹配文件1 的login 对象(第5 行),表明第1 行的Source 与第17 行的Sink 不存在污点传播路径。

静态分析主要完成原生Source 的正确判断和转换处理,发现存在潜在SQL 注入漏洞的Source 和Sink 对,通过自定义的污点传播语义规则并使用活跃变量分析方法对它们进行反向数据流分析,排除不可能存在SQ 注入漏洞的Source 和Sink 对。预处理过程有效地过滤无关Source 和Sink 对,极大减少了待分析的Source 和Sink 对数量,进而提高了分析的性能。通过自定义一套完整的指令级污点传播语义规则,并结合经典活跃变量分析方法,进一步排除不可能存在SQLIA 漏洞的Source 和Sink 对,提高了分析的精确度。

1.2 动态验证方法

1.2.1 流程概述

动态验证基于污点传播规则对Jimple 中间代码进行插桩,执行插桩后程序获得污点传播的指令序列Trace,基于Trace 验证静态分析结果的Source 和Sink 之间是否存在真实污点传播路径。验证过程如图5 所示,首先将Java 和Jsp 文件编译为class,然后应用Soot 框架反编译为Jimple 中间代码,并基于污点传播规则对相应语句进行插桩,插桩代码用于动态跟踪污点变量的传播,接着将插桩后的Jimple 代码重新编译为class,最后打包为jar 文件并进行网站部署。应用自动化测试工具对网站进行测试,根据插桩代码动态跟踪的污点传播信息打印相应的代码片断Trace,然后将每条Trace 与静态分析结果的Source和Sink 进行匹配,判断是否存在真实污点传播路径。如果存在,尝试在源代码中增加验证方法,并重新执行静态分析,检测漏洞是否已经被安全消除。

1.2.2 插桩处理

动态插桩定义一个辅助类用于实现污点传播,其中设置不同的静态方法用于实现不同语句的污点传播语义。静态遍历程序的每条语句,根据污点传播规则判断其是否需要插桩,是则根据其类型进行相应插桩,即插入一条调用辅助类的相应方法的语句并传入相应参数。不用插桩的语句类型包括definitionstmt、ifstmt、return 和goto 等。

插桩根据不同类型的语句(Unit)调用不同类型的辅助方法,传入不同的方法参数,同时不影响原来代码的业务逻辑,对AssignStmt 和InvokeStm 类型语句的插桩方式如表1所示。对于AssignStmt类型分为Source、Sink、右值为InvokeExpr 和other 等情形进行处理,对于InvokeStmt 类型分为request_set、session_set、invokeexpr 等情形进行处理。传入参数Unit 指语句自身,left 指语句左值,base 指方法调用对象,arg 表示方法参数,itsClass 表示语句所属类,key 指request参数或session 属性名,value 表示key 对应的值,method 表示被调用方法。

Fig.5 Flowchart of dynamic verification图5 动态验证流程

Table 1 Statement instrumentation parameter example表1 语句插桩参数示例

对于AssignStmt 中的Source、Sink 和other 情形,left和arg参数用于跟踪污点变量传播,Unit和itsClass参数用于记录产生污点传播的语句。对于InvokeExpr、base 和method 参数,用于处理方法调用的污点传播。对于InvokeStmt 中的request_set 和session_set,key 和value 参数用于处理匹配Request 和Session 对象的键值对进行污点传播。

图6 展示了一段Java 代码转换为对应的Jimple代码以及根据Jimple 代码的特性进行插桩操作后的效果展示图。总共有3 段代码片段:第1 段为Java 代码。第2 段为Java 代码对应的Jimple 代码,在该段Jimple 代码中,第1 句为接口调用方法,调用Http-ServletRequest 接口对象的getParameter 方法来获得对应参数的值,第2 句为普通调用方法,调用String 对象的trim 方法来去除String 对象的字符串的头尾空白符,第3、4 句与第1、2 句类似。第3 段为插桩后得到的Jimple 代码,第2 和第4 句为插桩代码,在第2 段代码的每条语句后面插入一条调用DynamicUtil辅助类的静态方法的语句。第1 句是AssignStmt 类型的Source语句,插入静态findAndjudgeSource方法,传入unit、left、arg 和itsClass 参数进行污点传播。第3 句为AssignStmt 类型的InvokeExpr 语句,插入taintanalysis_assignstmt_invokeexpr 方法,并传入unit、left、base、method、arg 和itsClass等参数进行污点传播。

1.2.3 验证

Fig.6 Example of instrumentation result图6 插桩效果展示图

验证过程首先对原始Web 程序进行人工测试,尽量覆盖程序中所有可能执行数据库操作的路径,同时应用Selenium 工具录制浏览器与Web 程序的交互过程,录制的全部路径作为自动化测试模块对插桩后的Web 程序进行自动测试。

图7 给出了一条测试路径示例,从用户登录开始,包括浏览化妆品、加入购物车、查看购物车和用户退出。主要记录交互过程,即定位到特定元素并执行对应操作,如点击元素click 和元素赋值sendKeys等。

Fig.7 Example of test path图7 测试路径示例

验证过程通过重放执行录制的测试路径实现,在程序执行的同时,插入的代码执行动态污点分析并打印与污点传播相关的指令序列即Trace。然后,验证过程搜索每条Trace 的Source 和Sink 集合,判定是否与静态分析结果的Source 和Sink 对匹配,是则表明找到了对应Source 和Sink 的一条真实污点传播路径,同时验证了静态分析结果的正确性。

图8 是一条Trace 示例。第1 行是Source 语句,获取login.jsp 中“password”对应的外部输入并赋值给password 变量,password 变量成为污点变量。第2行,password 作为查询语句的一部分,在查询操作完成后将污点信息传播给ResultSet类型的变量rs,污点信息在第3 行从rs 传播至userid 变量。第4 行是session 对象的写属性语句,将污点变量userid 以键值对<“userid”,userid>的形式保存在session 对象中。在第5 行,通过session 对象的读属性语句将loing_jsp文件中的污点变量userid 传播给basket_jsp 文件中的字符串变量userid。第6 行是Sink 语句,污点变量userid 作为查询语句的一部分,可能造成相关数据表的数据泄漏。

Fig.8 Trace example图8 Trace示例

动态验证使用程序插桩技术,基于自定义的污点传播规则对Jimple 中间代码进行插桩,使用自动测试工具动态遍历执行插桩后程序的不同路径,针对程序运行后产生的Trace 进行跟踪验证,检查Source是否能够成功传播到相应Sink,进而判断漏洞是否真实存在。

静态分析结果的误报率较高,人工验证的效率很低,本文将静态分析与动态验证相结合,实现了静态分析的自动化和动态验证过程的半自动化,实验结果表明本文方法在大幅提高分析效率的同时保持了很好的准确率。

2 实验结果及分析

实验环境为Windows 7 64 位操作系统,CPU 为Intel®CoreTMi5-4200M,8 GB 物理内存,浏览器为加载SeleniumIDE 3.15.1 插件的Firefox。开发环境基于JDK 7、Soot-2.5.0 以及Kepler 和Mars 等Eclipse 工具,代码量为7 600 行,其中原生Source 判断及转换模块1 100行,多重匹配模块600行,污点规则定义及活跃变量分析模块代码量为2 300行,动态插桩模块1 600行。

实验整体框架如图9 所示,静态分析检测潜在SQL 注入漏洞,作为动态验证过程的数据集。动态验证通过验证数据集定位真实漏洞,排除误报,并对漏洞进行消毒处理。

Fig.9 Experimental overall framework图9 实验整体框架

实验数据集包括6 个网站,其中3 个漏洞测试网站为bodgeit、security-shepherd 和webgoat,3 个开源网站为contineo、cosmetic_shop 和puzzlemall。静态分析阶段的实验结果如表2 所示,经过多重匹配(P_decrement rate)后,需要验证的Source 和Sink 对的数量极大减少,最少减少88.6%,最多减少99.4%,平均减少98.7%。将活跃变量分析与污点传播相结合进一步消除不可能存在污点传播路径的Source 和Sink,最少减少6.2%,最多减少84.4%,平均减少69.6%,表明静态分析阶段能够有效排除无关Source和Sink 对,避免无谓的计算资源浪费。

动态验证的结果如表3 所示,Finds 表示成功验证的Source和Sink 对数量。bodgeit项目的15 个潜在漏洞中有9 个是Source 和Sink 位于相同方法,其余6个是经过Session 对象跨文件传递,动态验证确认了其中的12 个漏洞。contineo 项目的100 个潜在漏洞有89 个被成功地验证,都是通过方法调用进行跨文件污点传播。cosmetic_shop、security_shephred 和puzzlemall 等项目的潜在漏洞全部被成功地验证。webgoat 项目潜在的30 个漏洞有25 个被成功验证。所有数据集中无法验证的漏洞都是因为Selenium 没有生成相应执行路径导致,这是由于动态测试无法做到100%覆盖测试路径。

Table 2 Static analysis results表2 静态分析实验结果

Table 3 Results of dynamic verification表3 动态验证结果

Table 4 Comparison of time performance表4 时间性能比较

将分析结果与经典Java EE 程序分析工具ANDROMEDA[10]进行对比,结果如表3 所示。在ANDROMEDA 的数据集中,因为只成功运行了contineo 和webgoat 两个项目,所以表3 和表4 只针对这两个项目比较准确率和时间性能。表4 以静态分析得到的Source 和Sink 对作为潜在漏洞总数,以动态验证确认的Source 和Sink 对作为Findings,得出TP和FP,准确率P=TP/(TP+FP),表示动态验证通过的漏洞数量与潜在漏洞数量的比值,虚警率FPR=FP/(FP+TP),表示动态验证未通过的漏洞数量与潜在漏洞数量的比值,T/F=Time/Findings,表示找到一个Finding 的平均时间。ANDROMEDA 的有关数据来自文献[10]。

对于contineo,本文方法和ANDROMEDA 的TP和FP接近,对于webgoat,本文方法的TP和FP都要好于ANDROMEDA,上述结果表明本文方法的准确率优于ANDROMEDA,因为本文方法专注分析SQL 注入漏洞,并且将静态分析与动态验证相结合以排除虚警,而ANDROMEDA 是单纯的静态分析工具,用于分析多种Web 程序漏洞,在保证可靠性的同时虚警率会上升。

FN 表示静态分析没有发现的漏洞数量,在本文中表示没有和原生Source 形成映射关系的非原生Source 数量。召回率R=TP/(TP+FN),表示验证通过的漏洞数量与所有实际漏洞数量的比值。综合评价指标F=2PR/(P+R),表示准确率和召回率的加权调和平均,综合了准确率和召回率的结果,有利于对检测结果综合性能的评价[15]。

ANDROMEDA 和本文方法的综合情况对比如表5 所示,由于ANDROMEDA 论文中没有对应的召回率数据,因此将其保守地设置为1。对于contineo项目,ANDROMEDA 和本文方法的误报率和综合评价相差无几,对于webgoat 项目,本文方法的误报率显著低于ANDROMEDA,准确率和综合评价指标更好。总体来看,本文方法的误报率明显低于ANDROMEDA,具有更好的准确率,同时召回率也维持在一个较高水平,综合评价指标也略优于ANDROMEDA,说明本文方法具有更好的检测效果。

时间性能以平均Time/Finding 进行比较,即找到一个漏洞所用的平均时间,如图10 所示。ANDROMEDA 在两个项目中总计找到297 个漏洞,花费848 s,平均为2.85 s,本文方法在6 个项目中共找到220 个漏洞,花费598 s,平均为2.71 s,两者的时间性能相差无几。

Table 5 Comparison of comprehensive performance表5 综合性能比较

Fig.10 Average time for a finding图10 Finding 平均时间

3 结束语

本文提出了一种面向SQLIA 漏洞的静态检测和动态验证方案。静态分析阶段首先搜索程序中所有的Source 和Sink,然后对Source 集合进行预处理,区分原生Source 和非原生Source,同时定位和映射非原生Source 对应的原生Source。接着根据Request 和Session 对象属性,采用多重匹配方法,寻找存在潜在污点传播路径的Source和Sink,然后应用活跃变量分析结合污点传播,排除不可能存在污点传播路径的Source 和Sink,极大减少了待分析的潜在漏洞数量。动态验证首先对程序插桩,插入污点传播跟踪代码,然后应用seleniumIDE 插件对原始Web 项目产生自动化测试路径,接着对插桩后的Web 项目重放这些测试路径并根据生成的Trace 验证潜在漏洞是否真实。最后尝试对传播的污点变量进行可信验证,阻断污点传播路径从而修复漏洞。针对真实项目的实验结果表明了方法的有效性,同时,本文方法与经典工具ANDROMEDA 对比,在时间性能和准确度上均有一定提高。

方法不足之处主要有:(1)在Source 预处理和多重匹配时,目前只能处理action 和属性名为常量字符串的情况,无法处理对于运行时确定的action 和属性名,解决该问题需要引入字符串分析技术。(2)库方法的建模采用保守方式,可能影响污点传播的精确度,拟应用污点摘要定义库方法的污点传播语义。(3)动态验证采用人工测试并自动重放执行路径,存在路径覆盖率低的问题,未来拟引入模糊测试和动态符号执行技术改善路径覆盖。

猜你喜欢
污点静态语句
最新进展!中老铁路开始静态验收
静态随机存储器在轨自检算法
黑蚂蚁
猜猜他是谁
污点
基于HTML5静态网页设计
一节生物课的课堂生成
基本算法语句
我喜欢
作文语句实录