Torna al Blog
13 maggio 2026

Chiave API OpenAI nelle Intestazioni di Risposta HTTP: Trovata in 7 Minuti

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

La dashboard di fatturazione di OpenAI mostrava addebiti che il fondatore non riusciva a spiegare. L'applicazione contava circa 800 utenti su un modello freemium, ma l'utilizzo dell'API tendeva a raggiungere i 2.000 dollari al mese — molto più alto di quanto giustificato dall'attività degli utenti sulla piattaforma. Il fondatore ipotizzò che ci fosse un prompt inefficiente da qualche parte e prese nota di indagare.

Sette minuti dopo l'inizio di una scansione Penetrify, il motivo divenne chiaro: la chiave API di OpenAI veniva ritrasmessa agli utenti negli header di risposta HTTP di ogni chiamata API proxata. 800 utenti l'avevano vista. Alcuni la stavano usando.


L'Architettura: Da Dove Proveniva la Fuga

L'applicazione era un backend FastAPI che serviva un frontend React. La sua funzionalità principale era quella di proxyare le richieste degli utenti all'API di OpenAI, aggiungendo prompt di sistema personalizzati, memorizzando la cronologia delle conversazioni e applicando lo strato proprietario di prompt engineering del fondatore. Questo è un modello comune per i prodotti wrapper AI — il valore non è il modello, ma il prodotto costruito attorno ad esso.

Il funzionamento dell'applicazione:

  1. L'utente invia un prompt dal frontend React
  2. Il frontend lo invia a POST /api/generate
  3. Il gestore FastAPI aggiunge il prompt di sistema e chiama l'API di OpenAI
  4. FastAPI restituisce il completamento al frontend

Da qualche parte nell'implementazione della route FastAPI, l'header Authorization della richiesta OpenAI in uscita — contenente la chiave API in formato token Bearer — veniva ritrasmesso nella risposta. Si tratta di una classe specifica di bug di inoltro degli header: l'applicazione stava inoltrando gli header di risposta dalla chiamata API OpenAI a monte anziché costruire i propri header di risposta.

Gli header di risposta su ogni chiamata a /api/generate includevano:

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

Ogni utente che avesse mai utilizzato la funzione di generazione — tutti gli 800 — aveva ricevuto la chiave API negli header di risposta delle proprie richieste. Era visibile nella scheda Rete degli Strumenti per sviluppatori del browser, in qualsiasi proxy HTTP e in qualsiasi client programmatico che leggesse gli header di risposta.


Cosa Ti Offre una Chiave API OpenAI

Una chiave API OpenAI senza restrizioni d'uso conferisce al titolare pieno accesso alla quota API dell'account corrispondente. Ciò significa:

  • Accesso illimitato ai modelli a spese del proprietario della chiave — GPT-4o, o1, o3, image generation, embeddings, fine-tuning
  • Nessun limite per richiesta fino al raggiungimento del limite di spesa mensile dell'account
  • Accesso a qualsiasi modello fine-tuned che l'account ha creato
  • Possibilità di leggere i file memorizzati se l'account utilizza l'API Files

Per un singolo fondatore la cui applicazione elabora 200-400 dollari/mese di utilizzo legittimo, subire un abuso esterno della propria chiave può far salire la fattura mensile a 2.000, 5.000 dollari o più — a seconda di quanto ampiamente la chiave circola e di cosa stanno generando gli abusatori.

Il modello di costo per l'abuso dell'API OpenAI è asimmetrico: l'attaccante non paga nulla, il proprietario della chiave paga per tutto.


I Picchi di Fatturazione Inspiegabili, Spiegati

Una volta identificata l'esposizione della chiave, i picchi di fatturazione ebbero senso. Il fondatore consultò la dashboard di utilizzo di OpenAI filtrata per endpoint e ora. Il modello dei picchi mostrava richieste ad alto volume che non correlavano con l'attività degli utenti sulla piattaforma — richieste alle 3 del mattino, richieste da intervalli IP che non corrispondevano a nessuna geografia utente nota, richieste per tipi di modello che l'applicazione non utilizzava.

Qualcuno aveva estratto la chiave — forse più persone — e la stava usando direttamente contro l'API di OpenAI, bypassando completamente l'applicazione. Le richieste andavano direttamente a OpenAI usando le credenziali estratte, non tramite l'applicazione del fondatore.

La chiave era stata esposta approssimativamente dalla prima settimana del lancio pubblico dell'applicazione. Al momento della scansione, era stata attiva e in perdita per diversi mesi.


Gli Altri Rilievi

