Description
Verbose error messages (CWE-209 — Generation of Error Message Containing Sensitive Information) occur when an application returns detailed internal error information in its HTTP responses, including stack traces, exception messages, internal file paths, database query strings, framework version numbers, server software versions, and configuration details. This information is invaluable to an attacker during the reconnaissance phase of a penetration test or a real attack — it dramatically reduces the time required to identify exploitable components and craft targeted payloads.
Classified under A05:2021 — Security Misconfiguration, verbose errors are frequently a symptom of debug mode being left enabled in production or of missing error handling configuration. They do not directly cause exploitation but provide the information needed to escalate other vulnerabilities. A stack trace that reveals a vulnerable library version, an internal path that suggests a file structure, or a SQL error that confirms injection — all of these materially assist an attacker.
The issue is particularly common at the boundary between development and production. Developers rely on verbose errors during development for rapid debugging. When deploying to production, error verbosity is often not explicitly configured or tested, resulting in the same detailed errors being visible to end users.
How It Works
A typical Django application in debug mode returns a full exception page when an error occurs:
GET /api/users/abc HTTP/1.1
Host: app.example.com
Response:
ValueError at /api/users/abc
invalid literal for int() with base 10: 'abc'
Request Method: GET
Request URL: http://app.example.com/api/users/abc
Django Version: 3.2.4
Python Version: 3.9.1
Installed Apps: ['django.contrib.auth', 'rest_framework', 'app.core', ...]
Installed Middleware: [...]
Traceback:
File "/home/deploy/app/venv/lib/python3.9/site-packages/django/core/handlers/exception.py",
line 55, in inner
File "/home/deploy/app/venv/lib/python3.9/site-packages/django/core/handlers/base.py",
line 217, in _get_response
File "/home/deploy/app/core/views.py", line 34, in get_user
user = User.objects.get(id=int(user_id))
This single response tells an attacker:
- The framework is Django 3.2.4 (check CVE databases for vulnerabilities)
- The Python version is 3.9.1
- The full server file path:
/home/deploy/app/ - The virtual environment structure
- The exact source code line responsible for the error
- All installed Django applications
SQL error messages are another high-value variant. A MySQL error returned directly to the client reveals the exact query structure:
Error: You have an error in your SQL syntax; check the manual that corresponds to your
MySQL server version for the right syntax to use near ''abc''' at line 1.
Query: SELECT * FROM users WHERE id = 'abc''
This confirms the application constructs SQL queries from user input, provides the exact query template for crafting injection payloads, and reveals the table name users.
API error responses frequently include exception class names, internal service names, and message queue topics — information that assists in mapping the internal architecture for targeted attacks.
Impact
- Technology fingerprinting — framework, language, and library versions reveal specific CVEs to target.
- SQL injection facilitation — SQL error messages confirm injection points and reveal query structure, dramatically simplifying attack development.
- Internal path disclosure — file paths reveal deployment structure, enabling path traversal and local file inclusion attack refinement.
- Configuration exposure — environment details, installed apps, and middleware configuration reveal the attack surface.
- Logic flaw discovery — exception messages describing unexpected input often reveal assumptions in business logic that can be exploited.
- Reconnaissance efficiency — verbose errors reduce the information gathering phase from hours to minutes.
Detection
- Trigger errors deliberately — send invalid data types, SQL injection characters, excessively long strings, null bytes, and Unicode characters to all input fields. Observe whether responses contain stack traces, SQL queries, or internal paths.
- Test error handling on all HTTP methods — error handling is often configured for GET but not for POST, PUT, or DELETE requests with unexpected payloads.
- Probe API endpoints with invalid parameters — supply strings where integers are expected, missing required fields, negative numbers, and zero values. Check whether error responses are generic or verbose.
- Check the 500 error page — request a path that is guaranteed to cause an error (e.g., a known broken endpoint) and check whether the 500 response is a generic "Internal Server Error" or a full exception trace.
- Test mobile app error handling — mobile apps that display or log API error responses may expose verbose server errors in the app UI or debug logs accessible via
adb logcat. - Look for debug pages — check for
/debug,/__debug__,/_ah/admin(App Engine),/jolokia(Spring), and similar development debug endpoints that should not be accessible in production.
Remediation
Configure a generic error handler in production. Return a simple, generic error message to clients for all unhandled exceptions. Log the full detail server-side:
# Django — set DEBUG = False in production settings
DEBUG = False
ALLOWED_HOSTS = ["app.example.com"]
# Custom error handler returns a generic message
@app.errorhandler(500)
def internal_error(error):
app.logger.exception("Unhandled exception")
return jsonify({"error": "An internal error occurred"}), 500
Suppress framework debug modes in all production environments. In Django: DEBUG = False. In Laravel: APP_DEBUG=false. In Spring Boot: server.error.include-stacktrace=never, server.error.include-message=never.
Log verbosely server-side, respond generically client-side. The detailed exception, stack trace, and context should be captured in server logs (Splunk, CloudWatch, ELK) for debugging but never transmitted to the client.
Implement structured error codes. Return a stable, opaque error code ("error_code": "ERR_USER_NOT_FOUND") that allows client-side handling without exposing implementation details. Avoid including exception messages or stack traces in API responses.
Test error responses as part of QA. Include error handling in the test suite — verify that 4xx and 5xx responses from all endpoints return only generic messages, never stack traces.
