11

我是 java 新手,我不太熟悉错误堆栈跟踪在抛出并随后显示给我的 Web 应用程序的最终用户时使用的格式规则。

我对 Oracle 数据库的体验是错误堆栈包含内部信息,例如模式和过程名称以及行号,这些信息虽然对调试很有用,但我希望防止用户看到这些信息。这是一个例子:

java.sql.SQLException : ORA-20011: Error description here
ORA-07894: at "NAME_OF_SCHEMA.PROCEDURE_NAME", line 121
ORA-08932: at line 10

我想向用户显示的字符串是Error description here. 我可以使用正则表达式提取这个字符串,因为我知道(1)这个字符串总是在第一行,所以我可以提取错误堆栈跟踪的第一行,并且(2)这个字符串总是以结尾开头Error和结尾的线。[Oracle 用户注意事项(我不想误导您):上述内容仅适用于使用带有以 开头的错误字符串的 RAISE_APPLICATION_ERROR 时Error,否则文本Error不存在]。

我对 Java 的问题是:

(1) 是否有任何您不希望用户在错误堆栈中看到的潜在敏感信息?如果是这样,是什么?例如,文件路径、服务器名称/IP 等。

(2) Java 错误堆栈跟踪是否有任何格式化规则可以用来提取非敏感信息?或者,其他人如何解决这个问题?

更新 1:

感谢到目前为止的所有回复,他们非常有帮助。尽管许多人评论使用诸如getUserFriendlyMessage()将错误映射到有用的用户消息之类的功能,但我想知道是否有人可以扩展此映射。也就是说,对于常见错误(SQL、I/O 等),可以使用什么“可靠”标识符来搜索此错误堆栈以识别发生的错误类型,然后您会推荐什么相应的文本字符串映射到此错误消息以显示给用户?@Adarshr 下面的回复是一个好的开始。例如,

Identified Expected   If found in error stack, display this friendly msg to user
-------------------   ----------------------------------------------------------
SQLException          An error occurred accessing the database. Please contact support at support@companyname.com.
IOException           Connection error(?). Please check your internet connection.

假设不需要解决与编译相关的错误,而是关注最终用户在正常使用过程中可能遇到的错误。作为参考,这里是运行时错误消息的列表:http: //mindprod.com/jgloss/runerrormessages.html#IOEXCEPTION

或者,是否可以只使用堆栈跟踪的第一行显示给用户?这个链接是我在上面的原始问题中得到的:

http://www3.ntu.edu.sg/home/ehchua/programming/howto/ErrorMessages.html

例如,如果Exception始终使用标识符,则可以简单地提取介于Exception第一行和结尾之间的文本。我不知道我们是否可以依靠Exception一直在那里。

4

7 回答 7

7

您不应该向您的用户展示任何这些 gobbledygook。这对他们中的大多数人来说毫无意义,也对你没有帮助。正如您所怀疑的那样,它还暴露了您的实现的内部结构,这些内部结构可能暗示恶意用户可能能够使用的漏洞。

相反,您应该捕获异常、记录它们并向用户显示更易于理解的错误消息。您可以使用getMessage()异常来提取消息部分。如果异常没有消息,则显示“没有可用的详细信息”之类的内容。

更新:

我有一些基于问题更新的评论。首先,我会将用户与您系统的任何内部完全隔离,既要善待用户,又要确保安全。(例如,即使知道您正在使用 java.sql 包也可能会向聪明的黑客暗示漏洞。)因此,在向用户显示任何内容时,不要使用异常消息、堆栈跟踪的第一行或类似的东西.

其次,您应该将所有错误从异常级别(它们在您的代码中遇到)映射到对用户而言处于正确抽象级别的消息。执行此操作的正确方法取决于系统的内部结构以及引发异常时用户可能尝试执行的操作。这可能意味着将您的系统结构化为层,以便捕获异常的每一层都将其转换为更高抽象层的异常。Java 异常可以包装另一个异常(原因)。例如:

public boolean copyFile(File source, File destination) throws CopyException {
    try {
        // lots of code
        return true;
    } catch (IOException e) {
        throw new CopyException("File copy failed", e);
    }
}

然后可以在 User 类的更高级别上使用它:

