糟糕的错误处理源代码审查

信息安全 Web应用程序 应用安全 爪哇
2021-08-17 02:53:02

我今天收到了安全小组的报告。该报告包含以下提到的漏洞和描述:

1)糟糕的错误处理:过度广泛的抛出

program1.java 中的方法抛出了一个通用异常,使得调用者更难做好错误处理和恢复工作。

2) 错误处理能力差:过于宽泛

EXAMPLE1.java 第 146 行的 catch 块处理大量异常,可能会捕获不同的问题或程序此时不应处理的问题。

3) 错误处理不当:空 Catch 块

somefile.java 中的方法 SomeMethod() 忽略了第 33 行的异常,这可能导致程序忽略意外的状态和条件。

看完上面的报道,我心里有几个问题:

  1. 这些与安全有什么关系?根据我的理解,上述问题似乎是代码质量问题。

  2. 如果这些是真正的安全威胁,那么攻击者如何利用它呢?

  3. 需要什么样的保护控制来缓解上述问题?

4个回答

我认为你是对的,这些问题与代码质量而不是安全性更相关,而且它们都不能以任何明显的方式被利用。我不会称它们为“漏洞”。

但是漏洞是由错误产生的,而错误是由糟糕的代码质量产生的。糟糕的错误处理可能会导致意想不到的结果 - 即错误 - 如果您不走运,可能会导致漏洞。

以下是一些与异常处理相关的示例:

  • 如果您根本不处理异常,则可能导致程序崩溃。这可用于拒绝服务攻击 - 只需用恶意请求溢出服务器,使其崩溃,它将花费所有时间重新启动而不是服务合法请求。
  • 如果在 HTTP 响应中传递错误消息(如Rich Moss所指出的),未处理的异常可能会导致敏感信息的泄露。
  • 捕获所有内容的过于宽泛的 catch 语句可能会导致安全关键异常被掩盖。

有关实际示例,请查看按“异常”一词过滤的 CVE 列表(如Ben Hocking所建议的)。

解决方案是遵循异常处理的最佳规范。我不会在这里详细介绍,因为这更多的是编程而不是安全问题。

这些与安全有什么关系?根据我的理解,上述问题似乎是代码质量问题。

这些都是代码质量问题。从名字上看,它们似乎是直接从 Fortify 出来的。Fortify 能够查找代码质量问题,并且看起来您的安全团队在运行扫描时启用了这些规则。

如果这些是真正的安全威胁,那么攻击者如何利用它呢?

我不认为列出的任何问题都可以以有意义的方式加以利用。我能预见的唯一影响是,这将使您很难调试代码。

需要什么样的保护控制来缓解上述问题?

这些问题是不言自明的。遵循异常处理最佳实践。

坏人通过尽可能多地了解目标系统来发起攻击。处理不当的异常可能会向调用客户端泄露敏感信息。

例如,在 REST API 中,典型的成功 GET 响应将包括请求的对象和 200 HTTP 状态代码,但几乎没有关于服务器实现的其他内容。对不存在对象的 GET 请求的响应应返回 404 HTTP 状态代码并且没有响应正文。这是正确的错误处理。

在异常情况下,应返回 500(服务器错误)状态代码,并且响应正文可能包含有关异常来源的一些信息:连接问题、数据库唯一键违规等。这种类型的响应可以包含有关坏人可以使用的系统,包括堆栈跟踪、服务器技术、软件库和供应商名称。正确的异常处理可以防止这些数据被泄露给调用客户端,或者以其他方式遍历软件层以存储在不必要的日志中。

请记住,数据扩散是安全系统的敌人之一。随着更多异常详细信息存储在冗余日志中,发现数据的攻击面扩大了。因此,请将多余的噪音排除在您的日志和服务器响应之外。

来自您的安全审查员的信息是处理所有已知的异常情况,如正常流程。我猜一个过于宽泛的 throw表明代码正在抛出一个通用异常而不是类型化异常,应该将其作为类型化异常捕获以处理过于宽泛的 catch在 REST API 示例中,如果此异常是服务器端的已知/频繁超时问题,调用者可能会选择返回空响应,并指示用户重试或检查活动。相反,像“<script..>”这样提供无效数据的异常可能需要额外级别的警报、可能的用户锁定和空响应。

我一直在处理代码审计,像这样的安全分析和道德黑客攻击已经运行了十五年了,所以让我分享一些我的经验。

我所看到的每一位老板和团队领导都坚持对代码质量有高标准,这不仅是为了让代码审查更容易,而且也是为了稳定性、可维护性和安全性。像您的团队一样进行异常处理的开发人员要么没有经验,要么没有时间,要么根本不在乎。而这种懒惰的编码会导致灾难。

对我来说最明显的是你的问题 3,一个空的捕获。这掩盖了通过它的任何错误,甚至没有记录它。现在假设发生以下情况:

try {
    // start a transaction
} catch (Exception ex) {
}
finally {
    // commit the transaction
}

您可能会将对业务无效的数据放入数据库中。对于更详细的示例,假设具有属性的Account类型fund,这样fund是一个无符号整数(因为您不希望允许其中的资金少于零的帐户):

try {
    // moving funds between accounts
    Transaction.start();
    accountA.funds += amount;
    accountB.funds -= amount; // throws an overflow exception if funds < amount
} catch (Exception ex) {
}
finally {
    Transaction.commit();
}

最终结果可能是资金流向账户 A,但不会从账户 B 中扣除。

这听起来可能很愚蠢,对你来说不符合你的团队,但我不得不在我工作过的一些公司处理遗留代码中的这些事情。我并不是说您的解决方案中包含这种代码,但我是说如果您有懒惰或心怀不满的员工,发现这样的代码不要感到惊讶。如果你得到一份像你所做的那样的报告,永远不要相信你的代码库是完全安全的。

让我先一步,举一个例子,在这个例子中,糟糕的错误处理确实让很多人感到厌烦。历史上最昂贵的编码错误之一是阿丽亚娜 5 号火箭的首次试飞

1996 年 6 月 4 日,阿丽亚娜 5 号的首次试飞(阿丽亚娜 5 号航班 501)失败,由于控制软件故障,火箭在发射后 37 秒自毁。将数据从 64 位浮点值转换为 16 位有符号整数值以存储在表示水平偏差的变量中导致处理器陷阱(操作数错误),因为浮点值太大而无法用 16 位表示有符号整数。该软件最初是为 Ariane 4 编写的,考虑到效率(运行该软件的计算机有 80% 的最大工作负载要求)导致四个变量受到处理程序的保护,而其他三个变量,包括水平偏差变量,没有受到保护,因为人们认为他们“身体受到限制或有很大的安全余地”

强调我的。如果我没记错的话,那是 3.7 亿美元的失败。诚然,您可能不是为 NASA 或 DARPA 编码,因此您因编码错误而造成的损失应该主要是由于返工而不是机器爆炸造成的员工时间。但是损失同样是损失,如果你可以通过更加小心来避免它们,你最好至少考虑一下。