Zpět na blog
13. května 2026

Klíč OpenAI API v hlavičkách HTTP odpovědi: Nalezeno za 7 minut

Viktor Bulanek
Founder & CTO, Penetrify
MSc IT Security · 20+ years in security · 4x Ex-CTO

Fakturační panel OpenAI ukazoval poplatky, které si zakladatel nedokázal vysvětlit. Aplikace měla zhruba 800 uživatelů na freemium modelu, ale využití API směřovalo k 2 000 $ měsíčně — mnohem více, než by odpovídala uživatelská aktivita na platformě. Zakladatel předpokládal, že někde mají neefektivní prompt, a poznamenal si, že to prošetří.

Sedm minut po zahájení skenu Penetrify se důvod objasnil: klíč OpenAI API byl předáván zpět uživatelům v hlavičkách HTTP odpovědí každého proxy API volání. 800 uživatelů ho vidělo. Někteří z nich ho používali.


Architektura: Odkud únik pocházel

Aplikace měla backend FastAPI obsluhující frontend React. Její hlavní funkcí bylo proxyování uživatelských požadavků na OpenAI API, přidávání vlastních systémových promptů, ukládání historie konverzací a aplikování vlastní vrstvy prompt engineeringu zakladatele. Toto je běžný vzor pro produkty typu AI wrapper — hodnota není v modelu, ale v produktu, který je kolem něj postaven.

Jak aplikace fungovala:

  1. Uživatel odešle prompt z React frontendu
  2. Frontend ho odešle na POST /api/generate
  3. Handler FastAPI přidá systémový prompt a volá OpenAI API
  4. FastAPI vrátí dokončení (completion) frontendu

Někde v implementaci routy FastAPI byla hlavička Authorization z odchozího požadavku OpenAI — obsahující API klíč ve formátu Bearer tokenu — předávána zpět v odpovědi. Jedná se o specifickou třídu chyby předávání hlaviček: aplikace předávala hlavičky odpovědí z upstream volání OpenAI API namísto vytváření vlastních hlaviček odpovědí.

Hlavičky odpovědí u každého volání /api/generate zahrnovaly:

HTTP/1.1 200 OK
Content-Type: application/json
Authorization: Bearer sk-proj-...[OpenAI API key]
...
{"completion": "..."}

Každý uživatel, který kdy použil funkci generování — všech 800 — obdržel API klíč v hlavičkách odpovědí svých požadavků. Byl viditelný v záložce Síť (Network) v DevTools prohlížeče, v jakémkoli HTTP proxy a v jakémkoli programovém klientovi, který četl hlavičky odpovědí.


Co vám dává OpenAI API klíč

OpenAI API klíč bez omezení použití poskytuje držiteli plný přístup k API kvótě příslušného účtu. To znamená:

  • Neomezený přístup k modelům na náklady majitele klíče — GPT-4o, o1, o3, generování obrázků, embeddings, fine-tuning
  • Žádný limit na požadavek, dokud není dosaženo měsíčního limitu útraty účtu
  • Přístup k jakýmkoli fine-tuned modelům, které účet vytvořil
  • Možnost číst uložené soubory, pokud účet používá Files API

Pro individuálního zakladatele, jehož aplikace zpracovává 200–400 $ měsíčně v legitimním využití, může zneužití jeho klíče externě zvýšit měsíční účet na 2 000 $, 5 000 $ nebo více — v závislosti na tom, jak široce se klíč rozšíří a co útočníci generují.

Nákladový model pro zneužití OpenAI API je asymetrický: útočník neplatí nic, majitel klíče platí za vše.


Nevysvětlitelné nárůsty fakturace, vysvětleno

Jakmile bylo odhaleno zveřejnění klíče, nárůsty fakturace dávaly smysl. Zakladatel si stáhl panel využití OpenAI filtrovaný podle endpointu a času. Vzor nárůstů ukázal požadavky s vysokým objemem, které nekorelovaly s uživatelskou aktivitou na platformě — požadavky ve 3 ráno, požadavky z IP rozsahů, které neodpovídaly žádné známé uživatelské geografii, požadavky na typy modelů, které aplikace nepoužívala.

Někdo extrahoval klíč — možná několik lidí — a používal jej přímo proti OpenAI API, zcela obcházel aplikaci. Požadavky směřovaly přímo na OpenAI s použitím extrahovaných pověření, nikoli přes aplikaci zakladatele.

Klíč byl vystaven přibližně od prvního týdne veřejného spuštění aplikace. V době skenování byl aktivní a unikal po dobu několika měsíců.


Další zjištění

Vystavení klíče OpenAI bylo nejbezprostředněji škodlivým zjištěním, ale byly nahlášeny tři další problémy:

