This weakness occurs when an application unintentionally includes sensitive information—such as internal IDs, file paths, API keys, or database details—in data…
This weakness occurs when an application unintentionally includes sensitive information—such as internal IDs, file paths, API keys, or database details—in data sent to users or external systems. Unlike access control failures, the information isn't stolen; the application itself leaks it. This can expose architectural details, enable account enumeration, or reveal credentials that attackers can use for further compromise.
02How It Happens
Developers often include internal identifiers, debug information, or system details in responses without realizing they're visible to end users. This happens most commonly in error messages, API responses, HTML comments, or log files that are accidentally exposed. The root cause is usually a lack of awareness about what data is sensitive in context, combined with insufficient review of what gets sent to clients. Even "harmless" details like sequential user IDs, database table names, or server software versions can be pieced together by attackers to build a map of the system.
03Real-World Impact
Leaked information can enable account enumeration (testing which usernames exist), facilitate targeted attacks (knowing the exact software version helps attackers find known exploits), or expose credentials embedded in responses. In some cases, internal file paths or API endpoints revealed in error messages allow attackers to bypass security controls or discover hidden functionality. The impact ranges from minor information disclosure to complete system compromise, depending on what is leaked and how an attacker uses it.
04Vulnerable & Fixed Patterns
Vulnerable pattern
import json
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/user/<user_id>')
def get_user(user_id):
try:
user = database.query(f"SELECT * FROM users WHERE id = {user_id}")
return jsonify({
'user_id': user['id'],
'username': user['username'],
'internal_account_number': user['account_number'],
'database_path': '/var/db/app/users.sqlite',
'api_key': user['api_key']
})
except Exception as e:
return jsonify({'error': str(e), 'query': f"SELECT * FROM users WHERE id = {user_id}"})
Why it's vulnerable: The response includes internal account numbers, database paths, and API keys that should never be sent to a client. The error handler also echoes the SQL query, revealing database structure.
Fixed pattern
import json
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/user/<user_id>')
def get_user(user_id):
try:
user = database.query("SELECT id, username FROM users WHERE id = ?", (user_id,))
return jsonify({
'user_id': user['id'],
'username': user['username']
})
except Exception as e:
app.logger.error(f"Database error: {e}")
return jsonify({'error': 'Unable to retrieve user'}), 500
Classify data by sensitivity. Identify which fields are internal-only (IDs, paths, credentials, notes) and which are safe to expose to clients.
Whitelist response fields. Explicitly select only the fields that should be returned; never use SELECT * or serialize entire objects.
Sanitize error messages. Log full error details server-side; return generic messages to clients (e.g., "An error occurred" instead of SQL syntax errors).
Remove debug information. Strip stack traces, query strings, file paths, and version numbers from production responses.
Review API contracts. Document what each endpoint returns and audit responses against that contract during code review.
Scan for hardcoded secrets. Use automated tools to detect API keys, passwords, or tokens accidentally committed to code or embedded in responses.
06Signs You May Already Be Affected
Check your application's network responses (using browser developer tools or proxy logs) for unexpected fields like internal IDs, file paths, database names, or server hostnames. Review error pages and API responses for stack traces, SQL queries, or configuration details. Search your codebase for places where entire database records or user objects are serialized and returned without filtering.