Introduction
When an application error occurs, either due to user input or an internal function, we as conscientious developers want to present an error message that will help the end user correct the problem. However, it is possible to be too helpful with your error handling approach. By providing overly detailed application error messages to your users, you can actually be opening your site to hackers. Hackers spend the majority of their time performing reconnaissance on a site, slowly gathering multiple pieces of information to determine how a site is vulnerable. Sometimes, it is a seemingly innocuous piece of information in an application error message that provides an attacker with the last piece of the puzzle necessary for him to launch a devastating attack.
User Input Errors
A classic example of providing too much information in an application error message is an authentication failure message on a login screen. At first, it would seem helpful to utilize an error handling method that presents a distinct message indicating that the user ID entered was not found versus indicating that the password was incorrect. And, in fact, it is helpful--but more helpful to an attacker than to a legitimate user.
Imagine that an attacker is trying to break into a Web application. He doesn't know any existing user IDs or passwords for the site, so he attempts a "brute force" or "dictionary" attack. A list of common user IDs (such as admin, user, and guest) is paired with a list of common passwords (such as password, admin, and Elvis). Every possible combination of the two is tried against the website to see if any of them works. If lists of significant sizes are used, such as actual electronic dictionaries, then the number of possible combinations could run into billions. Even if an automated tool is used to make the requests, it could take weeks or months to find a match.
If, however, the website's error handling process provides distinct messages that distinguish between an invalid user ID and an invalid password, then the attacker's job is greatly simplified. Once he comes across a user/password combination that displays an "Invalid Password" application error message, he can stop checking every other user ID in his list. He now knows that the guessed user ID exists in the system, and he can focus on breaking into that account. If his lists of potential users and passwords each contained 5000 items, his task is now reduced from making 25 million requests to a much more manageable 5000 requests. Making 5000 requests could be accomplished in a matter of hours instead of weeks, which means it is much more likely that the attacker could obtain access to the website before the site administrator notices the unusual behavior.
Best Practice for User Errors
In this case, the best course of action for the developer working on an error handling approach is to create a single application error message that appears regardless of whether the user ID was not found or the password was incorrect. A good example is: "Invalid user ID or password". This error handling message is just helpful enough to let a legitimate user know that he needs to re-enter his login credentials, while not providing any additional information that could help an attacker perform a brute force attack.
Internal Errors
As we have seen, it is important not to provide too much detail when formulating error handling procedures for users. It is even more important to withhold details when an error occurs in the application itself. There are many possible reasons why this might happen: a required database or Web service might be unavailable; a component license might have expired; or it might even just be a bug in the code. Regardless of the cause, it is vital to never reveal the specifics of the application error to the user.
Attackers thrive on specifics. By allowing a detailed component error to be displayed, a database error for example, an attacker might be able to determine the type of database being used, its version, the Operating System on which the database is running, the Web server being used, possibly even some of the source code of the Web application. All of these pieces of data are ammunition that the attacker can use to attack the website. For example, if the error handling method allows the attacker to find that the database being used is Microsoft SQL Server 2000, the very next thing he will do is search the Internet for known security vulnerabilities in Microsoft SQL Server 2000. Some application error messages even reveal the complete or partial path of the offending file, or the name of the offending row or column in a table, in an attempt to help a user track down the origin of the problem. This form of application error handling will give an attacker more information that can be used for malicious purposes.
Many websites attempt to prevent this information disclosure and still try to be helpful to the user by displaying a friendly application error message with the component error code embedded in it. For example: "An error has occurred. Please contact tech support and reference the error code 123-456-789". However, this error handling method is not much better than before, and for the same reason. The attacker will simply search for "123-456-789". Chances are good that documentation for the application error code exists someplace on the Internet, and if it does, an attacker will find and use it.
Custom Error Codes
Some web applications create their own internal error codes to display to users as part of the error handling process. If any application error occurs in the database layer, for instance, they display a message like "Please contact tech support and reference the error code 01". If any application error occurs in a licensing component, the message changes to "Reference the error code 02". This error handling method does provide a degree of security over previous methods, since the meanings of the internal codes will not be found in a search of the Internet. Unfortunately, this form of error handling is still unsafe.
It is the distinctiveness of the application error, rather than its content, that is the security flaw in this case. Just as in the error handling method discussed earlier, wherein the attacker took advantage of the difference between the invalid-user response and the invalid-password response; a clever attacker will notice that some of his attacks return error code "01," and some return error code "02." While this error handling approach does not deliver much information, it may be enough to help him launch a successful attack. Furthermore, it is not information that a legitimate user really needs.
Death by a Thousand Cuts
Even a small amount of data leakage occurring via error handling can be extremely dangerous. It is rare that a single error message contains all the information that an attacker needs to compromise the system. Instead of a single, deadly "silver bullet", a much more common attack is one that precipitates a "death by a thousand cuts". It is the sum of the many tiny individual bits of information provided by improper application error handling that provides an attacker with enough knowledge to successfully exploit the security weaknesses of the system.
Best Practice for Internal Errors
One solution, of course, is to display a very generic "An error occurred" message for anything that could possibly go wrong in the system. There is no detail to the application error message and no distinctiveness either. There is no information that an attacker could use. While this approach to error handling is very secure, it is not very helpful to the developers or technical support staff. If a user encounters an application error, there is no useful information that he can provide the application owners that would help to reproduce or debug the problem.
The error handling approach that provides the best mix of security and functionality is the issue tracking technique. Whenever an application error occurs, a detailed log is made of the error conditions. This log is assigned a unique issue ID, and stored someplace securely on the server, such as a database or directory that cannot be accessed via a Web browser. An error message that contains the issue ID is returned to the user. The user can report the issue ID to technical support, who can reference the detailed log from the secure location on the server. This error handling approach provides detailed information to the application owner, while keeping it from the user and any potential attackers.
Issue tracking also satisfies the condition that errors not be distinct based on the type of application error. Every error handling message is unique, since every error will have a unique tracking ID. Since no pattern can be discerned from the responses, however, this technique is just as secure as providing exactly the same error message for any error situation. To provide an additional measure of security during error handling, use a completely random number or globally unique identifier (GUID) for the issue tracking ID rather than a sequential number. This will prevent attackers from performing "social engineering" attacks by contacting technical support with easily-guessed tracking numbers.
Conclusion: A Simple Solution
It can be difficult to determine the correct amount of feedback to present to a user in the event of an application error. If the developer tries to be as helpful as possible with his error handling approach, it can backfire on him, since information that is helpful to a user can also aid an attacker in attacking the system. On the other hand, if the developer is overly vague, then it is much more difficult to repair the application error since the user won’t have any data to provide the developers or the technical support team.
For a simple solution, follow these guidelines when creating application error messages:
- When the application error is due solely to invalid user input, such as an invalid password, then the application should present a generic and non-distinct error message.
- When the error occurs in the application code, such as a database error, then the application should provide a generic message containing a tracking number. The tracking number can then be used by technical support to help resolve the issue.
By following these guidelines, you can thwart the attempts of attackers who try to gain knowledge of your application through its error handling messages.