STŘEDNÍ — IDOR na /api/history/:userId

Aplikace ukládala historii konverzací pro každého uživatele a vystavovala ji na předvídatelném koncovém bodě:

GET /api/history/abc123

Obslužná rutina trasy načítala historii konverzací pro ID uživatele v parametru cesty, aniž by kontrolovala, zda požadující uživatel vlastní tyto záznamy. Jakýkoli ověřený uživatel mohl číst historii konverzací jakéhokoli jiného uživatele nahrazením jeho ID. Jelikož konverzace obsahovaly uživatelem zadané výzvy, jednalo se také o narušení soukromí: útočník mohl číst, jaké otázky kladli ostatní uživatelé nástroji AI.

STŘEDNÍ — Režim ladění FastAPI povolen v produkčním prostředí

Aplikace běžela s FastAPI(debug=True). V režimu ladění jakákoli neošetřená výjimka vrací úplný stack trace v HTTP odpovědi, včetně interních cest k souborům, verzí závislostí a názvů proměnných prostředí (i když ne jejich hodnot). Tyto informace jsou přímo užitečné pro plánování dalších útoků — znalost přesné verze FastAPI, verze Pydantic a verze Pythonu významně zužuje seznam použitelných CVEs.

Režim ladění také ve výchozím nastavení umožňuje interaktivní dokumentaci FastAPI na /docs a /redoc, která byla přístupná v produkčním prostředí a dokumentovala každý interní API koncový bod, včetně těch, které nebyly určeny pro uživatelský přístup.

NÍZKÁ — HTTP nepřesměrovává na HTTPS

HTTP verze aplikace poskytovala plný obsah bez přesměrování na HTTPS. Ve veřejných nebo sdílených sítích mohl útočník provádějící útok man-in-the-middle zachytit nešifrované relace a extrahovat tokeny relací, uživatelem zadané výzvy a API odpovědi.


Oprava: Nasazeno tentýž večer

Zakladatel nasadil opravy pro všechna zjištění do tří hodin od obdržení zprávy.

Nejprve obměňte klíč

Než se sáhne na jakýkoli kód, okamžitou akcí bylo zrušit kompromitovaný klíč v ovládacím panelu OpenAI a vygenerovat nový. Tím se okamžitě zastavilo jakékoli probíhající zneužívání. Obměna klíče OpenAI je okamžitá — starý klíč přestane fungovat v okamžiku, kdy jej smažete.

Opravte chybu přeposílání hlaviček

Hlavní příčinou bylo, že trasa FastAPI používala generického HTTP klienta, který přeposílal všechny hlavičky odpovědi z upstream volání OpenAI. Opravou bylo konstruovat explicitní hlavičky odpovědi namísto propouštění těch z upstreamu:

# Před (zranitelné) — přeposílání všech upstream hlaviček
upstream_response = await client.post(
    "https://api.openai.com/v1/chat/completions",
    headers={"Authorization": f"Bearer {settings.OPENAI_API_KEY}", ...},
    json=payload
)
return Response(
    content=upstream_response.content,
    headers=dict(upstream_response.headers)  # ← toto přeposílá hlavičku Authorization zpět
)

# Po (opravené) — explicitní konstrukce odpovědi
upstream_response = await client.post(
    "https://api.openai.com/v1/chat/completions",
    headers={"Authorization": f"Bearer {settings.OPENAI_API_KEY}", ...},
    json=payload
)
completion_data = upstream_response.json()
return JSONResponse(content={"completion": completion_data["choices"][0]["message"]["content"]})
# Pouze data, která explicitně chceme vrátit — žádné upstream hlavičky nejsou přeposílány

Opravte IDOR

Koncový bod historie konverzací byl aktualizován tak, aby extrahoval ID uživatele z ověřeného JWT namísto z parametru cesty:

@router.get("/api/history")
async def get_history(current_user: User = Depends(get_current_user)):
    # User ID comes from the verified JWT — can't be spoofed
    history = await db.get_history(user_id=current_user.id)
    return history

Zakázat režim ladění

# In config.py
app = FastAPI(
    debug=settings.DEBUG,  # reads from environment variable
    docs_url=None if not settings.DEBUG else "/docs",  # hide docs in production
    redoc_url=None if not settings.DEBUG else "/redoc"
)

S nastavením DEBUG=false v produkčním prostředí interaktivní dokumentace a podrobné chybové odpovědi okamžitě zmizely při dalším nasazení.


Přidání limitů využití OpenAI jako bezpečnostní síť

Kromě opravy úniku zakladatel přidal dvě obranná opatření k omezení rozsahu dopadu jakéhokoli budoucího odhalení klíče:

