摘要

我们提出了一种静态分析算法,用于检测PHP中的安全漏洞,PHP是一种用于构建Web应用程序的流行服务器端脚本语言。我们的分析采用了一种新颖的三层体系结构以在内部块,过程内和过程间级别以更低的粒度级别捕获信息。这种架构使我们能够处理脚本语言的动态特性,这些特性尚未被先前技术充分解决。
我们在六个流行的开源PHP代码库中证明了我们的方法的有效性,发现了105个以前未知的安全漏洞,其中大多数我们认为可以远程利用。

介绍

近年来,基于Web的应用程序迅速扩散,并已成为提供从论坛到银行和零售等安全敏感领域的在线服务的事实标准
因此,这些应用程序中的安全漏洞对这些服务的提供者和用户都构成了越来越大的威胁。在2004年下半年,赛门铁克编制了670个影响Web应用程序的漏洞,比2003年同期增加了81%。在可预见的未来,这种趋势可能会持续下去。

根据同一报告,这些漏洞通常是由输入验证中的编程错误和提交的请求的不当处理引起的
由于漏洞通常深深嵌入程序逻辑中,因此传统的网络级防御(例如防火墙)无法提供足够的保护来抵御此类攻击。测试也基本上无效,因为攻击者通常使用最不期望的输入来利用这些漏洞并危及系统。

一种自然的替代方法是使用静态分析找出这些错误。这种方法已经在WebSSARI 和Minamide 中进行了探索。 WebSSARI已被用于在PHP脚本中查找许多安全漏洞,但由于其基于过程内类型的分析而具有大量误报和否定。 Minamide的系统检查PHP脚本的HTML输出的语法正确性,似乎不能有效地发现安全漏洞。

本文的主要信息是,脚本语言的分析不需要比传统语言的分析困难得多。虽然脚本语言强调静态分析的不同方面,但是为解决脚本语言的重要方面而设计的分析可以可靠地识别脚本中的许多严重漏洞并具有高度自动化。鉴于脚本在现实世界应用程序中的重要性,我们认为静态分析有机会在这个新领域产生重大影响。

在本文中,我们应用静态分析来发现PHP中的安全漏洞,PHP是一种服务器端脚本语言,已成为开发Web应用程序最广泛采用的平台之一.我们的目标是自动发现严重漏洞的错误检测工具很有信心。然而,这项工作并不旨在验证缺少错误。

本文做出以下贡献:

(1)我们提出了一个PHP的过程间静态分析算法。像PHP一样动态的语言为静态分析提出了独特的挑战:语言结构(例如,include )允许动态包含程序代码,类型在执行期间发生变化的变量,具有依赖于操作数的运行时类型的语义的操作(例如, <),并且普遍使用散列表和正则表达式匹配只是必须很好地建模以产生有用结果的一些特征。为了忠实地模拟这种语言中的程序行为,我们使用三层分析来捕获在块内,过程内和过程间级别上降低粒度级别的信息。这种体系结构允许在最重要的地方分析如 在内部块中,并且在较小程度上,在过程内层面,并且在函数调用的自然抽象边界处使用激进抽象来实现可伸缩性。我们使用符号执行来模拟基本块内的动态特征,并使用块概要来隐藏程序内和程序间分析的复杂性。我们相信相同的技术可以很容易地应用于其他脚本语言(例如,Perl)。

(2)我们将展示如何使用我们的静态分析算法来查找SQL注入漏洞。配置完成后,分析将完全自动完成。虽然我们在这项工作中专注于SQL注入,但是可以应用相同的技术来检测其他漏洞,例如跨站点脚本(XSS)和Web应用程序中的代码注入。

(3)我们通过实施分析算法并在六个用PHP编写的流行Web应用程序上运行它来实验验证我们的方法,找到105个以前未知的安全漏洞。我们分析了两个报告的PHP融合漏洞,这是一个成熟的,广泛部署的内容管理系统,并为两者构建漏洞,允许攻击者控制或破坏系统。

背景

本节简要介绍PHP语言,并显示PHP中SQL注入漏洞的示例。PHP是十年前由Rasmus Lerdorf创建的,它是一组简单的Perl脚本,用于跟踪对其在线简历的访问。它已经发展成为用于构建Web应用程序的最流行的服务器端脚本语言之一。根据最近的安全调查显示,PHP安装在44.6%的Apache Web服务器上,被数百万开发人员采用,并由雅虎,IBM,甲骨文和SAP等人使用或支持。

