Technical information leak - Stacktrace - Dart

Technical information leak - Stacktrace - Dart

Need

Prevention of technical information leakage from system error traces

Context

  • Usage of Dart for building cross-platform mobile, web, and desktop applications
  • Usage of Shelf for managing data storage and retrieval

Description

Non compliant code

        import 'package:shelf/shelf.dart' as shelf;

void main() {
  var handler = const shelf.Pipeline().addMiddleware(shelf.logRequests())
      .addHandler(_handleRequest);

  shelf.serve(handler, 'localhost', 8080).then((server) {
    print('Serving at http://${server.address.host}:${server.port}');
  });
}

shelf.Response _handleRequest(shelf.Request request) {
  try {
    // ... some code that might throw
    throw Exception('An error occurred');
  } catch (e, s) {
    return shelf.Response.internalServerError(body: '$e\\n$s');
  }
}
        
        

The above code is an example of a backend application in Dart using the Shelf framework. The function _handleRequest() is designed to handle incoming requests. However, it contains a vulnerability.

When an error occurs (in this case we're forcibly throwing an exception for demonstration), the catch block catches the error and sends it as a response with a status of 500 (Internal Server Error). The problem here is that it sends the error message and the stack trace ( $e\\n$s) as the body of the response.

This is a technical information leak vulnerability, specifically a stack trace leak. The stack trace provides detailed information about the path the program took to get to the point where the error occurred. This information can be very useful for debugging, but it can also provide an attacker with valuable insights into the inner workings of the application, potentially revealing sensitive information or exploitable weaknesses.

In a production environment, you should never expose such detailed error information to the client. Instead, log the error and stack trace server-side where it can be reviewed by authorized personnel, and send a generic error message to the client.

Steps

  • Remove or modify the error message that is currently being sent in the response body. It should not contain any system or technical information.
  • Implement a custom error handling mechanism. This should catch any exceptions and handle them appropriately, without revealing any sensitive information.
  • Instead of returning the actual error and stack trace, return a generic error message to the client. This message should be informative enough for the user to understand what went wrong, but not detailed enough for an attacker to gain any useful information.
  • Log the detailed error message and stack trace server-side for debugging purposes. This way, developers can still access this information when needed, but it is not exposed to the client.
  • Consider using an error tracking service that securely stores error information and provides a unique error ID for each occurrence. This ID can be safely returned to the client and used by developers to find the corresponding error details in the tracking service.

Compliant code

        import 'package:shelf/shelf.dart' as shelf;
import 'package:logging/logging.dart';

final _logger = Logger('server');

void main() {
  var handler = const shelf.Pipeline().addMiddleware(shelf.logRequests())
      .addHandler(_handleRequest);

  shelf.serve(handler, 'localhost', 8080).then((server) {
    print('Serving at http://${server.address.host}:${server.port}');
  });
}

shelf.Response _handleRequest(shelf.Request request) {
  try {
    // ... some code that might throw
    throw Exception('An error occurred');
  } catch (e, s) {
    _logger.severe('An error occurred', e, s);
    return shelf.Response.internalServerError(body: 'An error occurred. Please try again later.');
  }
}
        
        

The updated code now includes a custom error handling mechanism that catches any exceptions and handles them appropriately, without revealing any sensitive information.

In the _handleRequest function, we have a try-catch block. If an error occurs in the try block, the catch block is executed. Instead of returning the actual error and stack trace, we now return a generic error message to the client: 'An error occurred. Please try again later.' This message is informative enough for the user to understand what went wrong, but not detailed enough for an attacker to gain any useful information.

We also added a logger from the logging package. When an error occurs, the error message and stack trace are logged server-side using _logger.severe('An error occurred', e, s);. This way, developers can still access this information when needed, but it is not exposed to the client.

Consider using an error tracking service that securely stores error information and provides a unique error ID for each occurrence. This ID can be safely returned to the client and used by developers to find the corresponding error details in the tracking service.

References