_
So here's the tea_ 🍵.
A while back, I picked up a Django consulting gig — nothing major, just helping out a startup that was panicking over something real bad:
“Bro, someone’s draining our MPESA till via the Daraja API.”
They kept rotating their consumer key and secret, but the money kept disappearing. It was like patching a leaking pipe without finding the hole. So they called me in.
🔍 The Setup
I asked for full access to the codebase. They were using Django (bless them — I love Django too), and they had a clean enough setup. But something instantly made me flinch:
python
# settings.py
DEBUG = True
🚨 Red flag. Big one.
They were running production with Django’s debug mode on — which basically hands over your server's internals on a silver platter whenever something crashes.
I started poking around and spotted something worse...
đź’€ The Leak Was Right There...
They had this endpoint meant to parse JSON from requests:
python
def index(request: HttpRequest) -> JsonResponse:
consumer_secret = os.getenv("CONSUMER_SECRET")
consumer_key = os.getenv("CONSUMER_KEY")
data = json.loads(request.body.decode("utf-8")) # đź’Ą crashes on bad JSON
return JsonResponse(data)
Looks innocent enough, right?
Well... anytime that JSON payload was invalid — like if someone sent a GET instead of a POST or posted broken JSON — Django threw an unhandled exception.
But with
DEBUG=True
, it responded with the entire stack trace...
And guess what was sitting right there in the local variables?
Yup. The consumer key and secret. đź«
So if a curious hacker sent a malformed request to that endpoint… they’d get a nice, juicy traceback with the keys to the kingdom.
đź”§** My Fixes**
I immediately told them:
Rotate the consumer credentials — assume they’re already out there.
Set
DEBUG = False
— like yesterday.
Wrap the JSON parsing in a try-except block to catch garbage input:
python
try:
data = json.loads(request.body.decode("utf-8"))
except json.JSONDecodeError:
return JsonResponse({"error": "Invalid JSON"}, status=400)
After patching it, the bleeding stopped
.
🔍** Bonus: I Got Curious…**
Out of curiosity, I ran a few dorks on Shodan and Censys to see how many live Django apps in Kenya still had DEBUG=True.
Let’s just say... we’re sitting on a ticking time bomb.
đź§ Lessons Learned
Never — ever — deploy Django with DEBUG=True
. It’s basically broadcasting your secrets to the world.
Wrap any fragile logic (especially input parsing) in try/except blocks.
Treat dev tools like weapons — fine in the lab, deadly in prod.
Assume your environment variables are vulnerable when stack traces are exposed.
That’s it. One line of careless code almost cost them a business.
And the scary part? Most of this could've been caught with a simple code review.
If you're building anything with sensitive APIs — especially involving money — take time to audit your error handling and config. It could save you millions... or your reputation.
Happy coding — and stay paranoid. 🧠💻
Top comments (0)