Skip to main content

Free 30-min security demo  — We'll scan your real code and show live findings, no commitment Book Now

Offensive360
Academy Debug Mode in Production
Beginner · 15 min

Debug Mode in Production

Learn how debug mode exposes stack traces, environment variables, and internal paths to attackers.

1 Debug Mode Information Disclosure

Debug mode in web frameworks provides detailed error information to help developers troubleshoot. In production, this information is invaluable to attackers.

Django DEBUG=True leak:

# When DEBUG=True and an error occurs, Django exposes:
# - Full Python stack trace with local variable values
# - All settings (including SECRET_KEY, DATABASE, API keys)
# - File paths on the server
# - SQL queries that caused errors
# - Environment variables

Flask debug mode:

app.run(debug=True)  # Exposes Werkzeug debugger
# Attackers can access an interactive Python console!
# Complete server compromise if Werkzeug debugger is accessible

Express.js in development mode:

app.use((err, req, res, next) => {
  res.status(500).send(err.stack);  // Stack trace to all users!
});

Even if debug mode is not explicitly enabled, error handlers that return stack traces or internal paths create the same information disclosure risk.

2 Environment-Based Configuration

Use environment variables to toggle debug mode, and ensure production uses safe error handlers that log details internally but return generic messages to users.

Django settings.py:

import os

# Never hardcode DEBUG=True
DEBUG = os.environ.get("DJANGO_DEBUG", "False") == "True"

# Production checklist — Django will warn if these are wrong
if not DEBUG:
    ALLOWED_HOSTS = os.environ["ALLOWED_HOSTS"].split(",")
    SECRET_KEY = os.environ["SECRET_KEY"]  # No default!

Express.js production error handler:

const isDev = process.env.NODE_ENV !== "production";

// Development — detailed errors
if (isDev) {
  app.use((err, req, res, next) => {
    res.status(500).json({ error: err.message, stack: err.stack });
  });
} else {
  // Production — generic error, log internally
  app.use((err, req, res, next) => {
    logger.error({ err });  // Detailed logging to server logs only
    res.status(500).json({ error: "Internal server error" });
  });
}

Knowledge Check

0/3 correct
Q1

What information does Django expose when DEBUG=True and an unhandled exception occurs?

Q2

What special risk does Flask's debug=True create beyond information disclosure?

Q3

What is the correct production behavior for unhandled exceptions?

Code Exercise

Environment-Based Error Handler

The error handler always returns the full stack trace. Fix it to only include technical details in development, returning a generic message in production.

javascript