Limity využití: Na řídicím panelu OpenAI v sekci Billing → Usage limits nastavte měsíční pevný limit a měkký notifikační práh. I když dojde k opětovné kompromitaci klíče, schopnost útočníka navyšovat poplatky je omezena.

Vyhrazené klíče pro každou službu: Vytvořte samostatný API klíč pro každou aplikaci nebo prostředí. Pokud dojde ke kompromitaci klíče, můžete rotovat pouze tento klíč, aniž byste narušili ostatní služby, a protokoly využití pro každý klíč jsou čistě odděleny — což výrazně usnadňuje detekci neoprávněného přístupu.


Jak časté to je?

Odhalení API klíče v HTTP odpovědích je méně časté než odhalení v JavaScriptových balíčcích, ale pravidelně se s ním setkáváme konkrétně u aplikací s AI wrappery. Tento vzorec má téměř vždy stejnou hlavní příčinu: vývojář vytvářející proxy vrstvu používá generického HTTP klienta, který přeposílá hlavičky odpovědí, a neprověřuje, co tyto hlavičky obsahují.

Chyba v přeposílání hlaviček se snadno udělá, protože často zjednodušuje implementaci. Proč vytvářet novou odpověď, když můžete přeposlat tu z upstreamu? Odpověď v tomto případě zní, že upstreamová odpověď obsahuje přihlašovací údaje, které nechcete sdílet se svými uživateli.

Pokud vaše aplikace proxyuje volání na OpenAI, Anthropic nebo jakékoli jiné externí API, explicitně prověřte hlavičky svých odpovědí. Použijte nástroj jako curl -v nebo DevTools vašeho prohlížeče k prohlédnutí každé hlavičky vrácené každým API koncovým bodem. Hlavičky se snadno přehlédnou právě proto, že většinu času jsou nezajímavé — což z nich dělá tak efektivní úkryt pro únik.


Kontext žádosti o YC

Zakladatel připravoval žádost o YC v době skenování. Kombinace nevysvětlitelných nárůstů fakturace, odhaleného API klíče a IDOR zranitelnosti ovlivňující historii konverzací všech uživatelů by byla významným problémem k vysvětlení investorům — nebo, což je horší, k objevení po získání financování.

Bezpečnostní problémy ve fázi před spuštěním nebo rané trakce jsou opravitelné během hodin. Stejné problémy objevené po bezpečnostním incidentu, oznámení o úniku dat nebo nepřátelské mediální zprávě trvá měsíce, než se z nich společnost zotaví, a mohou ukončit společnost, která si ještě nevybudovala dobrou pověst, aby přežila mediální cyklus.

Zakladatel znovu spustil Penetrify před odesláním žádosti o YC. Zpráva se vrátila čistá.

Frequently Asked Questions

Jaké typy zranitelností Penetrify detekuje?

Penetrify detekuje všechny kategorie zranitelností OWASP Top 10, včetně SQL injection, XSS, CSRF, IDOR, broken authentication, bezpečnostních misconfigurations a expozice citlivých dat. Testuje také bezpečnost API, správu relací a běžné misconfiguration v Supabase, Firebase a Bubble.

Jak dlouho trvá AI penetrační test?

Rychlý sken je dokončen za 15–30 minut. Standardní sken trvá 1–2 hodiny s širším pokrytím. Hloubkový sken může pro komplexní aplikace trvat několik hodin.

Co obsahuje zpráva Penetrify?

Každá zpráva obsahuje executive summary, celkové bezpečnostní skóre, nálezy klasifikované dle závažnosti (Kritická, Vysoká, Střední, Nízká), kroky pro reprodukci a konkrétní doporučení pro nápravu napsaná pro vývojáře – ne pro compliance manažery.

Related articles

CI/CD Penetration Testing: Jak integrovat zabezpečení do každého nasazení
Naučte se, jak integrovat Penetration Testing do vašeho CI/CD pracovního postupu. Zahrnuje SAST, DAST, brány kvality a testování poháněné umělou inteligencí, bez zpomalení dodávky.
Autonomní skenování zranitelností OWASP: Jak AI nahrazuje testování bezpečnosti založené na pravidlech
Zjistěte, jak autonomní skenování zranitelností OWASP využívá AI k překonání pouhého porovnávání signatur. Pokrývá OWASP Top 10 2025, agentní testování a vysvětluje, proč skenery založené na pravidlech nestačí.
Simulace vícekrokového útočného řetězce: Proč skenování jedné zranitelnosti nestačí
Zjistěte, jak simulace vícestupňového řetězce útoků odhaluje zřetězené exploity, které skenery zranitelností přehlížejí. Příklady z reálného světa, mapování MITRE ATT&CK a průvodce implementací.

Explore more

Compare alternatives →Security glossary →CI/CD integration →Security statistics →
Zpět na blog