Why this matters
Most Flask “leaks” aren’t cinematic hacks. They’re boring misconfigurations: debug flags left on, apps binding to a public interface, secrets copied into repos, or logs quietly capturing tokens.
These checks are designed to be fast and low-risk. Run them on the server/container where Flask is deployed. If you’re on shared infrastructure, avoid printing full secrets into terminal history or logs.
Goal: find the “oops” settings — debug mode, accidental public exposure, world-readable config, risky file perms, and credentials sitting in places you forgot about.
The 7 commands
1) Confirm what’s listening on the network
This tells you what ports are open and which processes own them. On a typical secure setup, you’ll see your reverse proxy (Nginx/Caddy/Traefik) listening publicly on 443 (HTTPS) — and your Flask app listening only on a local/private interface.
sudo ss -lntup
2) Verify your app is NOT bound publicly
You don’t want Gunicorn/Flask binding to 0.0.0.0 (public) unless you have a very specific reason.
The safe default is: reverse proxy handles HTTPS on 443, and the app binds to
127.0.0.1 (or a private network interface) on an internal port.
ps aux | egrep -i 'gunicorn|flask|uvicorn' | egrep -v egrep
3) Check for DEBUG / development settings
Debug mode can leak stack traces, config, and sometimes interactive consoles (depending on setup). Make sure it’s not enabled via env vars or systemd.
env | egrep -i 'FLASK_ENV|FLASK_DEBUG|DEBUG|ENVIRONMENT' || true
4) Hunt for accidental secrets in the app folder
The usual culprits: .env, copied config files, token dumps, “temporary” debug JSON, or backups.
cd /path/to/your/app && \
find . -maxdepth 3 -type f \( \
-name ".env" -o -name "*.env" -o -name "*secret*" -o -name "*token*" -o -name "*key*" -o -name "*.pem" -o -name "*.p12" -o -name "*.pfx" -o -name "*.bak" \
\ ) -print
5) Check for world-readable files (quiet but deadly)
Anything containing credentials should not be readable by “other”. This surfaces obvious permission mistakes.
cd /path/to/your/app && \
find . -type f -perm -o=r -maxdepth 4 -print 2>/dev/null | head -n 200
6) Scan recent logs for “oops” strings
You’re not trying to be perfect here — you’re trying to catch obvious leaks:
Authorization:, Bearer, api_key, secret, etc.
sudo journalctl -u your-service-name --since "24 hours ago" --no-pager | \
egrep -i 'authorization:|bearer |api[_-]?key|secret|token|password' | tail -n 200
7) Confirm what’s reachable from the outside (default HTTPS)
Run this from a different machine (or your laptop) to see what the internet sees. HTTPS defaults to port 443, so you should not need to specify a port.
curl -sS -D- https://your-domain.com/healthz -o /dev/null
What “good” looks like
- Your reverse proxy handles the public edge on 443 (HTTPS).
- Your app binds to
127.0.0.1(or a private interface) — not0.0.0.0. - Debug flags are off. Stack traces are not served publicly.
- Secrets live in env vars / secret managers — not committed files or logs.
- Logs do not contain auth headers or bearer tokens.
Next steps: the fast fixes
- Terminate TLS at Nginx/Caddy/Traefik and forward to your app privately.
- Disable debug everywhere (env vars, config, systemd, container args).
- Move secrets into environment variables (or a secret store) and rotate anything that leaked.
- Lock down file permissions: configs and keys should not be world-readable.
- Add a minimal
/healthzendpoint and monitor it.
Related PLEX reading
References & further reading
- Flask documentation — Production notes, config, and deployment basics.
- Gunicorn documentation — Binding, workers, and safe defaults.
- OWASP Top 10 — Common failure modes that show up in real deployments.