Java: какую информацию в трассировке стека ошибок мы обычно не хотим показывать пользователям?

Я новичок в java, и я не знаком с правилами форматирования, используемыми трассировкой стека ошибок, когда он бросается и впоследствии отображается конечному пользователю моего веб-приложения.

Мой опыт работы с базой данных 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 [email protected]
IOException           Connection error(?). Please check your internet connection.

Предположим, что ошибки, связанные с компиляцией, не нуждаются в исправлении, а скорее сосредоточьте те ошибки, которые могут возникнуть у конечных пользователей при нормальном использовании. Для справки, здесь приведен список сообщений об ошибках во время выполнения: http://mindprod.com/jgloss/runerrormessages.html#IOEXCEPTION

В качестве альтернативы можно ли просто использовать FIRST LINE трассировки стека для отображения пользователю? Эта ссылка - это то, что я получал в своем первоначальном вопросе выше:

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

Например, если идентификатор Exception всегда используется, можно просто извлечь текст, который находится между Exception и концом первой строки. Я не знаю, можем ли мы полагаться на Exception, всегда находясь там.

Ответ 1

Вы не должны показывать никому из этих gobbledygook своим пользователям. Это бессмысленно для большинства из них и не помогает вам. Как вы подозреваете, он также предоставляет внутренности вашей реализации, которые могут указывать на уязвимости, которые может использовать злоумышленник.

Вместо этого вы должны ловить исключения, регистрировать их и показывать пользователям более понятные сообщения об ошибках. Вы можете использовать getMessage(), чтобы извлечь часть сообщения из исключения. Если исключение не имеет сообщения, тогда покажите что-то вроде "нет доступных данных".

UPDATE:

У меня есть комментарии, основанные на обновлении вопроса. Во-первых, я бы полностью изолировал пользователя от каких-либо внутренних компонентов вашей системы, чтобы быть добрым к пользователю и к безопасности. (Например, даже зная, что вы используете пакет 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 с сообщением "отказ в разрешении" ) может означать совершенно разные вещи для пользователя (и вашей системе) в разных контекстах.

Ответ 2

Не показывать сообщение исключения /stacktrace непосредственно конечному пользователю. Вместо этого попробуйте использовать подход с отображением исключения - сообщения.

Например:

  • SQLException - Извините, произошла ошибка базы данных. Повторите попытку позже.
  • RuntimeException/Exception - Извините, что произошла ошибка. Повторите попытку позже.

Фактически вы можете сделать это как можно более общим. Возможно, вы можете использовать собственное сопоставление кода ошибки. Например, E0001 для SQLException, E0000 для Exception и т.д. И отобразите это для конечного пользователя. Это будет полезно, когда они в конечном итоге обратятся в службу поддержки клиентов с кодом ошибки.

Ответ 3

Вы не должны показывать какую-либо информацию конечным пользователям, которую они не могут понять, включая всю трассировку стека. Ошибка, которую вы должны показать, когда вы поймаете подобное исключение, должно читать что-то вроде этого на языке ваших конечных пользователей:

  • Наша программа испытала временные трудности. Пожалуйста, обратитесь в службу технической поддержки.

Конечным пользователям не все равно, о вашей внутренней организации программы, базах данных, стеках и т.д. Все, что они знают, это то, что, по их мнению, должно было работать просто не получилось, поэтому они ищут помощь.

Трассировки стека для журналов ошибок: вы можете сохранить их сами или отправить по электронной почте их технической поддержке, но не хотите показывать их конечным пользователям.

Ответ 4

Вы должны показывать только трассировки стека, когда ваше приложение находится в режиме отладки. В режиме производства вы должны показать общее сообщение об ошибке (или реализовать Exception.getUserFriendlyMessage()) и зарегистрировать ошибку (если возможно, указать идентификатор журнала).

Ответ 5

Строка, которую я хочу отобразить для пользователя, - Error description here.

Вы можете использовать Exception.getMessage() для этой цели, хотя, как предложил Хуан Мендес, подумайте о внедрении более "удобного" механизма сообщений об ошибках для отображения ошибок конечному пользователю.

Если вы не хотите, чтобы конечный пользователь видел имена ваших классов, методов и полей в стеке, рассмотрите использование обфускатора, например Proguard. Мне нравится печатать обфускацию stacktraces в файлах журналов, а затем использовать ReTrace, чтобы разузнать их для целей отладки.

(1) Есть ли что-нибудь потенциально чувствительное, которое вы бы не хотели видеть в стеке ошибок? Если да, то? Например, пути к файлам, имя сервера /IP и т.д.

Это может зависеть от того, какое исключение выбрасывается, я думаю. Выбор регистрации исключения или нет - это компромисс между безопасностью приложения и возможностью эффективной диагностики проблемы, я беру его в каждом конкретном случае и оставляю комментарии по мере необходимости, чтобы объяснить мои рассуждения о регистрации или не протоколировать его.

Ответ 6

Имена пользователей и пароли - это, очевидно, конфиденциальная информация, которую вы не хотите показывать пользователю. Кроме того, как вы сказали, пути к файловой системе, имена серверов и IP-адреса также должны быть скрыты.

Метод Exception.getMessage() возвращает только сообщение об исключении, а не всей трассировке стека. Но, похоже, в примере, который вы указали, 2-я и 3-я строки также являются частью сообщения об исключении, так что это вам не поможет.

НО, это не хорошая практика, когда-либо показывающая трассировки стека ваших пользователей. Они не только могут содержать конфиденциальную информацию, но и для пользователя, они примерно так же читабельны, как и клингон. Вместо этого вы должны регистрировать все неперехваченные исключения, а затем показывать пользователю более удобное для пользователя сообщение.

Ответ 7

Как говорили другие, вы не должны сообщать пользователям о трассе стека. Некоторые другие предложили вам отобразить текст getMessage(). Я рекомендую не делать это. Как Я уже говорил:

  • Сообщение было создано, когда было выбрано исключение. Поэтому он в лучшем случае может предоставлять только информацию с очень низким уровнем, которая может быть неприемлемой для сообщения пользователю.
  • Философски, используя сообщение, кажется мне против всей точки исключения, которая заключается в том, чтобы отделить обнаружение и инициирование обработки ошибок (часть throw) от завершения обработки и отчетности (часть catch). Использование сообщения означает, что сообщение должно быть хорошо для отчетности, которое несет ответственность за отчетность в месте, которое должно отвечать только за обнаружение и инициирование. То есть, я утверждаю, что getMessage() часть дизайна Throwable была ошибкой.
  • Сообщение не локализовано. Несмотря на свое название, getLocalizedMessage() не очень хорошо, потому что вы можете не знать, какой язык вы хотите использовать, пока не получите catch исключение (это отчет, который нужно перейти в системный журнал, прочитанный вашими английскими системными администраторами, или он появляется в окне для французского пользователя GUI?).