L'esposizione della chiave OpenAI è stato il rilievo più immediatamente dannoso, ma sono stati segnalati tre problemi aggiuntivi:

MEDIUM — IDOR su /api/history/:userId

L'applicazione memorizzava la cronologia delle conversazioni per utente e la esponeva a un endpoint prevedibile:

GET /api/history/abc123

Il gestore della rotta recuperava la cronologia delle conversazioni per l'ID utente nel parametro del percorso senza verificare se l'utente richiedente fosse il proprietario di tali record. Qualsiasi utente autenticato poteva leggere la cronologia delle conversazioni di qualsiasi altro utente sostituendo il proprio ID. Poiché le conversazioni includevano prompt forniti dall'utente, si trattava anche di un'esposizione della privacy: un attaccante poteva leggere quali domande altri utenti avevano posto allo strumento AI.

MEDIUM — Modalità debug di FastAPI abilitata in produzione

L'applicazione era in esecuzione con FastAPI(debug=True). In modalità debug, qualsiasi eccezione non gestita restituisce una traccia completa dello stack nella risposta HTTP, inclusi percorsi di file interni, versioni delle dipendenze e nomi delle variabili d'ambiente (anche se non i valori). Queste informazioni sono direttamente utili per pianificare ulteriori attacchi — conoscere la versione esatta di FastAPI, la versione di Pydantic e la versione di Python restringe significativamente l'elenco delle CVE applicabili.

La modalità debug abilita anche la documentazione interattiva di FastAPI su /docs e /redoc per impostazione predefinita, che era accessibile in produzione e documentava ogni endpoint API interno, inclusi quelli non destinati all'accesso degli utenti.

LOW — HTTP non reindirizza a HTTPS

La versione HTTP dell'applicazione serviva il contenuto completo senza reindirizzare a HTTPS. Su reti pubbliche o condivise, un attaccante che esegue un attacco man-in-the-middle potrebbe intercettare sessioni non crittografate ed estrarre token di sessione, prompt inviati dall'utente e risposte API.


La Correzione: Implementata la Stessa Sera

Il fondatore ha implementato le correzioni per tutti i rilievi entro tre ore dalla ricezione del rapporto.

Per prima cosa, ruotare la chiave

Prima di toccare qualsiasi codice, l'azione immediata è stata quella di revocare la chiave compromessa nella dashboard di OpenAI e generarne una nuova. Ciò ha interrotto istantaneamente qualsiasi abuso in corso. La rotazione delle chiavi di OpenAI è immediata — la vecchia chiave smette di funzionare nel momento in cui la si elimina.

Correggere il bug di inoltro degli header

La causa principale era che la rotta FastAPI utilizzava un client HTTP generico che inoltrava tutti gli header di risposta dalla chiamata upstream di OpenAI. La correzione consisteva nel costruire header di risposta espliciti anziché passare quelli upstream:

# Prima (vulnerabile) — inoltro di tutti gli header upstream
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)  # ← questo inoltra l'header Authorization indietro
)

# Dopo (corretto) — costruzione esplicita della risposta
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"]})
# Solo i dati che vogliamo esplicitamente restituire — nessun header upstream inoltrato

Correggere l'IDOR

L'endpoint della cronologia delle conversazioni è stato aggiornato per estrarre l'ID utente dal JWT verificato anziché dal parametro del percorso:

@router.get("/api/history")
async def get_history(current_user: User = Depends(get_current_user)):
    # L'ID utente proviene dal JWT verificato — non può essere falsificato
    history = await db.get_history(user_id=current_user.id)
    return history

Disabilitare la modalità debug

# In config.py
app = FastAPI(
    debug=settings.DEBUG,  # legge dalla variabile d'ambiente
    docs_url=None if not settings.DEBUG else "/docs",  # nasconde la documentazione in produzione
    redoc_url=None if not settings.DEBUG else "/redoc"
)

Con DEBUG=false impostato nell'ambiente di produzione, la documentazione interattiva e le risposte di errore dettagliate sono scomparse immediatamente al successivo deployment.


Aggiungere limiti di utilizzo OpenAI come rete di sicurezza

Oltre a correggere la fuga, il fondatore ha aggiunto due misure difensive per limitare il raggio d'azione di qualsiasi futura esposizione di chiavi:

Limiti di utilizzo: Nella dashboard di OpenAI, sotto Fatturazione → Limiti di utilizzo, imposta un limite massimo mensile e una soglia di notifica soft. Anche se una chiave viene nuovamente compromessa, la capacità dell'attaccante di accumulare addebiti è limitata.

