Weakness reference
CWE-550

Server-generated Error Message Containing Sensitive Information

When a server encounters an error, it sometimes responds with detailed diagnostic information intended to help developers debug the problem. If that error…

01Summary

When a server encounters an error, it sometimes responds with detailed diagnostic information intended to help developers debug the problem. If that error message is visible to end users or attackers, it can leak sensitive details like file paths, database names, software versions, or internal code structure. This information becomes a roadmap for further attacks.

02How It Happens

Applications often rely on default error handling that exposes internal state. When an exception occurs—a database connection failure, a missing file, a code error—the framework or language runtime generates a detailed error page or message. In development, this is helpful. In production, if these messages reach untrusted users, they reveal the application's architecture and potential weak points. The root cause is typically a lack of error handling logic that distinguishes between what developers need to see (full stack traces, SQL queries, file paths) and what users should see (a generic "something went wrong" message).

03Real-World Impact

Exposed error messages accelerate reconnaissance. An attacker who sees a stack trace learns the programming language, framework version, and internal file structure. A database error revealing table names or SQL syntax helps craft injection attacks. Exposed file paths show where sensitive files are stored. Configuration details like API endpoints or service names narrow the attack surface. While not a direct vulnerability itself, information disclosure through error messages significantly reduces the effort required to find and exploit other weaknesses.

04Vulnerable & Fixed Patterns

Vulnerable pattern
from flask import Flask, request
import sqlite3

app = Flask(__name__)

@app.route('/user/<user_id>')
def get_user(user_id):
    conn = sqlite3.connect('app.db')
    cursor = conn.cursor()
    # No error handling; exceptions propagate to user
    cursor.execute(f"SELECT name FROM users WHERE id = {user_id}")
    result = cursor.fetchone()
    return {"user": result[0]}

Why it's vulnerable:
Any database error, SQL syntax error, or missing file will return a full stack trace to the client, revealing the database schema, file paths, and code structure.

Fixed pattern
from flask import Flask, request
import sqlite3
import logging

app = Flask(__name__)
logger = logging.getLogger(__name__)

@app.route('/user/<user_id>')
def get_user(user_id):
    try:
        conn = sqlite3.connect('app.db')
        cursor = conn.cursor()
        cursor.execute("SELECT name FROM users WHERE id = ?", (user_id,))
        result = cursor.fetchone()
        if not result:
            return {"error": "User not found"}, 404
        return {"user": result[0]}
    except Exception as e:
        logger.error(f"Database error: {e}", exc_info=True)
        return {"error": "An error occurred. Please try again later."}, 500
Vulnerable pattern
<?php
$user_id = $_GET['user_id'];
$conn = mysqli_connect('localhost', 'root', 'password', 'app_db');

if (!$conn) {
    die("Connection failed: " . mysqli_connect_error());
}

$query = "SELECT name FROM users WHERE id = " . $user_id;
$result = mysqli_query($conn, $query);

if (!$result) {
    die("Query error: " . mysqli_error($conn));
}

echo json_encode(mysqli_fetch_assoc($result));
?>

Why it's vulnerable:
mysqli_connect_error() and mysqli_error() expose database credentials, server names, and SQL syntax to the client. The unparameterized query also hints at injection risk.

Fixed pattern
<?php
error_reporting(E_ALL);
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', '/var/log/app_errors.log');

$user_id = $_GET['user_id'];
$conn = mysqli_connect('localhost', 'root', 'password', 'app_db');

if (!$conn) {
    error_log("Database connection failed: " . mysqli_connect_error());
    http_response_code(500);
    echo json_encode(["error" => "An error occurred. Please try again later."]);
    exit;
}

$query = "SELECT name FROM users WHERE id = ?";
$stmt = mysqli_prepare($conn, $query);
mysqli_stmt_bind_param($stmt, "i", $user_id);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);

if (!$result) {
    error_log("Query error: " . mysqli_error($conn));
    http_response_code(500);
    echo json_encode(["error" => "An error occurred. Please try again later."]);
    exit;
}

echo json_encode(mysqli_fetch_assoc($result));
?>

05Prevention Checklist

Implement centralized error handling.
Catch exceptions at the application level and log them server-side; return only generic messages to clients.
Disable debug mode in production.
Set debug=False in frameworks, disable stack trace display, and ensure display_errors is off in PHP.
Log errors securely.
Write detailed error information (stack traces, SQL queries, file paths) to server-side logs with restricted access, never to the client response.
Use parameterized queries.
Prevents SQL errors from leaking schema details and blocks SQL injection.
Test error paths.
Deliberately trigger errors (bad input, missing resources, database failures) and verify that responses are generic and non-revealing.
Monitor error logs.
Regularly review server logs for patterns that suggest reconnaissance or exploitation attempts.

06Signs You May Already Be Affected

Check your application's error responses by triggering common failures: request a non-existent page, submit invalid input, or simulate a database outage. If the response includes file paths, stack traces, SQL queries, software versions, or internal hostnames, you are leaking information. Review your web server and application logs for entries that show detailed error messages being sent to clients.