Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Learn › Security › Web attacks and defenses

Information leakage

What do error responses and login failure messages tell the outside world?

The response a server returns when something fails can carry more than you might expect. An SQL statement inside an exception message, or the class names and line numbers inside a stack trace, show what the inside of the server looks like and give an attacker clues. If a login failure message tells "unknown user ID" apart from "wrong password", it also tells which IDs are registered.

Checked against real execution · Checked against the actual responses of a Spring Boot 4.1.1 (Spring Security 7.1.1) server for 20 combinations. What was compared is which items are in the response and whether the two responses are the same, not the message texts

Concepts in this lab

Error responses and information leakage
Information leakage through error responses is when the response a server returns on failure carries information from inside the server and sends it outside. An exception message can contain the SQL that was about to run or a file path, and a stack trace shows the framework in use and the structure of the code, so they give an attacker clues for the next attempt. The default error response of Spring Boot does not include the exception message or the stack trace. Information leaks when a setting is changed to always or when code puts the exception message into the response as it is. The basic rule is to put only a fixed message and an error ID in the response and to keep the details in the server log.
Stack trace
A stack trace is a record of which methods were being called, and in what order, at the moment an exception occurred. The first line has the class name and the message of the exception, and below it comes one line per method with the class name, method name, file name, and line number. If another exception was the cause, it is added with Caused by. It is the first thing a developer looks at to find the cause of an error. Because it shows the inner structure of a program in such detail, it is not put in the response to the user and is kept only in the log.
Account enumeration
Account enumeration is finding out which user IDs or email addresses are registered by using differences in the server's responses. If the login failure message tells "unknown user ID" apart from "wrong password", or if the sign-up and password recovery screens reveal whether an account exists, an attacker can request with different IDs and build a list of registered IDs. That list becomes the target of password guessing or of deceptive emails. To prevent it, make the responses for the two cases the same, down to the message and the status code, and reduce the difference in response time too. Spring Security by default treats the two cases as the same exception.
@ControllerAdvice and @ExceptionHandler
This feature lets you handle the exceptions raised in several controllers in one place. If you put an @ExceptionHandler method for each kind of exception inside an @ControllerAdvice class, an exception raised while a controller runs is passed to that method, which can build an error response in a consistent format.
HTTP status codes
A three-digit number that shows the result of the server's handling of a request. 2xx means success, 3xx means a move to another location (redirection), 4xx means a problem with the client's request, and 5xx means an error on the server side. Common ones are 200 (success), 201 (created), 400 (bad request), 401 (authentication required), 403 (no permission), 404 (not found), and 500 (server error).

Try these

Running with the default settings

  1. A GET /orders/7 request arrives. It is an ordinary request to look up order 7.
  2. jdbc.queryForMap(SQL, id): the column name price is misspelled as pirce in the SQL. The database rejects the SQL, and Spring converts that into a BadSqlGrammarException and throws it. The message of this exception contains the whole SQL statement that was about to run.
  3. No code catches the exception, so the request is forwarded to /error, and the BasicErrorController of Spring Boot builds the error response. The items included regardless of the settings are timestamp, status, error, and path. Up to here, the response only says that "an error happened".
  4. include-message=always: the exception message is put in the message item. The SQL statement goes into the response as it is, and the table name (orders) and the column names (id, item) are exposed.
  5. include-stacktrace=always: the stack trace is put in the trace item. The first line has the class name of the exception and its message (including the SQL), and below it the methods the exception passed through are listed with their class names and line numbers. The class name of the database driver (org.h2…) also shows which database is used.
  6. The server sends a 500 response. The items in the body are timestamp, status, error, trace, message, path. Whoever sent the request can read the SQL statement, class names and line numbers, database product name.

Result of this run

Code

# application.properties
spring.web.error.include-message=always
spring.web.error.include-stacktrace=always

// Order lookup. A column name in the SQL is wrong (price is written as pirce)
@GetMapping("/orders/{id}")
public Map<String, Object> order(@PathVariable long id) {
    return jdbc.queryForMap(
        "select id, item, pirce from orders where id = ?", id);
}

// Request: GET /orders/7

Assumptions of this model

12 error responses (2 message settings × 3 stack trace settings × whether the trace parameter is present), 2 responses built by hand, and 6 login failures were checked against the actual responses of a Spring Boot 4.1.1 (Spring Security 7.1.1, Spring JDBC, H2) server started on 127.0.0.1. The message texts and the number of stack trace lines vary with the version and the environment, so what was checked is which items are in the body, whether the SQL statement, the stack trace lines, and the name of the database driver are visible in the body, whether the two login failure responses are the same, which exception occurred, and how many times the password comparison was called. The message values on screen were seen on the PC used to write this lab, and the example errorId is a made-up value. The checks were based on requests that receive JSON; for a request that receives HTML, as a browser does, the same content appearing on the error page was confirmed for one setting. The setting names are the spring.web.error.* names of Spring Boot 4.1.1 (server.error.* up to Spring Boot 3). include-exception, include-binding-errors, and the on_param value of message are not covered. That the whole exception is written to the log in the safe response was confirmed by collecting the log output. Not run: the difference in response time was not measured (only the number of times the password comparison was called was checked), and the messages on the sign-up and password recovery screens are an explanation that follows the authentication guidance of OWASP. How much the leaked information helps in a real attack differs from server to server.

Previous lab: Clickjacking and open redirects · Next lab: Rate limiting

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these