Prevention of technical information leakage from system error traces
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.
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.