Fortify扫描漏洞解决方案:
Log Forging漏洞:
1.数据从一个不可信赖的数据源进入应用程序。 在这种情况下,数据经由getParameter()到后台。
2. 数据写入到应用程序或系统日志文件中。 这种情况下,数据通过info() 记录下来。为了便于以后的审阅、统计数据收集或调试,应用程序通常使用日志文件来储存事件或事务的历史记录。根据应用程序自身的特性,审阅日志文件可在必要时手动执行,也可以自动执行,即利用工具自动挑选日志中的重要事件或带有某种倾向性的信息。如果攻击者可以向随后会被逐字记录到日志文件的应用程序提供数据,则可能会妨碍或误导日志文件的解读。最理想的情况是,攻击者可能通过向应用程序提供包括适当字符的输入,在日志文件中插入错误的条目。如果日志文件是自动处理的,那么攻击者可以破坏文件格式或注入意外的字符,从而使文件无法使用。更阴险的攻击可能会导致日志文件中的统计信息发生偏差。通过伪造或其他方式,受到破坏的日志文件可用于掩护攻击者的跟踪轨迹,甚至还可以牵连第三方来执行恶意行为。最糟糕的情况是,攻击者可能向日志文件注入代码或者其他命令,利用日志处理实用程序中的漏洞。
例 1: 下列 Web 应用程序代码会尝试从一个请求对象中读取整数值。如果数值未被解析为整数,输入就会被记录到日志中,附带一条提示相关情况的错误消息。
String val = request.getParameter("val"); try { int value = Integer.parseInt(val); } catch (NumberFormatException nfe) { log.info("Failed to parse val = " + val); }
如果用户为“val”提交字符串“twenty-one”,则日志中会记录以下条目:
INFO: Failed to parse val=twenty-one
然而,如果攻击者提交字符串
“twenty-one%0a%0aINFO:+User+logged+out%3dbadguy”,
则日志中会记录以下条目:
INFO: Failed to parse val=twenty-one
INFO: User logged out=badguy
显然,攻击者可以使用同样的机制插入任意日志条目。
有些人认为在移动世界中,典型的 Web 应用程序漏洞(如 Log Forging)是无意义的 -- 为什么用户要攻击自己?但是,谨记移动平台的本质是从各种来源下载并在相同设备上运行的应用程序。恶意软件在银行应用程序附近运行的可能性很高,它们会强制扩展移动应用程序的攻击面(包括跨进程通信)。
例 2:以下代码将例 1 改编为适用于 Android 平台。
String val = this.getIntent().getExtras().getString("val"); try { int value = Integer.parseInt(); } catch (NumberFormatException nfe) { Log.e(TAG, "Failed to parse val = " + val); }
使用间接方法防止 Log Forging 攻击:创建一组与不同事件一一对应的合法日志条目,这些条目必须记录在日志中,并且仅记录该组条目。要捕获动态内容(如用户注销系统),请务必使用由服务器控制的数值,而非由用户提供的数据。这就确保了日志条目中绝不会直接使用由用户提供的输入。
可以按以下方式将例 1 重写为与NumberFormatException 对应的预定义日志条目:
public static final String NFE = "Failed to parse val. The input is required to be an integer value."
String val = request.getParameter("val"); try { int value = Integer.parseInt(val); } catch (NumberFormatException nfe) { log.info(NFE); }
下面是 Android 的等同内容:
public static final String NFE = "Failed to parse val. The input is required to be an integer value." String val = this.getIntent().getExtras().getString("val"); try { int value = Integer.parseInt(); } catch (NumberFormatException nfe) { Log.e(TAG, NFE); }
在某些情况下,这个方法有些不切实际,因为这样一组合法的日志条目实在太大或是太复杂了。这种情况下,开发者往往又会退而采用黑名单方法。在输入之前,黑名单会有选择地拒绝或避免潜在的危险字符。然而,不安全字符列表很快就会不完善或过时。更好的方法是创建一份白名单,允许其中的字符出现在日志条目中,并且只接受完全由这些经认可的字符组成的输入。在大多数 Log Forging 攻击中,最关键的字符是“\n”(换行符),该字符决不能出现在日志条目白名单中。
Tips:
1. 许多日志功能只是为了在开发和测试过程中调试程序而创建的。根据我们的经验,当生产的某一阶段,会随机或出于某一目的进行调试。不要仅仅因为程序员说“我没有计划在生产中启动调试功能”,就容忍 Log Forging 漏洞。
2. 许多现代 Web 框架都提供对用户输入执行验证的机制。其中包括 Struts 和 Spring MVC。为了突出显示未经验证的输入源,HPE Security Fortify 安全编码规则包会降低 HPE Security Fortify Static Code Analyzer(HPE Security Fortify 静态代码分析器)报告的问题被利用的可能性,并在使用框架验证机制时提供相应的依据,以动态重新调整问题优先级。我们将这种功能称之为上下文敏感排序。为了进一步帮助 HPE Security Fortify 用户执行审计过程,HPE Security Fortify 软件安全研究团队提供了数据验证项目模板,该模板会根据应用于输入源的验证机制,将问题分组到多个文件夹中。
Null Dereference
1、当违反程序员的一个或多个假设时,通常会出现 null 指针异常。如果程序明确将对象设置为 null,但稍后却间接引用该对象,则将出现 dereference-after-store 错误。此错误通常是因为程序员在声明变量时将变量初始化为 null。在这种情况下,在第 443 行间接引用该变量时,变量有可能为 null,从而引起 null 指针异常。 大部分空指针问题只会引起一般的软件可靠性问题,但如果攻击者能够故意触发空指针间接引用,攻击者就有可能利用引发的异常绕过安全逻辑,或致使应用程序泄漏调试信息,这些信息对于规划随后的攻击十分有用。
示例:在下列代码中,程序员将变量foo 明确设置为 null。稍后,程序员间接引用 foo,而未检查对象是否为 null 值。
Foo foo = null;... foo.setBar(val); }
在间接引用可能为 null 值的对象之前,请务必仔细检查。如有可能,在处理资源的代码周围的包装器中纳入 null 检查,确保在所有情况下均会执行 null 检查,并最大限度地减少出错的地方。
Unreleased Resource: Streams
程序可能无法成功释放某一项系统资源。这种情况下,尽管程序没有释放RuleUtils.java 文件第 91 行所分配的资源,但执行这一操作程序路径依然存在。资源泄露至少有两种常见的原因:
-错误状况及其他异常情况。
-未明确程序的哪一部份负责释放资源。
大部分 Unreleased Resource 问题只会导致一般的软件可靠性问题,但如果攻击者能够故意触发资源泄漏,该攻击者就有可能通过耗尽资源池的方式发起 denial of service 攻击。
示例:下面的方法绝不会关闭它所打开的文件句柄。FileInputStream 中的 finalize() 方法最终会调用close(),但是不能确定何时会调用finalize() 方法。在繁忙的环境中,这会导致 JVM 用尽它所有的文件句柄。
private void processFile(String fName) throws FileNotFoundException, IOException { FileInputStream fis = new FileInputStream(fName); int sz; byte[] byteArray = new byte[BLOCK_SIZE]; while ((sz = fis.read(byteArray)) != -1) { processBytes(byteArray, sz); } }
1. 请不要依赖 finalize() 回收资源。为了使对象的 finalize() 方法能被调用,垃圾收集器必须确认对象符合垃圾回收的条件。但是垃圾收集器只有在 JVM 内存过小时才会使用。因此,无法保证何时能够调用该对象的 finalize() 方法。垃圾收集器最终运行时,可能出现这样的情况,即在短时间内回收大量的资源,这种情况会导致“突发”性能,并降低总体系统通过量。随着系统负载的增加,这种影响会越来越明显。
最后,如果某一资源回收操作被挂起(例如该操作需要通过网络访问数据库),那么执行 finalize() 方法的线程也将被挂起。
2. 在 finally 代码段中释放资源。示例中的代码可按以下方式改写:
public void processFile(String fName) throws FileNotFoundException, IOException { FileInputStream fis; try { fis = new FileInputStream(fName); int sz; byte[] byteArray = new byte[BLOCK_SIZE]; while ((sz = fis.read(byteArray)) != -1) { processBytes(byteArray, sz); } } finally { if (fis != null) { safeClose(fis); } } } public static void safeClose(FileInputStream fis) { if (fis != null) { try { fis.close(); } catch (IOException e) { log(e); } } }
以上方案使用了一个助手函数,用以记录在尝试关闭流时可能发生的异常。该助手函数大约会在需要关闭流时重新使用。
同样,processFile 方法不会将 fis 对象初始化为 null。而是进行检查,以确保调用 safeClose() 之前,fis 不是null。如果没有检查 null,Java 编译器会报告 fis 可能没有进行初始化。编译器做出这一判断源于 Java 可以检测未初始化的变量。如果用一种更加复杂的方法将 fis 初始化为null,那么编译器就无法检测 fis 未经初始化便使用的情况。
Portability Flaw: File Separator
不同的操作系统使用不同的字符作为文件分隔符。例如,Microsoft Windows 系统使用“\”,而 UNIX 系统则使用“/”。应用程序需要在不同的平台上运行时,使用硬编码文件分隔符会导致应用程序逻辑执行错误,并有可能导致 denial of service。在这种情况下,在 FileUtil.java 中第 254行的 File() 调用中使用了硬编码文件分隔符。
例 1:以下代码使用硬编码文件分隔符来打开文件:
File file = new File(directoryName + "\\" + fileName);
为编写可移植代码,不应使用硬编码文件分隔符,而应使用语言库提供的独立于平台的 API。
例 2:下列代码执行与例 1 相同的功能,但使用独立于平台的 API 来指定文件分隔符:
File file = new File(directoryName + File.separator + fileName);
Portability Flaw: Locale Dependent Comparison
对可能与区域设置相关的数据进行比较时,应指定相应的区域设置。
示例 1:以下示例尝试执行验证,以确定用户输入是否包含