Generation of Error Message Containing Sensitive Information
This weakness occurs when an application displays error messages that reveal sensitive details—such as database structure, file paths, usernames, API keys, or…
This weakness occurs when an application displays error messages that reveal sensitive details—such as database structure, file paths, usernames, API keys, or internal system configuration—to users who should not have access to that information. An attacker can use these leaked details to plan further attacks or gain unauthorized access to systems and data.
02How It Happens
Developers often include detailed error information in messages to aid debugging during development. When these verbose error handlers are not properly restricted or sanitized before being shown to end users, sensitive data becomes visible. This commonly happens when exceptions are caught and their full stack traces are displayed, when database error messages are echoed directly to the client, or when file system paths are included in user-facing error pages. The root cause is a failure to distinguish between errors meant for developers (logged securely) and errors meant for users (generic and safe).
03Real-World Impact
An attacker can use exposed error messages to map out the application's internal structure, identify software versions and libraries in use, discover valid usernames or email addresses, or learn about database schemas. This reconnaissance significantly lowers the barrier to launching targeted attacks such as SQL injection, authentication bypass, or privilege escalation. In regulated environments, exposing sensitive information in error messages can also trigger compliance violations and breach notification requirements.
04Vulnerable & Fixed Patterns
Vulnerable pattern
import sqlite3
def fetch_user_data(user_id):
try:
conn = sqlite3.connect('/var/db/app.db')
cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
return cursor.fetchone()
except Exception as e:
# Directly returning exception details to user
return {"error": str(e)}
Why it's vulnerable: The exception message may contain the database file path, SQL syntax details, or other internal information that is then returned to the client.
Fixed pattern
import sqlite3
import logging
def fetch_user_data(user_id):
try:
conn = sqlite3.connect('/var/db/app.db')
cursor = conn.cursor()
cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
return cursor.fetchone()
except Exception as e:
# Log the full error for developers; return generic message to user
logging.error(f"Database error: {e}", exc_info=True)
return {"error": "An error occurred while retrieving data. Please try again later."}
Vulnerable pattern
<?php
$user_id = $_GET['id'];
try {
$mysqli = new mysqli("localhost", "app_user", "password123", "app_db");
$result = $mysqli->query("SELECT * FROM users WHERE id = $user_id");
echo json_encode($result->fetch_assoc());
} catch (Exception $e) {
// Directly echoing exception to user
echo "Error: " . $e->getMessage();
}
?>
Why it's vulnerable: The exception message may expose the database host, credentials in error logs, or SQL syntax, and the unparameterized query itself is also vulnerable to injection.
Fixed pattern
<?php
$user_id = intval($_GET['id']);
try {
$mysqli = new mysqli("localhost", "app_user", "password123", "app_db");
$stmt = $mysqli->prepare("SELECT * FROM users WHERE id = ?");
$stmt->bind_param("i", $user_id);
$stmt->execute();
$result = $stmt->get_result();
echo json_encode($result->fetch_assoc());
} catch (Exception $e) {
// Log full error securely; return generic message to user
error_log("Database error: " . $e->getMessage());
http_response_code(500);
echo json_encode(["error" => "An error occurred. Please try again later."]);
}
?>
05Prevention Checklist
Separate error handling for users and developers. Log detailed errors (stack traces, SQL, file paths) to secure server-side logs; display only generic, user-friendly messages in responses.
Disable verbose error display in production. Set display_errors = Off in PHP, use DEBUG = False in Django, and configure logging frameworks to suppress stack traces from client-facing output.
Sanitize all error messages before display. Remove file paths, database names, usernames, version numbers, and internal URLs from any message shown to users.
Use HTTP status codes appropriately. Return 500 Internal Server Error for unexpected failures rather than exposing the nature of the failure in the response body.
Test error paths regularly. Trigger intentional errors (invalid input, database unavailability, missing files) in a staging environment and verify that no sensitive information leaks in the response.
Implement centralized error handling. Use middleware, exception handlers, or global error pages to ensure all errors are processed consistently and safely.
06Signs You May Already Be Affected
Review your application's error pages, API responses, and server logs for exposed details: file paths like /var/www/html/..., database connection strings, usernames, SQL syntax, or stack traces mentioning internal libraries or versions. Check browser developer tools (Network tab) for error responses that contain more detail than a user should see. If your application logs are accessible to unauthorized users or if error messages are displayed in plaintext on public-facing pages, you are likely affected.