尽管PHP语言在过去十年中经历了两次重大的重新设计,但它保留了类似Perl的语法和动态(解释)特性,这有助于其最简单和灵活的声称优势。PHP有一套编程结构和特殊操作,可以简化Web开发。我们举三个例子:

1.与SQL自然集成:

PHP为数据库操作提供了几乎原生的支持。例如,在字符串中使用内联变量,大多数SQL查询可以通过简单的函数调用简明地表达

$rows=mysql query("UPDATE users SET
pass=‘$pass’ WHERE userid=‘$userid’");

将此代码与Java进行对比,其中通常通过预准备语句访问数据库:一个创建语句模板并使用绑定变量填充值(及其类型):

PreparedStatement s = con.prepareStatement("UPDATE users SET pass = ?WHERE userid = ?");
s.setString(1, pass); s.setInt(2, userid);
int rows = s.executeUpdate();

2.动态类型和来自字符串的隐式转换:

PHP与其他脚本语言一样,广泛支持字符串操作以及字符串和其他类型之间的自动转换。这些功能对于Web应用程序很方便,因为字符串充当浏览器,Web服务器和数据库后端之间的公共介质。例如,我们可以在没有显式强制转换的情况下将数字转换为字符串:

if ($userid < 0) exit;
$query = "SELECT * from users
WHERE userid = ‘$userid’";

3.变量范围和环境:

PHP有许多机制可以在从执行环境访问值时最大限度地减少冗余。例如,HTTP get和post请求会自动导入到全局名称空间中作为哈希表$_GET$_ POST。要访问提交表单的“name”字段,可以直接在程序中使用$_GET ['name']
如果这听起来仍然太多,那么PHP提供了一个提取操作,可以自动将哈希表的所有键值对导入当前范围。在上面的示例中,可以使用extract(_GET,EXTR_OVERWRITE)来导入使用HTTP get方法提交的数据。要访问$name字段,现在只需键入$name,某些人更喜欢$_GET['name']

但是,这些便利性具有安全隐患:

1.SQL注入变得简单:

Java中的绑定变量可以确保程序员传递给SQL查询的任何数据都是数据。对于PHP示例,不能说同样的情况,来自恶意攻击者的格式错误的数据可能会改变SQL语句的含义并导致对数据库的意外操作。这些通常称为SQL注入攻击。

在上面的示例中(情况1),假设$userid由攻击者控制并具有值’ OR ‘1’ = ‘1 ,查询字符串变为

UPDATE users SET pass=’...’
WHERE userid=’’ OR ’1’=’1’

它具有更新数据库中所有用户的密码的效果

2.隐式转换:

请看一下

人们会期望,如果程序打印任何东西,它应该是“0”。不幸的是,PHP在将字符串值与整数进行比较之前会隐式地将其转换为数字。非数值(例如,“abc”)在没有投诉的情况下转换为0,因此上面的代码可以打印除非零数字之外的任何内容。如果随后使用$userid,我们可以想象一个潜在的SQL注入漏洞

3.用户控制下的未初始化变量:

在PHP中,未初始化的变量默认为null。有些程序依靠这个事实来做出正确的行为;考虑以下代码:

extract($_GET, EXTR_OVERWRITE);
for ($i=0;$i<=7;$i++)
$new pass .= chr(rand(97, 122)); // append one char
mysql query("UPDATE . . . $new_pass . . .");

该程序生成随机密码并将其插入数据库。但是,由于第1行的提取操作,恶意用户可以通过在提交的HTTP表单数据中添加意外的新传递字段来为$new_pass引入任意初始值。

CFG := build control flow graph(AST);
foreach (basic block b in CFG)
summaries[b] := simulate block(b);
return make function summary(CFG, summaries);

分析

给定一个PHP源文件,我们的工具按以下步骤执行静态分析:

(1)我们将PHP源解析为抽象语法树(AST)。我们的解析器基于PHP 5.0.5的标准开源实现。每个PHP源文件都包含一个主要部分(以下称为主要部分,虽然它不是任何函数定义的一部分)和零个或多个用户定义的函数。我们将用户定义的函数存储在环境中,并从main函数开始分析

(2)单个函数的分析总结在上面 CFG 的代码示例中。对于程序中的每个函数,分析执行从函数体的抽象语法树(AST)到控制流图(CFG)的标准转换。 CFG的节点是基本块:最大单个条目,单个退出语句序列。CFG的边缘是块之间的跳转关系。对于条件跳转,相应的CFG边缘用分支谓词标记。

(3)使用符号执行来模拟每个基本块。目标是理解块中语句对程序全局状态的集体影响,并将它们的效果总结为简明的块概要(其中描述了在进入块之前必须清理的变量集3)。 。我们在3.1节描述了仿真算法。

(4)在计算每个基本块的摘要之后,我们使用标准可达性分析将块摘要组合成函数摘要。函数摘要描述了函数的前后条件(例如,在调用当前函数之后的一组已清理的输入变量)。我们将在3.2节讨论这一步骤。

(5)在分析函数期间,我们可能会遇到对其他用户定义函数的调用。我们将在3.3节讨论建模函数调用以及函数分析的顺序。

模拟基本块

概述

图2给出了伪代码,概述了符号模拟过程。

此处输入图片的描述

回想一下每个基本块包含一个线性的语句序列,中间没有跳跃或跳跃目标。模拟以初始状态开始,其将每个变量x映射到符号初始值x0。它按顺序处理块中的每个语句,更新模拟器状态以反映该语句的效果。模拟继续进行,直到遇到以下任何一种情况:

1.块的结束;
2.返回声明。在这种情况下,当前块被标记为返回块,并且模拟器评估并记录返回值;
3.退出声明。在这种情况下,当前块被标记为出口块;
4.调用退出程序的用户定义函数。使用被叫方的功能摘要自动确定此条件(参见第3.2和3.3节)。

请注意,在最后一种情况下,程序的执行也已终止,因此我们从当前块中删除任何后续语句和传出CFG边缘。
在模拟基本块之后,我们使用模拟器最终状态中包含的信息来概括块对块汇总的影响,我们将其存储在过程内分析中使用(参见第3.2节)。模拟后,状态本身被丢弃。

以下小节详细描述了模拟过程。我们首先定义我们建模的PHP子集(第3.1.2节),并讨论在符号执行期间模拟状态和程序值(第3.1.3节,第3.1.4节)的表示。使用值表示,我们描述了分析器如何模拟表达式(§3.1.5)和语句(§3.1.6)。最后,我们描述我们如何表示和推断块摘要(第3.1.7节)。

语言

此处输入图片的描述

图3给出了一个小命令式语言的定义,该语言捕获了我们认为与SQL注入漏洞相关的PHP构造的子集。与PHP一样,语言是动态类型的。我们模拟三种基本类型的PHP值:字符串,布尔值和整数。

另外,我们介绍一个特别的用于描述静态类型未确定的对象的类型(例如,输入参数)

表达式可以是常量,l-value,一元和二元运算以及类型转换。值得一提的是左值的定义,因为除了变量和函数参数之外,我们还包含一个命名的下标操作,以便对PHP程序中广泛使用的数组和哈希表访问提供有限的支持。
语句可以是赋值,函数调用,返回,退出或包含。前四种语句类型无需进一步说明。

include语句是脚本语言独有的常用功能,它允许程序员动态地将代码插入到程序中。在我们的语言中,include计算其字符串参数,并执行由字符串指定的程序文件,就像它插入该程序点一样(例如,它共享相同的范围)。我们将在3.1.6节描述如何模拟这种行为。

陈述

此处输入图片的描述

图4(a)给出了模拟过程中值和状态的定义。模拟状态将存储器位置映射到它们的值表示,其中存储器位置是程序变量(例如x),或者是通过另一个位置(例如x[key])访问的散列表中的条目。请注意,位置的定义是递归的,因此我们的算法支持多级散列解引用。

在进入函数时,每个位置L被隐式初始化为符号初始值L0,其构成模拟的初始状态。我们在状态中表示的值可以根据类型分为三类:

字符串:

字符串是许多脚本语言中最基本的类型,建模字符串的精度直接决定了分析的精确度。字符串通常通过串联构造。例如,用户输入(通过HTTP get和post方法)通常与预先构造的骨架连接以形成SQL查询。同样,查询的结果可以与HTML模板连接以形成输出。对连接进行建模可以使分析更好地理解脚本中的信息流。因此,我们的字符串表示基于连接。字符串值表示为字符串段的有序串联,可以是以下之一:字符串常量,进入当前块(l0)时的内存位置的初始值,或包含初始值零的字符串来自一组内存位置的更多元素(包含(σ))。我们使用最后一个表示来模拟函数调用的返回值,这些函数调用可能不确定地包含全局变量和输入参数的组合。例如,在

function f($a, $b) {
    if (. . .) return $a;
    else return $b;
}
$ret = f($x.$y, $z);

我们将第5行的返回值表示为包含({x,y,z}),以模拟它可以包含集合中的任何元素作为子字符串的事实。

上面描述的字符串表示具有以下好处:

首先,我们获得当前块内的字符串的自动常量折叠,这通常用于解析散列键和区分散列引用(例如,在$key = “key”中;return $hash[$key])。

其次,我们可以通过在后者的最终表示中查找前者的初始值的出现来跟踪一个输入变量的内容如何流入另一个输入变量。例如,在:$a = $a.$b$a的最终表示是<a0,b>我们知道如果$a$b在进入当前块时包含未经过授权的用户输入,退出时$a也是如此。
最后,通过使用contains(σ)跟踪基于函数摘要的函数返回值,可以实现过程间数据流。我们在3.3节中更详细地描述了这个方面。

布尔值:

在PHP中,执行输入验证的常用方法是调用一个返回true或false的函数,具体取决于输入是否格式正确。例如,以下代码清理$userid

$ok = is safe($userid);
if (!$ok) exit;

调用后布尔变量$ok的值未确定,但它与$userid的安全性相关。这激发了解释(σ0,σ1)作为此类布尔值的表示:σ0(相应的σ1)表示布尔值为假时的有效l值的集合(resp,true)。在上面的示例中,$ok表示untaint({},{userid})

除此之外,布尔的代表还包括常量(true and false)和 unknown

整数:

我们的模拟中不太强调整数运算。我们跟踪整数常量以及它们之间的二元和一元运算。我们还支持从整数到布尔值和字符串值的类型转换。

位置和L值

在图3中的语言定义中,散列引用可以通过赋值进行别名,而L值可以包含具有非常量键的散列访问。取决于主机和密钥的值,相同的L值可以指代不同的存储器位置,因此,L值不适合作为模拟状态中的存储器位置。

图4(b)给出了我们用于将L值解析为内存位置的规则。

此处输入图片的描述

var和arg规则将每个程序变量和函数参数映射到由其名称标识的内存位置,并且dim规则通过首先将哈希表评估到位置然后附加密钥以形成哈希条目位置从而解析哈希访问。这些规则旨在在存在简单别名的情况下工作。考虑以下程序:

$hash = $_POST;
$key = ’userid’;
$userid = $hash[$key];

该程序首先为哈希表$_POST创建一个别名($hash),然后使用该别名访问userid条目。在进入块时,初始状态将每个位置映射到其初始值:

此处输入图片的描述

根据var规则,每个变量映射到其自己的唯一位置。在前两个任务之后,状态是:

此处输入图片的描述

我们使用dim规则解决第3行的$hash[$key]$hash计算为_POST0$key计算为常量字符串’userid’。因此,L值$hash[$key]计算到位置_POST[userid],因此分析将所需值_POST[userid]0分配给$userid

表达

我们基于上述值表示执行表达式的抽象评估。由于PHP是一种动态类型语言,因此操作数会隐式转换为表达式中的操作的适当类型。

图4(c)给出了模拟PHP中的强制转换操作的强制转换规则的代表性示例。例如,布尔值true,在字符串上下文中使用时,计算结果为“1”。另一方面,false被转换为空字符串而不是“0”。在无法精确表示的情况下,结果是unknown

图4(c)还给出了三种用于评估表达式的代表性规则。第一个规则处理L值,并通过首先将L值解析为内存位置,然后在评估上下文中查找位置(在进入块时回想起Γ(L)= L0)来获得结果。

第二个规则模拟字符串连接。我们首先将两个操作数的值转换为字符串值,结果是两者的串联。

最终规则处理布尔否定。有趣的案例涉及不明确的值。回想一下,如果对集合σ0(相应的σ1)中的L值进行消毒,则untaint(σ0,σ1)表示未知的布尔值,该值为false(resp.true)。根据这个定义,unaint(σ0,σ1)的否定是不明确的(σ1,σ0)。

表达式的分析是不明确的,如果我们无法确定更精确的表示,这可能是漏报的潜在来源。

实验结果

第3节中描述的分析已经实现为两个独立的部分:基于开源PHP5.0.5发行版的前端,将源文件解析为抽象语法树,以及用OCaml编写的后端,将AST读入内存并进行分析。这种分离可确保最大的兼容性,同时最小化对PHP 实现的依赖。

在intrablock,intraprocedural和interprocedural级别中使用不同抽象级别的决定使我们能够微调我们保留在一个级别的信息量,而不依赖于另一个级别的算法,并允许我们快速构建可用工具。
检查器很大程度上是自动的,几乎不需要人为干预。我们使用一小组查询函数(例如mysql查询)和清理操作(例如,数字)来为检查器播种。检查员自动推断其余部分。

正则表达式匹配对自动化提出了挑战。正则表达式用于各种目的,包括但不限于输入验证。
一些正则表达式匹配格式良好的输入,而其他正则表达式检测格式错假设一种方式或另一种方式导致误报或漏报。

我们的解决方案是维护一个先前看到的正则表达式及其效果的数据库(如果有的话)。
默认情况下,以前看不见的正则表达式假定没有过滤效果,以免因错误判断而错过任何错误。为了使用户能够轻松指定正则表达式的清理效果,检查器具有交互模式,当分析遇到以前看不见的正则表达式时会提示用户,并记录用户的答案以供将来参考

用户声明正则表达式的规则有将错误引入分析的真正潜力;然而,实际上,我们发现这种方法非常有效,它帮助我们找到至少两个由于过于宽松的正则表达式而导致的漏洞。

们的工具在所有实验中收集了来自用户的49个正则表达式的信息(用户回答每次查询一次击键),因此用户的负担很小。

检查器通过使用主函数摘要中的信息来检测错误 - 检查器将所有在进入时需要被检测的变量标记为潜在的安全漏洞。从检查器的角度来看,这些变量在环境中定义,用于构造SQL查询而不进行清理。但实际上,这些变量要么由运行时环境定义,要么由检查器不完全理解的某些语言结构定义(例如,我们在下面的案例研究中描述的PHP中的提取操作)。该工具发出错误信息,如果已知变量由用户控制(例如$_GET['...']$_POST ['...']$_COOKIE['...']等)。对于其他,检查器会发出警告。

案例研究:PHP融合中的两个可利用的SQL注入攻击

在本节中,我们将展示两个关于PHP融合中可利用SQL注入漏洞的案例研究

我们的工具。 PHP-fusion是一个基于PHP和MySQL的开源内容管理系统(CMS)。它不包括特定于语言环境的自定义模块,它包含超过16,000行PHP代码,并且由于其速度,可定制性和丰富的功能而具有广泛的用户群。
浏览代码时,很明显,作者在编写安全性时考虑了安全性,并且在使用查询字符串之前已经特别注意清理输入

我们的实验是在最新的6.00.204版软件上进行的。与我们检查的其他代码库不同,PHP-fusion使用提取操作将用户输入导入当前环境。例如, extract($_POST,EXTR_OVERWRITE)具有将$_POST哈希表中的每个键的一个变量引入当前范围,并将$_POST[key]的值赋给该变量的效果。此功能减少了键入,但会导致检查器和安全漏洞混淆到软件中我们构建的两个漏洞都涉及使用未初始化的变量,其值可以由用户操作,因为提取操作。

由于PHP-fusion不直接从输入哈希值(如$_GET or $_POST)读取用户输入,因此我们的工具不会生成直接的错误消息。相反,我们检查警告(回想上面关于错误和警告的讨论),这些警告对应于安全敏感变量,其定义未被检查器解析(例如,通过提取操作引入,或从配置文件中读取)。

我们在PHPfusion中的所有顶级脚本上运行了我们的检查器。
该工具生成了22个唯一的警告,其中大部分与构建大量查询时使用的配置变量相关过滤掉后,4个不同文件中的7个警告仍然存在。我们相信7个警告中除了一个之外的所有警告都可能导致可利用的安全漏洞。唯一的误报来自于意外的过滤:

arises from an unanticipated sanitization:
/* php-files/lostpassword.php */
if (!preg match("/ˆ[0-9a-z]{32}$/", $account))
$error = 1;
if (!$error) { /* database access using $account */ }
if ($error) redirect("index.php");

程序不是根据 preg_match 的结果立即终止程序,而是将$error标志设置为true并延迟错误处理,这通常不是一个好习惯。可以通过在块摘要中添加更多信息来处理这个习惯用法。我们调查了剩余的潜在攻击警告的前两个,并确认两者在测试安装中确实可以利用

不出所料由于提取操作,两个错误都成为可能。我们在下面详细解释这两个错误

1)用于恢复丢失密码的脚本中的漏洞

