2.10 【资源泄露问题】
2.10.1 概述
程序可能会不能成功释放某一系统资源,或者释放资源的速度相比创建来慢很多,就会引发系统资源 不够,资源泄露等等一系列问题。
通常资源未释放干净导致资源泄露,至少有以下两种情况经常发生:
- 错误的条件以及其它异常的情况。
- 不明确程序的哪一部份负责释放资源。
多数未释放资源的问题都是由软件可靠性造成的,如果攻击者企图引发资源泄露,那有可能通过下面 二种方法来进行攻击:
第一:对未进行显式释放的资源,攻击者反复触发,直到将系统对资源释放失去控制为止。 第二:是通过清空资源池发动一个拒绝服务攻击;非正常退出,资源不能干净释放。
2.10.2 问题分析
下面举例说明资源泄露的典型情况: 例一:如数据连接对象,在编写代码时,仅仅关闭打开对象,而不显示释放数据库连接对象;很明显,
挡频繁使用时,会出现连接池超限问题,就是说里面有很多可以利用的数据连接对象。 例二:如一些数据库记录集合如resordset和DataSet等等对象,也会有上面相同的问题。 例三:如一些文件处理对象等等不显式释放。
例四:其它的一些自己定义的数据库对象。
2.10.3 修复建议
请不要依赖Finalize()来回收资源。为了使对象的finalize()方法能被自动调用,前提是垃圾收 集器必须确认对象符合垃圾回收的条件。但是垃圾收集器只有在内存过一定时间才会被使用。因此,没人 知道何时将会自动调用该对象的Finalize()方法,以作为权宜之计。当垃圾收集器最终进行运行时,很 有可能出现这样的情况,在一段很短的时间内,大量的资源会被回收,而这种情况更将造成系统性能的减 弱。
在修复时可以有下列方法来帮助进行资源释放: 第一:将对象置空,如:
ConObject=null;
第二:直接调用对象释放方法。
2.11 【双边信任问题】
2.11.1 概述
同一个数据结构中混合可信赖的和不可信赖的数据会使得程序员错误地相信未经校验地数据。
-15-
一个受信任的边界可以被认为是由系统划出的线。在线的一边的数据是不受到信任的,另外一边数据
被假设为可以信任的。验证逻辑的目的是允许数据安全地跨越受信任的边界--从不被信任的地方移动到 受信任的地方。
当一个系统把该被信任和不该被信任之间的分隔线弄得模糊不清的时候,Trust boundary violations 漏洞就会发生。发生这种错误最普遍的方式是受信任的和不受信任的数据混合在通一个数 据结构中。
2.11.2 问题分析
应当在应用程序中定义一个容易辨别的信任边界。不要使用相同的数据结构来保存在某些环境下受信 任的数据和在其它环境下不受信任的数据。
当输入数据在进行处理之前,需要验证的输入数据会在一系列用户的交互中累积增加,这时Trust boundary violations漏洞就会可能会出现。所以,直到所有的数据都已经到达,否则不可能进行完 善的输入验证。在这种情况下,维护一个信任边界仍然是非常重要的。不受信任的数据应该被增加到一个 专门的不受信任的数据结构中,经过验证,然后再移动到一个受信任的区域中。
原则一:原则上在不同对象之间进行值转换,应该是要求在赋值和取值时全部进行有效性判断。 类型一:字符型向数值型转换,肯定会判断,因为编译器会帮助你发现问题。在值使用时,是显式类
型,是不需要判断的。 类型二:当值转换在一个扩展对象(一般这种类型的对象是可以序列化的),没有明显的类型,什么
样的数值类型都可以给它赋值;编译器不会帮助你去发现问题;只能在调试过程中、运行过程中发生问题。 当然如果在赋值时按照你设计的数据类型进行了校验,就是标准的做法,它会减少很多运行中的问题的发 生。在值使用时是需要根据情况进行判断的。
原则二:在扩展对象进行值转换时,如果过程是连续的,只要在这个连续的过程中,找到判断的地方, 确保在执行到代码这里时,是有效的,也就是对有效性进行了判断。赋值时采取同样的原则,下面举例说 明:
例一:说明样的过程是不连续的。和上面SQL注入问题和脚本注入问题类似,如从数据库取出、从
Request中获取、从Session中获取等等都是不连续的。
例二:下面的情况认为是连续的。
If(request.getParameter("SS")!=null)
{
BB= request.getParameter("SS")。
}
If(request.getParameter("SS")==null)
{
//Return;
request.getParameter("SS")=‖‖;
}
-16-
中间没有处理request.getParameter("SS") ……
BB=request.getParameter("SS")
public TestObject GetTestObject ()
{
If(request.getParameter("SS")==null)
{
//Return;
request.getParameter("SS")=””;
}
……
}
BB= GetTestObject()
2.11.3 修复建议
在不同类型的数据变量相互使用值时,应该对“值”进行有效性判断。
2.12 【日志注入问题】
2.12.1 概述
该问题主要是针对 java 环境下的 Logger 来说明的。
把未经确认的用户输入内容来写入日志文件可能会引起一个攻击者伪造日志条目或者将恶意信息内 容注入日志。
在发生日志注入错误时,一般来讲,需要二个前提条件:。
? 数据从一个不受信赖的来源进入应用程序。
? 数据写进一个应用程序或者是日志文件。 上面的二个前提是串联结构,在防止攻击过程中,原则上只要让其中一个前提不发生就可以了。
2.12.2 分析
对输入日志的数据来源 info 没有判断,也就是说满足数据源不受信赖的发生条件。
下面举二个示例来说明大量数据日志是如何生成的: 对于一个数值型的输入,并且校验做得不够,那么就会产生大量数据日志;同时如果输入源恶意,一
个日志也有可能产生多条记录: 比如在一个数值文本框中输入可以控制,如果一个攻击者提交字符串‖xxINFO:xxINFO:ss‖ INFO: Failed to parse val=xx
INFO: xx
INFO: ss
-17-
比如在一个URL的输入可以控制,如果一个攻击者提交字符串
―twenty-one%0a%0aINFO:+User+logged+out%3dbadguy‖, 以下的条目就被记录了:
INFO: Failed to parse val=twenty-one
INFO: User logged out=badguy
一条被拆成二条甚至多条,日志回增多,被恶意者利用,就可能撑爆日志。
2.12.3 修复建议
很多日志功能之所以被创造出来,只是为了满足在开发过程中调试和测试一个程序的目的。根据我们 的经验,当程序开发到某一程度,调试会意外地或有意识地被进行。
在编写代码时,注意考虑下面二个问题: 第一:最好不要直接使用用户输入的信息,产生错误日志;尽可能去捕捉有效的日志的内容,然后转
义后再进入数据库。 第二:能处理的错误尽可能先处理,而不要全部向错误日志里扔。
2.13 【系统出错配置】
2.13.1 概述
为了避免攻击者通过框架的内置错误响应来挖掘信息,Web 应用程序必须提供自定义错误页面。
2.13.2 问题分析
当攻击者通过浏览 web 站点寻找漏洞时,站点所提供信息的数量是攻击成败的关键。如果应用程序
向攻击者展示了一个栈踪迹 ,那它所丢弃的信息将使攻击者的工作变得轻而易举。例如,一个栈踪迹可 以向攻击者展示一个不合式的 SQL 字符串,或是所使用数据库的类型,亦或是应用容器的版本。而这些信 息都可以使攻击者清楚地了解到这些组件的弱点。
因此,应用程序应通过配置来指定一个缺省错误页面以保证应用程序将永远不会向攻击者泄漏错误信 息。 处理标准 HTTP 错误代码是非常有用的,除了是一个很好的安全实践之外,一个好的配置将也定义 一个 last-chance 错误处理器来捕捉那些可能被应用程序抛出的异常。
ASP .NET 应用程序可以使用自定义错误页面,而不是框架缺省的页面。对于出现的错误,缺省错 误页面提供了太过详细的信息,这就使得它不能在正式的环境中被使用。<customErrors>标签中的mode 属性定义了程序使用的是自定义的错误页面还是缺省的错误页面。攻击者能够利用从缺省错误页面获取的 信息来进行针对应用系统框架、数据库或者其他资源的攻击。
2.13.3 修复建议
针对系统出错时的系统应对方案,就是不能让原始错误页面直接将错误信息抛给用户,常见的解决办
-18-
法如下:
第一:系统自己定义一套完整的出错处理机制,在发生错误时指向相应的页面。 第二:使用相应的开发工具的系统配置,定义错误处理页面,出错时定向到相应错误处理页面,这个
解决办法也需要按照语言的特性来处理,下面分别以.Net和Java来说明.
对于java来说,在系统中补充error.jsp页面,在Web.xml文件中补充下面的代码,可以解决这个 问题:
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/error.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/error.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error.jsp</location>
</error-page>
对于.Net来说,在已部署的应用系统中总是使用定制错误页面。它的实现是通过在你的应用程序配 置文件中将 <customErrors>标签的mode属性设为On并且设其指向你的自定义错误页面,例如 error.aspx,如下所示:
<configuration>
<customErrors mode="On" defaultRedirect="error.aspx"/>
...
</configuration>
2.14 【XML 良性校验】
2.14.1 概述
在开发过程中使用 XML 文件来处理数据时,由于 XML 是有严格要求的,必须是开闭完全匹配的,否 则就不是一个良构的 XML 文件,进行 XML 解析就会失败,造成系统错误。XML 解析发生错误时,会引起 下面二个安全方面的现象:
第一:将 XML 数据直接暴露,为攻击者提供信息。 第二:出错后,为攻击者绕过系统的一些安全防御提供通道。
2.14.2 问题分析
如何保证 XML 良构的进入运行时的应用系统,可分为二种情况来分析:一是动态运行时生成 XML;而 是静态系统取出 XML。针对这二种情况下面分类说明:
-19-
在代码中动态构造 XML 文档时使用如下写法时,文档能保证是良构的:
XMLDocument xmlData=new XMLDocument();
myXml.LoadXml(@"<?xml version='1.0' encoding='utf-8'?><Root>
<Elements/></Root>");
XmlElement pPCRElementXml = myXml.DocumentElement; DetailNode = xmlData.CreateElement("Element ",""); DetailNode.InnerText=@"<111&";
pPCRElementXml.AppendChild(DetailNode);
下面的写法,一旦数据源有关键字符,XML 文档很可能就不是良构的:
string strTemp=@"<Root><Elements><Element NO=‘>‘ /><Element NO =‘21‘
/></Elements></Root>";
string strXmlHead=@"<?xml version='1.0' encoding='utf-8'?> ";
strTemp = strXmlHead + strTemp;
xmlData.loadXML(strTemp);
XML 文档是从已经存在的物理文件中获取时,判断文件是否良构,而不是默认良构;直接加载 XML
文档方法也应该禁止:
try{
myXml.Load(Server.FilePath+@"\a.xml")
}
catch
{
Return;
}
2.14.3 修复建议
第一:XML 文档最好不要使用字符串并串的方法产生,使用 XML 对象会好,它会将特殊字符转义; 也不容易出现遗漏。
第二:在最后使用时,进行校验,可以调用 XML 中间件里的校验方法进行。
2.15 【Finally 返回值】
2.15.1 概述
在 Finally 块中使用 return 语句,会导致(try 块抛出的)异常丢失。
2.15.2 问题分析
finally
-20-
{
return "to node3";
}
2.15.3 修复建议
应该把 return 语句从 finally 块中移出来。如果 finally 块中必须有一个值被返回,可以简单 地把这个值赋给一个局部变量,然后在 finally 块执行完毕以后返回这个变量。
2.16 【系统路径泄露】
2.16.1 概述
允许用户可以通过输入来控制用于文件系统操作的路径,会使得攻击者能够访问或修改被保护的系统 资源。
当满足以下两种情况的时候,会产生 Path manipulation 的错误:
? 攻击者能够指定一个在文件系统的操作中使用的路径。
? 攻击者可以通过指定特定资源来获取某种权限,而这种权限在一般情况下是不可能获得的。
2.16.2 问题分析
下面举例说明,系统路径是如何泄露的:
下面的代码使用来自于HTTP请求的代码来删除文件。而系统中文件名称相关的对象用户可以控制, 攻击者能够提供像―../../tomcat/conf/server.xml‖这样的文件名的可能性,这会导致应用程序删 除它自己的配置文件。
String rName = request.getParameter("reportName");
File rFile = new File("/usr/local/apfr/reports/" + rName);
... rFile.delete();
在这里rName是否用户可以修改就需要判断,如果可以,那么就有安全隐患。
2.16.3 修复建议
避免Path manipulation最有效的方法就是采用一些间接手段,如创建一份合法资源名的列表,并 且规定用户只能选择其中的资源名。使用这种方法,用户将再也不能将文件名直接输入为指定资源的名字。
但在某些情况下,这种方法是不可行的,因为这份列表太过于庞大、难以跟踪。因此,程序员通常在 这种情况下采用黑名单的手法来进行处理。在输入时,黑名单会有选择性的丢弃某些具有潜在危险的字符。
-21-
但是,任何一份黑名单都不可能是完整的,都将随着时间的推移而变得过时老旧。因此,一种更好的方法
就是创建一张白名单。这份列表列出允许出现的资源名,并且仅仅接受完全由该列表中的字符组成的输入。
如果一定要动态配置资源,那么建议进行下列处理:
? 第一:针对资源,将正确的待选资源数据列举出来,限制只能在列表中的数据可以输入。
? 第二:建立黑名单,使用排除法,将可能有问题的资源数据列举排除。