Web环境中两个主要的风险在于:注入——也就是SQL注入,它会让黑客更改发往数据库的查询——以及跨站脚本攻击(XSS)。注入攻击会利用有问题代码的应用程序来插入和执行黑客指定的命令,从而能够访问关键的数据和资源。当应用程序将用户提供的数据不加检验或编码就发送到浏览器上时,会产生XSS漏洞。
Web应用程序需要有深度防御的措施来避免和减少安全性漏洞。1这种方式假设所有的安全预防措施都可能失败,所以安全性依赖于多层的机制从而能够覆盖其他层的失败。为了减少成功攻击的可能性,软件工程师团队必须做出必要的努力来引入适当的安全性防护措施。要达到这一点必须使用各种技术和工具来确保安全性涵盖软件产品开发生命周期的所有阶段。
软件开发生命周期中的安全性
尽管软件开发的生命周期有多种不同的划分方式,但正如图1所示,它通常包含如下的阶段:初始化、规范和设计、实现(编码)、测试、部署以及停用,这些阶段应用开发人员可以不断地重复迭代。2
尽管开发人员应该在产品的整个生命周期中都关心代码安全性,3但是他们应该特别关注三个关键阶段:1
- 实现。在编码过程中,软件开发人员必须使用特定应用领域内避免关键漏洞的最佳实践。这种实践的例子包括输入和输出校验、识别恶意字符以及使用参数化的命令。4 尽管这些技术在避免大多数安全漏洞方面很有效,但因为缺乏安全相关的知识,开发人员通常并不使用它们或者使用得不正确。边栏“为什么开发人员不使用安全编码实践?”更详细地讨论了这个问题。
- 测试。有很多技术可以在测试阶段使用,包括渗透测试(目前最流行的技术)、静态分析、动态分析以及运行时的异常检测。4 问题在于开发人员通常会关注需求功能的测试而忽略安全方面。另外,现有的自动化工具要么在漏洞探测覆盖度方面比较差要么产生太多的误报。
- 部署。在运行时环境中,会有不同的攻击探测机制。这些机制可以按照不同的级别运行并使用不同的探测方式。它们的使用障碍在于性能开销以及不准确的结果会打乱系统的正常行为。
开发安全的代码
为了编写没有漏洞的安全代码,4 基于Web基础设施的关键业务开发人员就要遵循编码实践,这个实践包括了深度防御的措施,它假设所有的安全性预防措施都会失败。在实现阶段依赖多层的安全机制是特别重要的,使用一个预防或保护措施来避免安全漏洞是不够的。
Web应用程序的特征在于需要三层不同的安全防线:输入校验、热点保护以及输出校验。
输入校验
大多数的安全漏洞是因为目标应用程序没有正确地校验输入数据。1所以,应用程序要考虑到所有恶意的输入直到能证明其合法,这要涵盖不可信环境中的所有数据。
输入校验是第一道防线,总体来讲就是缩小应用程序允许输入的范围,它会直接作用在用户提供的数据上。这种类型的防御要依赖输入参数在一个合法的范围内,或者如果用户提供了超出了范围的值就会停止执行。在Web应用程序中,这首先要标准化输入将其转换到基线字符集和编码。接下来,应用程序必须对标准化的输入使用过滤策略,拒绝那些值在合法范围之外的输入。这种方式能够避免很多Web应用程序中的问题,在执行输入校验时会使用正向模式匹配或正向校验。在这种情况下,开发人员建立规则来识别那些可接受的输入而不是识别有什么输入是不可接受的。尽管开发人员不能预测所有类型的攻击,但他们应该能够说明所有类型的合法输入。
关键问题在于,输入校验通常使用地并不充分,这是因为输入参数的数据域允许存在恶意数据,这是与校验执行相独立的。例如,在SQL注入漏洞中,大多数的SQL语句使用引号作为字符串分隔符,这就意味着黑客可以使用它来执行SQL注入攻击。4但是,在有些情况下,字符串输入域必须允许存在引号值,所以应用程序不能排除所有包含引号的值。
热点防护
为了应对输入校验的局限性,有必要采用第二道防线
任何类型的攻击都是以热点为目标的,热点指的就是应用程序中可能会有某种类型漏洞的代码。通用的输入校验会在应用程序中进行或者在整个Web应用程序上下文中修改输入,与之相比,第二道防线关注于保护重要的热点,例如保护那些真正使用输入域值的代码行。
一个具体的例子就是SQL注入攻击,它们大多数会使用单引号或双引号。有些编程语言提供了对这些字符的转码机制,这样它们就能用在SQL语句中了,但是只能用来在语句中分隔值。4但是这些技术有两个问题。第一,更高级的注入技术,例如联合使用引号和转义字符,可以绕过这些机制。第二,引入转义字符会增加字符串的长度,如果结果字符串的长度超过数据库限制的话,可能会导致数据截断。
正确使用参数化命令是预防注入攻击最有效的方式。1在这种情况下,开发人员定义命令的结构,并使用占位符来代表命令的变量值。稍后,当应用程序将对应的值关联到命令上时,命令解释器会正确地使用它们而不会涉及到命令的结构。
这种技术最著名的用法是数据库的预处理语句,也被称为参数化查询。4 当应用程序创建预处理语句时,语句发送到了数据库端。应用程序使用占位符来表示查询的可变部分,占位符通常会是问号或标签。随后,每次查询执行时,应用程序都要往对应的可变部分绑定值。不管数据的内容是什么,应用程序会一直使用这个表达式作为一个值而并没有SQL代码。因此,不可能修改查询的结构。
为了确保正确使用数据,很多语言允许类型绑定。但是预处理语句本身并不能修复不安全的语句——开发人员必须正确地使用它们。例如,像传统语句一样使用预处理语句——也就是使用字符串拼接来绑定SQL查询——而不是对查询的可变部分使用占位符会导致类似的漏洞。
输出校验
在将一个进程的输出发送之前进行校验能够避免用户收到他们不应该看到的信息,例如应用程序内部的异常细节,这些信息有助于发起其他的攻击。在输出校验的另一个例子当中,保护系统会搜索应用程序输出的关键信息,如信用卡号,并在发送给前端之前用星号代替。将信息编码是能够避免XSS漏洞的一种输出校验方式。4如果发送给浏览器的数据要显示在Web页面上,它应该进行HTML编码或百分号编码,这取决于它在页面的位置。通过这种方式,XSS所用的恶意字符不再具有破坏性,而且编码会保留数据的原来意义。
探测漏洞
识别安全的问题要求不仅测试应用程序的功能还要寻找代码中可能被黑客利用的隐藏的危险缺陷。5探测漏洞的两个主要方式是白盒分析和黑盒测试。
白盒分析
白盒分析需要在不执行的情况下检查代码。开发人员可以按照以下两种方式中的某一种来进行:在代码的审查或评审时以手动方式进行或者借助自动分析工具自动化进行。
代码审查(Code inspection)指的是程序员的同伴系统检查交付的代码,查找编码错误。6安全审查是减少应用程序中漏洞最有效的方式;当为关键的系统开发软件时,这是重要的过程。但是,这种审查方式通常是很费时间的、代价昂贵并需要深入了解Web的安全知识。
代码检查(Code review)是代价稍为低廉的替代方案,6它是一种简化版本的代码审查适用于分析不像前面那么重要的代码。检查也是手动进行的,但是它不需要正式的审查会议。几个专家分别进行检查,然后由主持人过滤和合并结果。尽管这是一个有效的方式,但代码检查的成本依旧是很高的。
为了减少白盒法分析的成本,开发人员有时依赖自动化工具,如静态代码分析器。静态代码分析工具会检查软件代码,要么是源码格式要么是二进制格式,并试图识别出常见的编码级别缺陷。4使用现有工具所执行的分析会因为它们的复杂性而有所不同,这种差异体现在考虑单条语句和命令或考虑代码行之间的依赖。除了模型检查和数据流分析等功能之外,这些工具还会自动关注可能的编码错误。它的主要问题在于细致的分析是很困难并且因为源码的复杂性和缺乏动态(运行时)的视角有很多安全缺陷很难被发现。
尽管使用静态代码分析工具很重要,但是它有时会降低开发人员的生产效率,这主要是因为误报,这会导致没有用处的额外工作。7为了避免这种情况,除了要有足够的时间学习怎样使用这些工具以外,开发人员需要一些策略来保证正确使用这些工具。例如,有必要指定规则来分类和选择开发人员应该处理的警告信息。同时,开发人员还要配置分析工具只报告那些与当前开发上下文相关的警告。没有接受怎样使用静态分析训练的开发人员最终会低估它的真正效益并且通常不能发挥它的所有功能。
黑盒测试
黑盒测试指的是从外部的视角分析程序的执行。简而言之,它会比较软件执行的输出与期望的结果。5对于软件的检验和确认来说,测试可能是最常用的技术了。
对于黑盒测试来讲,有多种级别,从单元测试到集成测试再到系统测试。测试方式可以是正式的(基于模型和定义良好的测试规范)也可以不那么正式(被称为“冒烟测试”,一种粗糙的测试目的是快速暴露简单的缺陷)。
健壮性是黑盒测试一种特殊形式,它的目标是查看系统在错误输入条件下的行为。渗透测试是特殊类型的健壮性测试,它会分析在遇到恶意输入时的代码执行并查找潜在的漏洞。在这种方式中,测试人员使用模糊技术,这包含通过HTTP请求,提交意料之外的或非法的数据项到Web应用程序上并检查它的响应。4测试人员不需要了解实现细节——他们在用户的角度来测试应用程序的输入。对于每种漏洞类型,可能会有上百次甚至上千次的测试。
渗透测试工具会自动搜索漏洞,这避免了手工为每种类型的漏洞构建上百个甚至上千个测试所带来的重复和乏味的工作。Web应用的常见自动化安全测试工具一般会称为Web应用或Web安全扫描器。这些扫描器可以很容易地测试应用程序以发现漏洞。对于目标应用,它们会有一些预定义的测试用例,所以用户只需要配置一下扫描器并让它测试应用即可。一旦扫描器完成测试,它会报告所探测到的漏洞。大多数的扫描器都是商业产品,尽管也有免费的应用程序扫描器,但是与商用版本相比,它们缺少大多数的功能所以用的很有限。
漏洞探测的局限性
渗透测试和静态代码分析可以是手动的也可以是自动化的。因为手动测试或检查需要特殊的安全资源并且很费时间,所以对于Web应用的开发人员来说自动化工具是常见的选择。当考虑漏洞检测工具的局限性时,很重要的一点就是安全测试是很困难的。确实,衡量应用程序的安全性是很有挑战性的:尽管发现一些漏洞可能很容易,但是保证应用没有漏洞是困难的。1
渗透测试和静态代码分析工具都有其固有的局限性。渗透测试依赖于有效地代码执行,但是在实践中,漏洞识别时只会检查Web应用的输出。所以,缺少查看应用的内部行为会限制渗透测试的有效性。
另一方面,详尽的源代码分析可能比较困难。代码的复杂性以及缺少动态(运行时)的观察可能会阻止发现很多安全缺陷。当然,渗透测试不需要查看源码,但是静态代码分析需要。
使用错误的检测工具会导致部署的应用含有未检测出的漏洞。图2比较了在Web服务中,知名的并广泛使用的渗透测试和静态分析工具在检测SQL注入漏洞中的表现。8结果显示静态代码分析工具——包括FindBugs、Fortify 360以及IntelliJ IDEA(在图中匿名为SA1到SA3)——的覆盖度通常高于渗透测试工具,包括HP WebInspect、IBM Rational AppScan、Acunetix Web Vulnerability Scanner以及科英布拉大学开发的一个原型工具(在图中匿名为VS1到VS4)。这两种方式都有的一个问题就是误报,但是在静态分析中更明显。一个重要的发现在于相同方式下的不同工具对于相同的代码通常报告不同的漏洞。
根据研究结果,需要强调工具的局限性使得有必要提高漏洞检测的有效性,比如这可以通过联合使用多种方式来实现。另外,开发人员需要定义一种机制来评估和比较不同的工具,这样它们才能选择最适合各种开发场景的工具。
检测攻击
为了防止对Web应用的攻击,软件工程师必须实施攻击检测机制,通常称为入侵检测系统(intrusion detection system,IDS)或Web应用防火墙(WAF)。不同的工具可以作用在应用或网络级别甚至在应用的资源上,如数据库,它们可以使用不同的方式如异常检测或签名匹配来检测攻击。
检测攻击的方法
检测攻击要区分出与所学习行为的差别。攻击检测工具所使用的方式要么基于异常检测要么基于签名。8
异常检测通常需要一个训练阶段。训练阶段会展现系统的无恶意请求,工具会在给定的架构级别观察它的行为并学习正常的操作。这些工具会考虑到每个Web应用程序的细节,但如果应用程序的正确行为发生了变化或学习不完整的话,会产生很多错误的警告。
相比之下,基于签名的工具会查找预定义的一组规则模式或标示攻击的签名。因为这些签名通常是独立于应用的,所以工具的成功与应用程序的运行配置文件或任何训练过程无关。
在网络级别进行操作的工具通常会监视和分析网络流量,以保证攻击在到达Web应用之前检测到。工作在应用级别的攻击检测工具会分析发送给应用的请求并试图利用服务端程序和请求中参数的特定关系。工作在资源层的工具会保护与每种漏洞类型相关的资源。这些工具会在应用层之下并接近受保护的资源。一个常见的例子是监控对数据库服务器的访问来检测SQL注入的IDS。
工具使用各种策略来收集应用请求以及可能收到攻击的信息。一些工具会使用嗅探策略来监控和分析通过网络传输的数据以此来观察HTTP流量,但是加密、编码以及封装可能会限制其有效性。同时,网络上可能会承载大量与受保护应用无关的数据。另一些工具会分析应用产生的日志甚至是应用所在服务器所产生的日志。尽管这种策略不会直接延迟对应用的请求,但是受限于日志中可以得到的信息。
而另一种策略就是在请求的来源和受保护的应用或资源间引入一个代理。这能够很容易地阻止攻击,因为它提供了关于目标应用或资源的有用信息,但是,它引入了不良的延时从而会影响应用的正常行为。
检测攻击的局限性
因为每个Web应用程序的细节会影响到攻击检测工具的表现,同时工具所运行的架构级别也会有所影响,所以它们的实际效果通常是未知的。10大多数工具的检测覆盖比较低(在很多场景下,低于20%),同时它们还会有很多误报(高达所产成警报的50%)。此外,有些工具在特定的场景下展现的结果很好,但是在其他场景下所提供的结果很差。
数据库级别的工具通常比应用级别的工具表现更好一些,10不过它们会产生一些关于请求的误报,这些请求是不会成功攻击数据库的。基于异常检测的工具对于简单的应用表现得更好,而基于签名的工具对于复杂的应用表现更好。在简单的应用程序中,工具能够学习并且更好地描述行为,因此从模式中检测偏差会更准确。实际上,异常检测的成功取决于训练阶段。如果训练不完整或者应用的正常操作配置在训练后发生了变化,那么攻击检测工具的有效性会降低。
使用这些工具的开发人员有时缺乏创建适当配置的培训。这会减少工具的有效性,这凸显了评估和对比不同工具及配置的重要性。10
新的趋势和方向
要达到更好的结果并提高有效性需要新的技术来克服漏洞检测工具的局限性。但是要克服这些局限性并不容易,因为它需要将传统方式改为颠覆性的方法。关键在于释放一些约束并将不同的方法结合起来以克服单个方法的局限性。
Acunetix AcuSensor就是一个商业技术的例子,它将黑盒扫描和测试执行反馈结合起来。反馈来自于植入到目标应用程序代码中的传感器(sensor)。Acunetix声称这种技术能够发现更多的漏洞并能够精确表明漏洞在代码中的位置,而且误报也会更少。
一项最近提出的技术试图以更小的侵入性实现类似的效果,它联合使用攻击签名和接口监控来克服渗透测试对注入攻击漏洞测试的局限性。11这是一种黑盒测试技术,因为它只会监控应用程序和漏洞相关资源的接口(如数据库接口)。
Analysis and Monitoring for Neutralizing SQL-Injection Attacks(Amnesia)工具组合了静态分析和运行时监控来检测SQL注入攻击。12它对Web应用的源码进行静态分析,构建一个由应用生成的合法查询模型。在运行时,它监控动态生成的查询,检查是否与静态生成的模型相符。这个工具认为违反模型的查询为攻击并阻止它访问数据库。
为了应对Web应用安全的新威胁,开发流程必须也要有所发展。例如,微软安全开发生命周期(Microsoft Security Development Lifecycle)完善了公司的开发流程并特别针对安全问题的解决,例如明确了开发团队的安全培训。13按照微软的说法,这个流程的采用减少了软件中的安全缺陷。尽管这只是一个例子,但是它表明在这个行业中,对软件开发执行安全流程是很重要的事情。
在整个软件产品的开发和部署生命周期中,开发人员必须要考虑安全性。他们必须要使用安全编码的最佳实践、执行足够的安全测试并使用安全检测系统在运行时保护应用程序。在这个任务中,开发人员需要得到一些帮助来获取需要的技术和能够提高生产率的工具。
研究人员应该提出创新的工具,能够在开发过程中方便地使用并满足部署时有效性和生产效率的要求。这个演变的中心是安全测试工具,对于检验和确认应用程序以检查安全漏洞来讲,它们是至关重要的。不过,必须要探索新的假设。一个可以预见的可能性就是开发编译器,使其不仅能强制使用最佳编码实现,还能自动化修改存在的安全漏洞。