Vai al contenuto

Basi della sicurezza

La tua chiave API OpenAI è esposta nel frontend. Ruotala.

Una chiave API OpenAI esposta nel frontend non si può legare a nessun dominio. Ruotala oggi, sposta la chiamata sul tuo server e metti un tetto di spesa.

Vlad Tkachenko10 min di lettura
Un valore a forma di chiave dentro una pagina di codice di un'app, disegnato tanto lungo da uscire dal bordo destro del pannello che lo contiene.

In breve

  • Una chiave API OpenAI esposta nel tuo frontend è il caso in cui l'allarme ha ragione. Nessuna impostazione rende sicura una chiave del genere dentro un browser.
  • È un token al portatore, quindi basta averla in mano. Non esiste una restrizione di dominio che la leghi al tuo sito, come invece esiste per una chiave Google.
  • Ruotala oggi nella dashboard di OpenAI, sposta la chiamata dietro un endpoint tuo e metti un limite di spesa sul progetto.
  • Ne abbiamo trovata una in 33 app su 30.998 scansionate. Rara, e tutte e 33 sono uscite con voto D o F.

Apri la tua app in un browser, guarda il sorgente della pagina e cercaci dentro sk-proj-. Se torna una stringa lunga, la tua chiave API OpenAI è esposta nel tuo frontend, e qualsiasi visitatore tu abbia mai avuto avrebbe potuto copiarla.

Ecco la parte che quasi tutti i consigli su questo tema sbagliano: una chiave OpenAI non è una chiave Google con un prefisso diverso, e non c'è niente che tu possa impostare in una dashboard per renderla sicura dov'è. Una chiave Google Maps sta bene nella tua pagina, e una impostazione gratuita la protegge. Una chiave OpenAI non ha da nessuna parte una impostazione equivalente, ed è per questo che l'unica prima istruzione onesta è ruotarla.

Fra il 12 e il 14 agosto 2026 abbiamo eseguito nove controlli esterni su 30.998 app online, costruite con Lovable, Bolt, v0, Replit e Base44. Una chiave OpenAI è comparsa in 33 di esse. Tutte e 33 sono tornate con voto D o F, perché un solo riscontro critico mette un tetto al voto qualunque cosa l'app abbia fatto bene per il resto. I numeri completi sono nel nostro rapporto di scansione.

Una chiave API OpenAI esposta nel frontend è un problema?

Sì. Questo è il caso in cui l'allarme ha ragione.

Una chiave OpenAI è un token al portatore, e la parola contiene tutta la spiegazione: chi la porta può usarla. Viaggia in un header che recita Authorization: Bearer sk-proj-…, e i server di OpenAI non chiedono altro. Non quale sito l'ha mandata. Non da quale paese è arrivata, né se il mittente sei tu.

Pensa a un biglietto del treno e non a un passaporto. Un controllore non verifica a quale nome è intestato un biglietto, perché averlo è tutta la qualifica. È questo che rende un biglietto degno di essere rubato e un passaporto quasi mai.

Quindi una chiave stampata nella tua app è un biglietto che hai consegnato a ogni visitatore. La maggior parte non guarderà mai. Chi guarda di solito non è nemmeno una persona: scraper automatici setacciano le pagine pubbliche raccogliendo stringhe a forma di chiave, e non hanno bisogno di sapere chi sei per trovare la tua.

Perché la variabile d'ambiente non l'ha nascosta

Perché una build di frontend compila le variabili d'ambiente dentro il file che spedisce.

È questo il passaggio che convince le persone che la chiave sia al sicuro. L'hai tolta dal codice e messa in un file .env, l'hai chiamata VITE_OPENAI_API_KEY o NEXT_PUBLIC_OPENAI_API_KEY, e ora il codice nomina una variabile dove prima stava la chiave. Nell'app spedita non è cambiato nulla. Lo strumento di build ha sostituito quella variabile con il suo valore in uscita, e il valore è lì nel JavaScript che il tuo visitatore scarica.

La documentazione di Vite lo dice chiaramente: le variabili con il prefisso VITE_ sono esposte nel codice sorgente lato client dopo il bundling, e le informazioni sensibili come le chiavi API non devono stare in una di esse, perché i valori vengono impacchettati dentro il tuo sorgente. I prefissi VITE_ e NEXT_PUBLIC_ non sono una cassaforte. Sono una dichiarazione che hai capito che quella variabile è pubblica.