Chiavi dedicate per servizio: Crea una API key separata per ogni applicazione o ambiente. Se una chiave viene compromessa, puoi ruotare solo quella chiave senza interrompere altri servizi, e i log di utilizzo per ogni chiave sono chiaramente separati — rendendo l'accesso non autorizzato molto più facile da rilevare.


Quanto è comune questo?

L'esposizione di API key nelle risposte HTTP è meno comune rispetto all'esposizione nei bundle JavaScript, ma la riscontriamo regolarmente, in particolare nelle applicazioni wrapper AI. Il modello ha quasi sempre la stessa causa principale: uno sviluppatore che costruisce un livello proxy utilizza un client HTTP generico che inoltra le intestazioni di risposta, e non verifica cosa contengono tali intestazioni.

L'errore di inoltro delle intestazioni è facile da commettere perché spesso semplifica l'implementazione. Perché costruire una nuova risposta quando si può inoltrare quella a monte? La risposta, in questo caso, è che la risposta a monte contiene credenziali che non si desidera condividere con i propri utenti.

Se la tua applicazione esegue il proxy delle chiamate a OpenAI, Anthropic o qualsiasi altra API esterna, verifica esplicitamente le tue intestazioni di risposta. Utilizza uno strumento come curl -v o i DevTools del tuo browser per esaminare ogni intestazione restituita da ogni endpoint API. Le intestazioni sono facili da trascurare proprio perché la maggior parte delle volte sono poco interessanti — il che le rende un nascondiglio così efficace per una fuga.


Il contesto della domanda YC

Il fondatore stava preparando una domanda YC al momento della scansione. La combinazione di picchi di fatturazione inspiegabili, una API key esposta e una vulnerabilità IDOR che interessava la cronologia delle conversazioni di tutti gli utenti sarebbe stata un problema significativo da spiegare agli investitori — o, peggio, da scoprire dopo il finanziamento.

I problemi di sicurezza nella fase di pre-lancio o di early traction sono risolvibili in poche ore. Gli stessi problemi scoperti dopo un incidente di sicurezza, una notifica di violazione dei dati o una storia mediatica ostile richiedono mesi per essere risolti e possono porre fine a un'azienda che non ha ancora costruito la buona volontà necessaria per sopravvivere al ciclo di notizie.

Il fondatore ha eseguito Penetrify di nuovo prima di inviare la domanda YC. Il rapporto è risultato pulito.

Frequently Asked Questions

Quali tipi di vulnerabilità rileva Penetrify?

Penetrify rileva tutte le categorie di vulnerabilità OWASP Top 10, inclusi SQL injection, XSS, CSRF, IDOR, autenticazione compromessa, configurazioni di sicurezza errate ed esposizione di dati sensibili. Testa anche la sicurezza delle API, la gestione delle sessioni e le comuni configurazioni errate in Supabase, Firebase e Bubble.

Quanto dura un test di penetrazione con IA?

Una scansione rapida si completa in 15–30 minuti. Una scansione standard dura 1–2 ore con una copertura più ampia. Una scansione approfondita può durare diverse ore per applicazioni complesse.

Cosa include un report di Penetrify?

Ogni report include un sommario esecutivo, un punteggio di sicurezza complessivo, i risultati classificati per gravità (Critico, Alto, Medio, Basso), procedure di riproduzione dettagliate e indicazioni concrete di rimediazione scritte per gli sviluppatori, non per i responsabili della conformità.

Related articles

CI/CD Penetration Testing: Come integrare la sicurezza in ogni distribuzione
Scopri come integrare il Penetration Testing nella tua pipeline CI/CD. Copre SAST, DAST, i quality gates e il testing potenziato dall'IA senza rallentare la delivery.
Scansione Autonoma delle Vulnerabilità OWASP: Come l'IA sta sostituendo i test di sicurezza basati su regole
Scopri come la scansione autonoma delle vulnerabilità OWASP utilizza l'IA per andare oltre il confronto delle firme. Tratta l'OWASP Top 10 2025, il test agentico e perché gli scanner basati su regole non sono sufficienti.
Simulazione di catene di attacco a più fasi: Perché la scansione di singole vulnerabilità non è sufficiente
Scopri come la simulazione di catene di attacco a più fasi individua gli exploit concatenati che sfuggono agli scanner di vulnerabilità. Esempi reali, mappatura MITRE ATT&CK e guida all'implementazione.

Explore more

Compare alternatives →Security glossary →CI/CD integration →Security statistics →
Torna al Blog