DEV Community

Cover image for How One Line of Code Almost Cost My Client Their OpenAI Credits
KagemaNjoroge
KagemaNjoroge

Posted on

How One Line of Code Almost Cost My Client Their OpenAI Credits

A few months ago, I started offering consulting services as a Django developer. One of my first clients had an urgent issue:

Our OpenAI credits are draining at a crazy rate, and we don’t know why.

They suspected misuse. I dug into their codebase and deployment setup — and one of the first red flags was this:

# settings.py
DEBUG = True
Enter fullscreen mode Exit fullscreen mode

Their production server was running with debug mode enabled.

It didn't take long to find something more worrying...

An Endpoint Was Leaking Their OpenAI API Key

Django's debug mode is helpful during development. But in production, it exposes detailed stack traces and local variable values in the browser when an exception occurs.

In their case, one endpoint was trying to parse JSON from a request body...but when the request was malformed(for example, accessing the endpoint via GET instead of POST, a malformed JSON body etc), it raised a JSONDecodeError. Here is a simplified version of the code:

from django.http import HttpRequest, JsonResponse
from dotenv import load_dotenv
import os
import json

load_dotenv() # loads env variables from a .env file

def index(request: HttpRequest) -> JsonResponse:
    open_ai_key = os.getenv("OPENAI_KEY")
    data = json.loads(request.body.decode("utf-8"))  # Boom on bad requests
    return JsonResponse(data)

Enter fullscreen mode Exit fullscreen mode

With DEBUG=True, that simple mistake triggered Django’s detailed error page, including:

  • The full stack trace
  • Request info
  • Local variables — including open_ai_key 😱

Here’s a screenshot from a proof of concept (POC) I recreated to demonstrate what was happening:

Screenshot from a proof of concept (POC)

Fix

I immediately advised two things:

  • Rotate the OpenAI key — assume it’s compromised.
  • Re-deploy the app with DEBUG = False in settings.py.
DEBUG = False
Enter fullscreen mode Exit fullscreen mode

With DEBUG set to false, Django shows a standard error 500 page without leaking any sensitive data.

I also recommended wrapping JSON parsing in a try/except block to avoid unhandled exceptions:

try:
    data = json.loads(request.body.decode("utf-8"))
except json.JSONDecodeError:
    return JsonResponse({"error": "Invalid JSON"}, status=400)
Enter fullscreen mode Exit fullscreen mode

Key takeaways

  • Never deploy Django with Django=True
  • Wrap dangerous logic (like JSON parsing) in proper error handling.

What looked like a billing issue turned out to be a security hole. If you’re building with Django (or any framework), treat debug tools like loaded guns: safe in dev, dangerous in prod.

Happy Django-ing 👋

Top comments (0)