Una variabile d'ambiente una cosa vera per te l'ha fatta: ha tenuto la chiave fuori dal tuo repository, dove l'avrebbe incontrata chiunque leggesse il tuo codice. I tuoi visitatori il tuo codice non lo leggono mai. Leggono il file che la tua build ne ha ricavato.

Perché non puoi limitarla come limiti una chiave Google

Perché OpenAI non offre una restrizione di quel tipo.

Se hai letto qualcosa su una chiave API Google in un frontend, hai incontrato una soluzione da cinque minuti: apri la chiave nella console di Google Cloud, imposta una restrizione di referrer HTTP, e la chiave stampata nella tua pagina funziona sul tuo sito e restituisce un errore ovunque altrove. Quel consiglio è giusto per una chiave Google, e non si trasferisce.

Su una chiave OpenAI non esiste alcun campo per "solo da yourapp.com". Nessuna lista di domini consentiti, nessun controllo del referrer, nessuna restrizione per IP che sopravviva a un browser. Quello che OpenAI ti dà al suo posto sta sull'account dietro la chiave: a quale progetto appartiene, quanto quel progetto può spendere in un mese e se la chiave esiste ancora. Questo limita quanto può costare una chiave rubata. La chiave stessa continua a funzionare da ovunque finché non la cancelli.

La corsia superiore è la soluzione di cui si parla alla gente. In quella inferiore non c'è nulla da disegnare, perché OpenAI non ha nessuna impostazione di quella forma.

Cosa fa davvero dangerouslyAllowBrowser

Spegne una protezione, e il suo nome è la documentazione.

La libreria JavaScript ufficiale di OpenAI si rifiuta di girare in un browser per impostazione predefinita. Il README dice che il supporto al browser è "disabled by default to avoid exposing your secret API credentials", e che attivare dangerouslyAllowBrowser "can be dangerous because it exposes your secret API credentials in the client-side code".

Se la tua app chiama OpenAI dal browser, quell'opzione è impostata a true da qualche parte nel tuo codice, perché la libreria senza non parte. Qualcuno ha digitato la parola dangerously per far sparire un messaggio di errore. Di solito è così che questo riscontro viene prodotto, e la libreria te lo aveva detto per prima.

L'opzione ha usi reali, e sono tutti stretti: uno strumento interno dove conosci ogni utente, una chiave di sviluppo temporanea, una chiave così limitata che spenderla non costa nulla. Un'app pubblica su internet aperto non è nessuno di questi.

Quanto costa che qualcuno trovi la tua chiave

Una fattura, e un'app che smette di funzionare.

La fattura è la parte che le persone si aspettano. Le richieste di qualcun altro vengono addebitate al tuo account, e le chiamate a un modello non sono economiche per gli standard di un progetto secondario. Di solito chi gestisce l'app se ne accorge da una fattura più alta di quella del mese prima senza un motivo che possa indicare.

Il fermo è la parte che non si aspetta. Il tuo account ha limiti di frequenza, e il traffico di uno sconosciuto se li mangia. La tua app comincia a restituire errori nelle ore in cui qualcun altro sta lavorando, e questo si legge come un bug e non come un furto, così si passa una giornata a fare debug della cosa sbagliata.

C'è un terzo costo, e dipende da come era limitata la chiave. Una chiave API autentica ogni chiamata che il progetto a cui appartiene può fare, quindi una chiave con permessi ampi raggiunge tutto il resto che è archiviato lì: i file caricati sull'account, i modelli affinati, gli assistenti che hai costruito. Verifica cosa poteva davvero raggiungere la chiave prima di decidere che qui si trattava solo di soldi.

Come chiamare OpenAI dalla tua app senza spedire la chiave

Metti qualcosa di tuo fra il tuo visitatore e OpenAI.

La forma è la stessa ovunque. La tua app chiama un piccolo endpoint che è tuo. Quell'endpoint tiene la chiave, chiama OpenAI e restituisce la risposta. Il browser la chiave non la vede mai, perché la chiave non lascia mai il tuo server.