这是一个可远程利用的漏洞,允许任何注册用户通过精心构建的URL提升其权限。我们在下面显示相关代码:

/* php-files/lostpassword.php */
for ($i=0;$i<=7;$i++)
$new pass .= chr(rand(97, 122));
. . .
$result = dbquery("UPDATE ".$db prefix."users
SET user_password=md5(’$new_pass’)
WHERE user_id=’".$data[’user_id’]."’");

我们的工具发出了$new_pass的警告,在进入时未初始化,因此在正常执行期间默认为空字符串。该脚本继续向$new_pass(第23行)添加七个随机生成的字母,并将其用作用户的新密码(第5-7行)。正常执行下的SQL请求采用以下形式:

UPDATE users SET user password=md5(’???????’)
WHERE user id=’userid’

但是,恶意用户只需将新的传递字段添加到其HTTP请求中,例如,将以下字符串附加到密码提醒站点的URL:

&new_pass=abc%27%29%2cuser_level=%27103%27%2cuser_aim=%28%27

上面描述的提取操作将在当前变量范围内神奇地引入$new_pass,并带有以下初始值:

abc'), user level ='103', user aim = ('

SQL请求现在构造为:

UPDATE users SET user password=md5(’abc’),
user level=’103’, user aim=(’???????’)
WHERE user id=’userid’

这里密码设置为 “abc”,用户权限提升到103,这意味着”超级管理员”。新推出的用户现在可以自由操作网站上的任何内容。

2)消息传递子系统中的漏洞

