Direct answers
what really should we do to catch all kind of exceptions without showing any error messages to clients?
The best way to enforce this is to catch exception top-level (e.g. in the api controllers). My experience is with ASP.Net, which gives you clean ways of doing so, e.g. the HandleError attribute.
Is There any OpenAPI or Exception Email-Friendly API for that?
I am unsure of other existing solutions as I'm strictly a .Net developer. I would assume similar solutions exist for other frameworks. If not, then you can still develop one yourself. The main focus is to catch it top-level to ensure that all of your application's exceptions are caught. Ideally, you'd want a blanket solution that automatically covers the entire API.
Is it good to show exception messages, or just Red Flag, or some Color that when the client sees, they would contact technicians?
I will elaborate more on this below. In short: curated willful exceptions (i.e. explicitly written by your developer with the intent to communicate something to the end user) can be shown, unexpected exceptions (i.e. not explicitly written by your developer) or intentionally private exceptions (i.e. written by your developers but not with the intent to communicate with the user) should not be shown.
In all cases, exception details other than the message should always be hidden from end users. At most, you can show it e.g. only if the current authenticated user is an admin of the system, assuming that the admins are people who are trusted with the exception details.
In absence of an allowed error message to show, you should still clearly communicate to the end user that an error has occurred. You'd also want to log the real exception somewhere in the backend so that a developer is able to figure out what exactly went wrong.
Never hide an exception and then also not log it. This is the equivalent of a swallowed exception and is an obstacle to proper debugging.
Assuming you have a software which you gave to clients.
This is an interesting point.
If the customer is running the application on their own server, then you should not obfuscate the errors, as the customer would be wholly unable to debug any issue with the application they bought.
I (cynically) assume you can get away with doing so legally, and it might be an efficient way to ensure they come to you for support, but I disagree with this approach on an ethical level.
If the user bought your software and runs it on their own hardware, they deserve to know how the software works and exactly what went wrong.
Besides, if the customer has access to the application files, they are already able to figure out anything about the application that an exception would reveal. This is different from e.g. a web service, as the end users do not have access to the application files and therefore cannot reverse engineer them.
In more detail
Pretty much any discussion on the exposure of exceptions (naturally occuring ones or intentionally thrown ones) ends up as a debate on opinions. Different people expect different behaviors and thus find different approaches acceptable.
I'm not going to focus on the subjective, as we won't find a consensus. However, I will give you a collection of tips on how to handle the (non-)exposure of exceptions to the end user in a way that maximizes utility (UX, debugging) while still avoiding security leaks.
Do not expose exceptions to the end user
You're already aware of this, but I'm reiterating the point. Leaking naked exceptions reveals information about your application (e.g. if you're using a particular library, or method names that suggest particular implementations).
Do expose user-friendly messages
User's don't like errors that don't make sense to them. Did they do something wrong? Did the server do something wrong? They are left at a loss.
Even a vague message is better than no message at all, but of course a message that is not vague (within reason) is even better.
Willful exceptions versus unexpected exceptions
In past projects, I have taken the following approach in order to handle error logic sensibly (in my opinion). The main point here is that there are some exceptions where you do want to reveal it to the user (at least the message), e.g. when a validation fails, and this approach allows for that while still preventing unwanted messages from leaking.
When a willful exception was thrown (e.g. due to a validation error), we always used a PublicException
(our own class, nothing more than a PublicException : Exception {}
) or any exception that derives from PublicException
. We then handled any exception at the top level (the api controller) and separated them based on type:
- In DEBUG mode (
#if DEBUG
), full exceptions (.ToString()
) were returned because debug mode is only to be used by developers. This is dangerous if you run the risk of ever deploying a debug version on production but due to our build pipeline we weren't particularly afraid of this happening.
- If it's a
PublicException
, return the exception message as part of the response
- If it's not a
PublicException
, return "An error has occurred" as the response message
This gave developers the ability to raise an exception which they specifically intended to carry a message to the end user.
However, any exceptions that were not foreseen by the developers (random failures, unexpected runtime errors, ...) would not be shown to the end user as this message was not verified by a developer.
This worked well. However, there is a "don't flow-by-exception" argument here. I'm a big fan of prohibiting flow-by-exception in general, but that is not synonymous with never raising exceptions in the first place.
When you use this PublicException
approach, developers may become sloppy and start wantonly using exceptions for program flow, and you need to actively check up on the code quality to ensure that this doesn't happen. But to be honest, you would always need to be checking for antipatterns even if you weren't using the PublicException
approach.
Store the exceptions in a developer-accessible way
Extending the previous tip, when we handled unexpected exceptions that would be hidden from the end user, what we did was log this exception to the error log using a unique ID (GUID in our case), and we then added the UID to the error message. The user would get something like:
An unexpected error has occurred for your request. If this problem persists, please contact the {COMPANY} helpdesk and reference your error ID ({GUID})
This achieves the best of both worlds. The issues with giving the end user the full exception are already clear. But if you just log the exception, then it becomes hard for a developer to figure out which exception in the error log belongs to which user (unless you have perfect logging, which in reality you often don't).
By giving the user a direct reference to the exception, you've basically given them the ability to tell the developer exactly what went wrong (= exception details) but you've hidden the information from the user themselves (= they only know a meaningless GUID number).