La stessa richiesta in entrambe le righe. Quello che cambia è da che parte del tuo server sta la chiave.

Dove vive quell'endpoint dipende da cosa hai usato per costruire:

  • Supabase nel tuo stack: una Edge Function, con la chiave salvata come secret nella dashboard di Supabase.
  • Deploy su Vercel o Netlify: una funzione serverless sotto /api, con la chiave nelle variabili d'ambiente lato server del progetto.
  • Lovable, Bolt o Replit: ognuno ha un proprio archivio di secret. La regola non cambia, e nemmeno la trappola: un archivio etichettato secret manda comunque il valore al browser se il codice che lo legge gira lì.

Due cose che vale la pena fare già che ci sei. Dai al tuo endpoint un limite di frequenza, perché un endpoint che chiama OpenAI per chiunque lo chieda è la stessa fattura per una via più lenta. E metti un limite di spesa mensile sul progetto OpenAI, l'unico controllo che mette un pavimento sotto il caso peggiore.

Se la tua app usa la Realtime API di OpenAI per la voce, il browser una credenziale gli serve davvero, e OpenAI documenta come dargliela: il tuo server conia un client secret a vita breve e passa quello alla pagina. Il biglietto esiste ancora, scade in pochi minuti, ed è stato il tuo server a emetterlo.

Come trovare gratis ogni chiave che la tua app spedisce

Comincia a mano, perché costa cinque minuti e non richiede di installare nulla. Guarda il sorgente del tuo sito in produzione e cercaci sk-proj- per una chiave OpenAI, sk-ant- per una di Anthropic, AIza per Google e eyJ per un token Supabase.

Quello che così sfugge è il JavaScript che la pagina carica dopo, che su un'app vibe-coded è quasi tutto. Il nostro scanner apre la tua app in un browser vero, aspetta che arrivino i bundle e legge quelli al posto dell'HTML. Decodifica anche ogni token Supabase e riporta il ruolo scritto dentro, così una chiave che nella tua pagina ci sta torna segnata come corretta e non sepolta in un muro di rosso.

Il controllo delle chiavi è uno di nove, e gli altri otto sono il motivo per cui un voto ti dice più di una ricerca:

I nove controlli in sola lettura che esegue una scansione, nell'ordine dell'elenco qui sotto. Ognuno viene letto da fuori, come vede la tua app uno sconosciuto.
Cosa guarda la scansioneLa domanda a cui risponde
Chiavi segrete nel tuo codiceC'è una chiave API a pagamento o di admin leggibile da chiunque?
Regole del databaseUno sconosciuto può leggere le righe dei tuoi utenti senza login?
File privatiFile .env o dump del database sono scaricabili da un URL?
Header di sicurezzaLe protezioni lato browser sono attive?
Bucket di storageChiunque può elencare i file caricati dai tuoi utenti?
Source mapIl tuo codice sorgente originale è pubblicato accanto all'app?
API aperte e CORSI tuoi endpoint rispondono a qualsiasi sito che chieda?
Scadenza del certificatoL'HTTPS è valido e non sta per scadere addosso ai visitatori?
Rinnovo del dominioIl nome è rinnovato prima che qualcun altro possa prenderselo?

Ottieni un voto, un punteggio e i conteggi sullo schermo in circa 20 secondi, senza account. Dai un indirizzo email e insieme arriva anche l'elenco dettagliato, con una correzione scritta per il tuo builder che puoi incollare così com'è.

Tre cose che non farà, e sono i motivi per cui è sicuro puntarlo su un'app in produzione: non fa mai il login, non scrive mai nulla e non conserva mai una chiave che trova. Un segreto esposto viene salvato come suggerimento mascherato nella forma sk-proj-…a1b2, e il valore vero viene buttato. Scansiona la tua app, oppure leggi prima cosa guarda ognuno dei nove controlli.

Cosa fare adesso

