Secure Development - Error Handling

Mar 09 2010

At first glance, error handling may seem more like a functionality issue than a security concern. However, when done improperly (or worse, not at all), error handling can lead to security holes in your application. The classic example of error handling working in favor of the bad guys are failed logins, where the system gives a different error message for an incorrect username vs. an incorrect password. This allows the attacker to first figure out a working username and then focus on brute-forcing the password for that user. This is much faster than than having to try an exponentially-higher number of all username-password combinations.

In general, good error handling is important because error messages can reveal implementation details and pinpoint flaws in your application if they are not used correctly. Here are some common examples of incorrect error handling:

  1. Stack traces due to uncaught exceptions, which reveal application logic/structure
  2. Verbose database errors that reveal SQL statement structure (or worse, database table structure)
  3. JSP compilation errors containing file paths (or ASP/PHP interpreter errors, etc.)
  4. Repeating user-generated data back in an error message (can lead to reflected XSS)
  5. Inconsistent error messages (e.g. access denied vs. not found)
  6. Errors causing server to crash (denial of service)

Let's look at a practical example of how an attacker's knowledge of a file path can help in an exploit scenario. Say an attacker is trying to deface a site's home page by uploading his own index.html file (this assumes a path traversal vulnerability exists and an uploaded file can overwrite existing files in the web server root). Initially, the attacker does not know the folder structure inside of the web server root, but if he can induce an error message that includes the full path of the page in question, he can learn a great deal. For example, let's say he observes the following error message on a page located at http://server.com/UserApp/upload/upload.php:

No such file or directory in /secret/server/root/UserApp/upload/upload.php on line 251

From this, he now knows that the web server root is /secret/server/root, and also that he would need to use two sets of double-periods to reach the server root: uploading a file to ../../index.html will overwrite the server's main home page. Alternatively, the attacker could also attempt to use the full path if the .. notation is filtered out by a (poorly-implemented) blacklist-based input validation routine.

To make sure you're handling errors correctly, you can use the following techniques:

  1. Define clear and consistent error handling: Show only short and meaningful messages to the user (including how to fix the error, if possible). Log more detailed debugging information on the backend for the system administrator (using a standard logging framework such as log4j). Do not show any raw debugging data to the user, such as stack traces or exception messages.
  2. Handle all errors using exceptions, not return codes: Java and almost all web application languages support exceptions for error handling. Use this functionality rather than relying on function return values to indicate error conditions. Return error codes are problematic for many reasons: they can be easily ignored (unlike exceptions, which must be caught), their meaning needs to be interpreted, and they are often not distinguishable from valid data (especially when called by other components/developers). Exceptions also provide far greater flexibility in throwing them "up" the call stack without having to write explicit return-checking code into every function.
  3. Catch specific exceptions and handle them intelligently: Do not use the generic Exception class to catch all exceptions (or worse, swallow them silently at the top level of your application). For each function that throws an exception, handle each type of exception separately and take appropriate action (your application may be able to recover gracefully in many cases). If no recovery is possible, display an appropriate error message as described in 1. above. If needed, don't hesitate to create lots of custom Exception classes that describe the many different errors your application can encounter.
  4. Precompile your JSPs before deploying to production (Java-specific): This prevents any compilation errors from being displayed, and it also increases performance slightly.
  5. Modify default error pages (404, 401, etc.): Depending on your web application server, the default pages can give away lots of detailed information. For example, you could replace the 404 page with a customized error message and your site's search box to help the user find the desired content.

Handling errors properly is important not only for the stability of your application, but it also has significant security implications. Following the five guidelines above will keep your error messages from giving away valuable information that attackers can use against you.

Next week, we'll talk about access control issues. This problem usually occurs after a user is already authenticated, but he is then able to access other users' data or administrative functions that are outside of the intended privileges of his account.

About the Author

Daniel is a business and technical systems analyst with a background in IT security and software development. He has four years of experience in the IT security field, including published academic research. His main areas of expertise include secure development, network security, and authentication. In addition to security, Daniel has a software development background in languages such as Java, PHP, SQL, and Perl. He also has over 12 years experience working with and administering various versions of Linux and related open-source software.

Disclaimer

The words and opinions expressed here are those of each article's respective author, and do not necessarily represent the views of CapTech Ventures.