public boolean shareFile(File source, User otherUser) throws ShareException {
    if (otherUser.hasBlocked(this) {
        throw new ShareException("You cannot share with that user.");
    }
    try {
        return copyFile(source, otherUser.getSharedFileDestination(source));
    } catch (CopyException e) {
        throw new ShareException("Sharing failed due to an internal error", e);
    }
}

(我希望很清楚,上面的代码是为了说明将异常转换为更高级别的抽象的想法,而不是作为您应该在系统中使用的代码的建议。)

您想要处理这样的事情(而不是以某种方式按摩消息和/或堆栈跟踪)的原因是异常(例如带有消息“权限被拒绝”的 IOException)可能对用户意味着完全不同的事情(并且您的系统)在不同的上下文中。

于 2012-06-08T18:29:36.487 回答
5

不要直接向最终用户显示异常消息/堆栈跟踪。而是尝试使用异常 - 消息映射方法。

例如:

  • SQLException- 抱歉,发生数据库错误。请稍后再试
  • RuntimeException/ Exception- 抱歉,发生错误。请稍后再试

实际上,您可以使其尽可能通用。也许您可以使用自定义错误代码映射。例如,E0001for SQLExceptionE0000forException等等,并将其显示给最终用户。当他们最终使用错误代码联系客户服务时,这将很有帮助。

于 2012-06-08T18:31:47.063 回答
3

您不应向最终用户显示他们无法理解的任何信息,其中包括完整的所有堆栈跟踪。当你捕捉到这样的异常时应该显示的错误应该是这样的,用你的最终用户的语言:

  • 我们的计划遇到了暂时的困难。请拨打技术支持热线寻求帮助。

最终用户不会关心程序的内部组织、数据库、堆栈等。他们所知道的是,他们认为应该有效的事情失败了,所以他们正在寻求帮助。

堆栈跟踪用于错误日志:您可以自己保存它们,或通过电子邮件将它们发送给技术支持,但您不想将它们显示给最终用户。

于 2012-06-08T18:31:50.107 回答
1

您应该只在您的应用程序处于调试模式时显示堆栈跟踪。在生产模式下,您应该显示通用错误消息(或实现Exception.getUserFriendlyMessage())并记录错误(如果可能,提供日志 ID)。

于 2012-06-08T18:30:58.453 回答
1

我想向用户显示的字符串是Error description here.

您可以将Exception.getMessage()其用于此目的,但正如 Juan Mendez 建议的那样,考虑实施一种更“用户友好”的错误消息机制,以便向最终用户显示错误。

如果您不希望最终用户在堆栈跟踪中看到您的类、方法和字段的名称,请考虑使用像Proguard这样的混淆器。我喜欢在日志文件中打印混淆的堆栈跟踪,然后使用 ReTrace 对它们进行去混淆以进行调试。

(1) 是否有任何您不希望用户在错误堆栈中看到的潜在敏感信息?如果是这样,是什么?例如,文件路径、服务器名称/IP 等。

我认为这可能取决于抛出的异常。选择是否记录异常是应用程序安全性和能够有效诊断问题之间的权衡,我会根据具体情况进行处理,并在必要时留下评论以解释我记录或不记录它的原因。

于 2012-06-08T18:31:08.700 回答
1

用户名和密码显然是您不希望向用户显示的敏感信息。此外,正如您所说,文件系统路径、服务器名称和 IP 地址也应该被隐藏。

Exception.getMessage()方法将只返回抛出异常的消息,而不是整个堆栈跟踪。但看起来在您给出的示例中,第 2 行和第 3 行也是异常消息的一部分,所以这对您没有帮助。

但是,向您的用户显示堆栈跟踪并不是一个好的做法。它们不仅可以包含敏感信息,而且对于用户来说,它们与克林贡语一样具有可读性。您应该记录所有未捕获的异常,然后向用户显示更用户友好的消息。

于 2012-06-08T18:37:08.173 回答
1

正如其他人所说,您不应该向用户报告堆栈跟踪。其他几个人建议您显示getMessage()文本。我建议要这样做。正如我之前所说

  • 该消息是在引发异常时创建的。因此,它最多只能提供非常低级别的信息,这可能不适合向用户报告。
  • 从哲学上讲,在我看来,使用消息与异常的全部意义背道而驰,即将错误处理的检测和启动(throw部分)与处理和报告的完成(部分)分开catch。使用消息意味着消息必须有利于报告,这将报告的责任转移到应该只负责检测和启动的位置。也就是说,我认为getMessage()设计的部分Throwable是错误的。
  • 消息未本地化。尽管有它的名字,但getLocalizedMessage()并不是很好,因为您可能不知道要使用什么语言环境,直到您catch出现异常(报告是转到您的英语系统管理员阅读的系统日志,还是在一个窗口中弹出GUI 的法国用户?)。
于 2012-06-12T13:17:45.353 回答