Slide 5 of 28
Part 1 · What Is It?Slide 5
Slide 5 · Error Messages
A stack trace in production is a tour of your internal infrastructure.
What attackers learn from verbose API errors — and why developers leave debug mode on.
What a verbose error exposes

A single unhandled exception response can reveal: the framework and version (Java Spring 2.7.3, Django 4.1), internal file paths (/app/src/services/UserService.java:142), SQL queries with table and column names, library names and versions (cross-referenceable with CVE databases), environment variable names, and sometimes partial data from the failed operation.

EXAMPLE
What a developer sees vs. what an attacker reads

The error: POST /api/users/search with a malformed input returns HTTP 500 with body:

java.sql.SQLException: You have an error in your SQL syntax near ’’ at line 1 — Query: SELECT * FROM user_accounts WHERE email = ’’ AND status = ’active’ AND org_id = 42

at com.company.api.repository.UserRepository.findByEmail(UserRepository.java:87)

at com.company.api.service.UserSearchService.search(UserSearchService.java:134)

What the attacker learned: (1) The database table is named user_accounts. (2) Columns include email, status, and org_id. (3) The query is not parameterized (the input is directly in the SQL string — SQL injection likely). (4) The exact source file and line number. This verbose error just handed the attacker a SQL injection roadmap.
Why debug mode stays on

Debug mode is enabled during development for good reason — detailed errors speed up debugging. The failure is a deployment process that doesn’t enforce environment-specific configuration. DEBUG=True is committed to the repo, never changed for production, and ships unchanged. The fix is not removing debug mode from development — it’s ensuring production deployments enforce DEBUG=False independently of what’s in source control.

← Back Why does this happen? →