ASVS 5.0 V13.4.2 is a Level 2 requirement. Debug modes must be disabled for all components in production, so that debugging features and the information they print are not available to anyone who finds them.
"All components" is the important phrase. The web framework is the obvious one, but databases, proxies, queue consoles and build tools have debug switches too.
Why it matters
Debug mode turns private internals into public output. A framework debug page can show source snippets, environment variables, connection strings and a working interactive console. Some debug consoles allow code execution by design.
Debug settings also tend to survive by accident. A flag set to true during an incident on Friday is still true on Monday.
How to implement it
Make production the default and development the exception, so a missing variable fails safe.
Django:
import os
DEBUG = os.environ.get("APP_ENV") == "development"
ALLOWED_HOSTS = ["app.example.com"]
Flask:
app = Flask(__name__)
app.config["DEBUG"] = False
app.config["PROPAGATE_EXCEPTIONS"] = False
# Never call app.run(debug=True) in production. Serve through gunicorn.
Express:
if (process.env.NODE_ENV !== "development") {
app.set("env", "production");
}
app.use((err, req, res, next) => {
console.error(err); // log details server side
res.status(500).json({ error: "Internal error" });
});
Spring Boot:
server.error.include-stacktrace=never
server.error.include-message=never
management.endpoints.web.exposure.include=health
logging.level.root=INFO
Then cover the rest of the stack: no verbose logging on the database that prints query parameters, no --inspect Node flag, no JDWP port open on the JVM, no admin console of the message broker on a public address.
Enforce it in the pipeline. A policy check that fails the deploy when DEBUG=true or NODE_ENV=development appears in production manifests costs minutes to write.
How to test it
- Trigger an error on purpose. Request a route with malformed input, or an invalid path with a bad encoding. Expect a generic error page with no stack trace, file paths or framework name.
- Look for known debug routes:
/console,/debug,/__debug__/,/actuator/env,/actuator/heapdump,/_profiler. - Scan open ports of the production hosts for debugger listeners (for example 5005 for JVM debug, 9229 for Node inspector).
- Compare production environment variables and config files with the baseline in code review.
A dynamic scanner will check many of these for you. You can see the idea in the DAST web app scan demo.
Click through it at your own pace, or open it full screen.
Common mistakes
- Fixing the framework flag and leaving a staging style debug port open on the host.
- Disabling the debug page but still returning the exception message in the JSON body.
- Different config for the web process and the background worker, where only one was hardened.
- Leaving verbose logs that print request bodies, which moves the leak from the response into the log store.
- Relying on a reverse proxy to hide errors while the application is also reachable directly.
Quick check
Is an HTTP 500 page with a request ID acceptable?
Yes. A generic message plus a correlation ID lets support find the real error in logs without telling the client anything about internals.
Does this apply to test environments?
The requirement is about production. Still, test environments that face the internet or hold real data deserve the same settings.
Sources
- OWASP ASVS
- Framework documentation for your stack, specifically the section on deployment settings.