Cosa fare

  • Ruota la chiave per prima cosa, nella dashboard di OpenAI sotto API keys. Toglierla dal codice non chiude niente, perché il vecchio valore resta nella tua cronologia delle versioni e in ogni copia in cache della tua pagina.
  • Metti un limite di spesa mensile sul progetto a cui appartiene la chiave. È l'unico controllo che mette un tetto a quanto questo errore, o uno successivo, può costarti.
  • Sposta la chiamata dietro un endpoint tuo, e dai a quell'endpoint un suo limite di frequenza. Un browser non dovrebbe mai tenere una chiave che spende soldi.
  • Leggi la pagina dei consumi per i giorni in cui la chiave era attiva. Ruotare ferma quello che succede da adesso e non dice nulla su quello che è già successo.
  • Tieni un prefisso VITE_ o NEXT_PUBLIC_ lontano da tutto ciò che ti dispiacerebbe far leggere a uno sconosciuto. Quei prefissi vogliono dire pubblico, e il tuo strumento di build li prende in parola.

Se preferisci affrontare tutto in una volta sola, la checklist di sicurezza in 10 minuti copre questo insieme alle altre cose da chiudere in un'app appena lanciata. E per la domanda più ampia su quali chiavi stiano bene in un browser, abbiamo una guida per distinguere le chiavi pubblicabili da quelle segrete e un censimento di cosa hanno davvero spedito 30.998 app.

FAQ

Qualcuno ha trovato la mia chiave OpenAI nella mia app. Qual è la prima cosa da fare?

Ruotarla. Apri la dashboard di OpenAI in API keys, crea una chiave nuova, metti quella nuova sul tuo server e cancella la vecchia. Toglierla dal codice non è la stessa cosa, perché il vecchio valore resta nella tua cronologia delle versioni e in ogni copia in cache della tua pagina. Poi metti un limite di spesa sul progetto e leggi la pagina dei consumi per i giorni in cui la chiave era attiva.

Posso limitare una chiave OpenAI al mio dominio, come faccio con una di Google?

No. Una chiave API Google accetta una restrizione di referrer HTTP che la fa funzionare sul tuo sito e fallire ovunque altrove, ed è per questo che una chiave Google nella tua pagina di solito non è un problema. OpenAI non offre nulla di equivalente. Non esiste né una lista di domini consentiti né un controllo del referrer su una chiave API, quindi gli unici controlli che hai stanno sull'account dietro di essa: a quale progetto appartiene la chiave, quanto quel progetto può spendere e se la chiave esiste ancora.

La libreria di OpenAI ha un'opzione dangerouslyAllowBrowser. La rende sicura?

No, e il nome è l'avvertimento. OpenAI consegna il supporto al browser disattivato, e il suo stesso README dice che l'opzione è pericolosa perché espone le tue credenziali API segrete nel codice lato client. Attivarla non cambia ciò che il browser può leggere; impedisce soltanto alla libreria di rifiutarsi di partire. I casi stretti per cui è pensata sono strumenti interni con utenti noti e chiavi di sviluppo a vita breve, non un'app pubblica.

Quanto può spendere qualcuno con una chiave che ha trovato?

Quanto il tuo account consente, ed è per questo che il limite di spesa conta più della fattura che hai visto finora. Una chiave rubata attinge agli stessi limiti di frequenza che usa la tua app, quindi il primo sintomo spesso non è la fattura: è la tua app che comincia a fallire mentre qualcun altro sta lavorando. Metti un limite di spesa mensile sul progetto e hai un tetto su tutto questo.

Ho usato la chiave solo per una demo veloce. Conta lo stesso?

Sì. Una chiave resta attiva finché qualcuno non la revoca, e non sa di dover essere temporanea. Gli scraper automatici raccolgono di continuo stringhe a forma di chiave dalle pagine pubbliche, quindi l'età della demo gioca contro di te e non a tuo favore. Cancellare la chiave richiede meno tempo che decidere se valeva la pena cancellarla.

Scritto da

Vlad Tkachenko

Fondatore di Reeve

Passo le giornate a guardare app costruite con Lovable, Bolt, v0, Cursor e Replit, e la breve lista di errori che vi ricompaiono di continuo.

Altro sull'autore

Da leggere dopo

Tutti gli articoli

Non sai come sta messa la tua app?

Esegui una scansione gratuita e ottieni un voto chiaro da A a F in una ventina di secondi. Senza account e senza carta.

Scansiona la tua app gratis

Controllo esterno automatizzato, non un audit completo. L'assenza di risultati non è una garanzia di sicurezza.