此漏洞利用了另一种可能未初始化的变量$result的使用,其中消息传递子系统中的消息ID。我们在图5中显示相关代码。

此处输入图片的描述

我们的工具警告在消息ID中未使用$result的结果。在正常输入时,程序使用级联if语句初始化$result where message id。如代码所示,作者非常谨慎地清理用于构造消息id的$result的值。但是,if语句的级联序列没有默认分支。因此,$result可能在格式错误的输入上未初始化消息ID。我们利用这个事实,并追加

&request where message id=1=1/*

因此,在第11-13行提交的查询字符串变为:

DELETE FROM messages WHERE 1=1 /* AND . . .

无论如何,”/ *:”在MySQL中被视为注释,因此被忽略。结果是丢失了系统中的所有私人消息。由于复杂的控制和数据流,不太可能通过代码审查或测试发现此错误。

我们向PHP-fusion的作者报告了这两个漏洞,他们立即修复了这些漏洞并发布了该软件的新版本。

相关工作

静态技术

WebSSARI是一种基于类型的PHP分析器。它使用简单的过程内污点分析来查找用户控制的值流入需要可信输入的函数(即敏感函数)的情况。该分析依赖于三个用户编写的“前奏”文件来提供有关以下方面的信息:

1)所有敏感功能的集合 - 需要过滤输入;
2)所有无操作的操作集;
3)不可信输入变量的集合。

不完整的规范导致大量的误报和漏报。

####WebSSARI有几个关键限制,限制了工具的精度和分析能力:

WebSSARI使用过程内算法,因此仅模拟不跨越功能边界的信息流。

大型PHP代码库通常使用少量系统库函数(例如,mysql_query)定义处理常见操作(例如,查询字符串构造,认证,清理等)的许多应用程序特定子例程。我们的算法能够自动推断这些用户定义函数的信息流和前后条件,而WebSSARI依赖于用户来指定每个用户的约束,这是每个检查的源代码库需要重复的重大负担。 3.3节中的示例表示WebSSARI无法在没有注释的情况下进行建模的一些常见用户定义函数形式。

为了显示过程间分析有多少提高了分析的准确性,我们关闭了函数摘要,并重复了我们在News Pro上的实验,这是五个代码库中最小的一个。这一次,分析产生了19条错误消息(而不是8条过程间分析)。
经检查,由于用户定义的清理操作,所有11个额外报告都是误报。

WebSSARI似乎没有对条件分支进行建模,条件分支代表了我们分析的脚本中最常见的清理形式之一

例如,我们认为它将在以下代码上报告错误警告:

if (!is numeric($ GET[’x’]))
exit;
mysql query(‘‘. . . $ GET[’x’] ...’’);

此外,过程间条件清理(参见3.1.6节中的示例)在代码库中也很常见。

WebSSARI使用基于静态类型的算法,该算法不专门为脚本中的动态特征建模。

例如,动态类型可能会引入WebSSARI错过的细微错误。在PHP脚本中广泛使用的include语句动态地将代码插入到程序中,该代码可能包含,诱导或防止错误。

Livshits和Lam 开发了一个静态检测器,用于Java应用程序中的安全漏洞(例如,SQL注入,跨站点脚本等)。该算法使用基于BDD的上下文敏感指针分析来发现从不可信源(例如,用户输入)到信任接收器(例如,SQL查询)的潜在流。此分析的一个限制是它不会对程序中的控制流进行建模,因此可能会错误标记随后流入SQL查询的已清理输入。使用条件分支的清理在PHP程序中很常见,因此忽略控制流的技术可能会在此类代码库中导致大量误报。

其他在C代码上证明有效的污点分析包括CQual,MECA 和MC。他们总共在Linux内核中发现了数百个以前未知的安全错误。

克里斯滕森等人开发一个字符串分析,使用无上下文语法来近似Java程序中的字符串值。结果扩展为常规语言,并根据预期输出的规范进行检查,以确定语法正确性。但是,语法正确性并不需要安全性,因此不清楚如何使这项工作适应SQL注入漏洞的检测。

Minamide扩展了该方法,并为PHP构建了一个字符串分析器,引用了SQL注入检测作为一种可能的应用程序。但是,分析器在PHP中模拟一小组字符串操作(例如,连接,字符串匹配和替换),并忽略更复杂的功能,如动态类型,转换和谓词。此外,框架似乎只是用字符串替换来模拟清理,字符串替换代表实际代码中所有清理的一小部分。因此,准确精确定位注射攻击仍然具有挑战性。

古尔德等人将字符串分析与类型检查相结合,不仅可以确保语法正确性,还可以确保Java程序构造的SQL查询的类型正确性。但是,类型正确性并不意味着安全性,这是我们分析的重点。

动态技术

Scott和Sharp 提出了一个应用级防火墙来集中客户端输入的清理。防火墙产品也可以从NetContinuum,Imperva,Watchfire等公司商购。

其中一些防火墙检测并防范先前已知的攻击模式,而其他防火墙则保留有效输入的白名单。这里的主要限制是前者容易受到误报和漏报,后者依赖于正确的规范,这很难得到。

Perl污染模式在不安全的环境中执行期间启用一组特殊的安全检查。它防止在需要可信输入的操作(例如,调用子shell的任何命令)中使用不受信任的数据(例如,所有命令行参数,环境变量,从文件读取的数据等)。
Nguyen-Tuong 提出了PHP的污点模式,与Perl污点模式不同,它不定义清理操作。相反,它会单独跟踪用户输入中的每个字符,并使用一组启发式方法来确定查询在包含用户输入片段时是否安全。例如,如果操作符号(例如,“(”,“)”,“%”等)被标记为污染,则它检测注入。

这种方法容易受到误报和影响。请注意,静态分析也容易受到误报和漏报的影响。关键的区别在于,在静态分析中,不准确性在编译时解决,而不是在运行时解决,这是不太容忍的。

结论

我们提出了一种静态分析算法,用于检测PHP中的安全漏洞。我们的分析采用了一种新颖的三层架构,使我们能够处理脚本语言独有的动态特性,如动态类型和代码包含。我们通过在六个流行的开源PHP代码库上运行我们的工具并找到105个以前未知的安全漏洞来证明我们的方法的有效性,其中大多数我们认为这些漏洞是可远程利用的。

个人总结

本文提出了一种新型的以三层结构方式(内部快,过程内,过程间)来静态分析PHP 代码中的漏洞

工具分析的流程:

(1)PHP 源码解析为抽象语法树
(2)将抽象语法树转化成控制流图
(3)使用符号执行的方式去模拟每个基本块,观察其对全局的作用,写出块摘要
(4)利用可达性分析,将块摘要组合成函数

原文链接

Static Detection of Security Vulnerabilities in Scripting Languages