Skip to course content
Free FastAPI backend course

FastAPI for AI Backend Development

Unit 10.01: Where credentials belong, and where they leak

Where a credential travels determines how many systems record it.

Six places you do not control

Five locations for a credential, two of which leak.

The code lists them, then enumerates where a query-string key ends up.

PLACES = [
    ("Authorization header",     "correct",  "not logged by default, not in history"),
    ("query string ?key=...",    "LEAKS",    "written to every access log it passes"),
    ("in the URL path",          "LEAKS",    "same, plus browser history and referrers"),
    ("request body",             "awkward",  "works, but breaks GET and caching"),
    ("a cookie",                 "depends",  "fine for browsers, needs CSRF protection"),
]
print(f"{'where the credential travels':28} {'verdict':9} why")
for place, verdict, why in PLACES:
    print(f"{place:28} {verdict:9} {why}")

LEAKS = ["your access logs", "the proxy's logs", "the CDN's logs",
         "browser history", "Referer headers to third parties",
         "screenshots in tickets"]
print(f"\na key in a query string reaches: {len(LEAKS)} places you do not control")
for place in LEAKS:
    print(f"   {place}")

A key in a query string is written to your access logs, the proxy's logs, the CDN's logs, browser history, any Referer header sent to a third party, and screenshots pasted into tickets. You cannot retract any of them.

The Authorization header exists precisely because logging tools omit it by default, which makes it the only place with that property.

The mistake this prevents

The mistake is putting a key in the query string to make testing easier with a browser. That convenience is exactly the property that leaks it - if it works by pasting a URL, the URL is now a credential.

Takeaway

Credentials belong in headers. A query-string key is recorded by at least six systems you do not control, and none of them can be un-logged.