Web › Module 10 › Lesson 2
Error Messages That Leak
Clients get safe messages; details go to logs you control — not stack traces to the world.
Visual · error_messages_leak
User sees “something went wrong #abc”; ops see stack in private logs. — router STOP — SAFE Own apps only.
Opening
Verbose errors help attackers map your stack — A10 includes leaky exceptions.
OWASP Top 10:2025 A10 Mishandling of Exceptional Conditions includes returning stack traces, SQL fragments, absolute paths, secret material, or account-oracle detail to untrusted clients. Attacker goal: reconnaissance from your crashes. Cyberlium: separate public message vs private log on apps YOU own — 0.0.1:8775. Not scrape stranger error pages for a portfolio. Next: unexpected states and recovery. Today: leaky errors.
1. Public vs private: correlation id pattern
Return a generic message plus a correlation id the user can quote. Log the full exception server-side with that id (still redacting secrets — A09). Never return Exception.toString() from production APIs you ship. The 8775 toy contrasts /broken/error vs /fixed/error.
Debug mode that prints stacks belongs only on local profiles — never default prod.
Command guide
A10 Exceptions — Public vs private
═══ INSTALL ═══
Linux (Debian/Ubuntu):
Command — copy this
sudo apt install curl
macOS: Built-in
Windows: Built-in (PowerShell: Invoke-WebRequest)
═══ COMMANDS ═══
Command — copy this
SAFE="http://127.0.0.1:8775"
Command — copy this
curl -sS -m 3 -D - "$SAFE/fixed/error" | head -n 20
2. Oracles: existence and validation detail
“User not found” vs “bad password,” raw validator dumps, and framework 500 bodies that reveal routes/files are oracles. Choose conscious product tradeoffs; default to less detail on auth. Document YOUR choice.
Dependency error strings often include hostnames and versions — strip before client response.
Command guide
A10 Exceptions — Oracles
═══ INSTALL ═══
Linux (Debian/Ubuntu):
Command — copy this
sudo apt install curl
macOS: Built-in
Windows: Built-in (PowerShell: Invoke-WebRequest)
═══ COMMANDS ═══
Command — copy this
SAFE="http://127.0.0.1:8775"
Command — copy this
curl -sS -m 3 "$SAFE/broken/error" | head -n 16
3. Secrets in exceptions: connection strings and tokens
Libraries sometimes embed credentials in exception messages. Scrub before log and never before client. Rotate if a secret ever leaked to a client response on a system you own.
Do not paste live connection strings into cyberlium-lab notes.
Command guide
A10 Exceptions — Secrets in exceptions
═══ INSTALL ═══
Linux (Debian/Ubuntu):
Command — copy this
sudo apt install curl
macOS: Built-in
Windows: Built-in (PowerShell: Invoke-WebRequest)
═══ COMMANDS ═══
Command — copy this
SAFE="http://127.0.0.1:8775"
Command — copy this
curl -sS -m 3 -D - "$SAFE/broken/error" | head -n 14
4. Own-lab proof: broken vs fixed local handler
A tiny local HTTP handler on 127.0.0.1:8775 shows leaky vs safe messages. Writeups cite http://192.168.0.1/ as the demo asset. No foreign targets. No hydra. Cite A10. Original Cyberlium — not official OWASP certification.
chmod 600 notes.
5. Wrong vs right: stack to client vs id + private log
Same word “error,” opposite exposure.
Wrong
Return stacks/SQL/paths to clients. Leave debug=True in prod. Harvest stranger 500 pages. Hydra the router. nmap the LAN.
Right
Identify DEMO; router → SAFE. Generic client message + correlation id; detail in private logs; lock error-leak-notes.txt. Next: Unexpected States and Recovery.
6. Hands-on: safe-error toy on 8775 + error-leak-notes.txt
Start a10_safe_error_toy.py (127.0.0.1:8775). Compare /broken/error vs /fixed/error. Record the pattern in notes.
Command guide
error_leak_notes.sh — DEMO identify + public vs private error toy
═══ INSTALL ═══
Linux (Debian/Ubuntu):
Command — copy this
sudo apt install curl sudo apt install nmap sudo apt install python3
macOS:
Command — copy this
brew install nmap brew install python3
Windows: Built-in (PowerShell: Invoke-WebRequest)
Command — copy this
choco install nmap # or download https://nmap.org/download.html
Download https://python.org/downloads/
═══ COMMANDS ═══
Command — copy this
cd "$HOME/cyberlium-lab"
Command — copy this
cat > a10_safe_error_toy.py << 'PY'
"""Leaky vs safe client errors. Bind loopback only."""
import uuid
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse
HOST = "127.0.0.1" # bind only — never 0.0.0.0, never argv
PORT = 8775
if HOST != "127.0.0.1":
raise SystemExit("refusing non-loopback bind")
class Handler(BaseHTTPRequestHandler):
def log_message(self, fmt, *args):
print("[demo]", fmt % args)
def _send(self, code: int, body: str):
data = body.encode("utf-8")
self.send_response(code)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
def do_GET(self):
p = urlparse(self.path).path
if p == "/broken/error":
return self._send(
500,
"RuntimeError: SQLSTATE boom at /secret/path with token=do-not-return",
)
if p == "/fixed/error":
cid = str(uuid.uuid4())
print("PRIVATE_LOG", cid, "RuntimeError redacted")
return self._send(500, f"something went wrong correlation_id={cid}")
return self._send(
200,
"Cyberlium A10 safe-error toy. DEMO writeup: http://192.168.0.1/ "
"GET /broken/error GET /fixed/error",
)
print("bind", HOST, PORT)
print("demo_writeup_url http://192.168.0.1/")
print("if 192.168.0.1 is a router, curl", f"http://{HOST}:{PORT}/")
HTTPServer((HOST, PORT), Handler).serve_forever()
PYCommand — copy this
cat > a10_l02_practical_commands.sh << 'SH' echo; cat /tmp/a10_leak.body; echo echo; cat /tmp/a10_safe.body; echo SH
Command — copy this
{Mission: error-leak-notes.txt (mode 600)
1) 0.0.1:8775 after starting a10_safe_error_toy.py.2) Compare broken vs fixed error curls. Document public vs private error handling. Confirm debug stacks are off in prod profiles. 3) chmod 600 $HOME/cyberlium-lab/error-leak-notes.txt. No foreign 500 harvesting. No hydra. No nmap.
Stuck? Ask Cyberlium AI Mentor
If “helpful errors” still means “return the stack,” ask for a hint. Try: "Hint only: correlation id pattern; what never returns to clients; why 192.168.0.1 router login is OUT OF SCOPE; where notes live?"
Leaky exceptions are free recon. A10. Original Cyberlium — not official OWASP certification. Next — Unexpected States and Recovery.
Knowledge Check
APPLY: API returns full stack with DB path on 500. Fix shape?
Multiple choice
Knowledge Check
APPLY: True or False: Production should ship with framework debug pages enabled.
True or False
Knowledge Check
APPLY: curl of http://192.168.0.1/ is router admin. Harvest its 500 page for A10?
Multiple choice