Vai al contenuto

Basi della sicurezza

Nascondere una chiave API: spostala in una Edge Function Supabase

Nascondere una chiave API vuol dire toglierla dal browser, e una Edge Function Supabase è il posto più piccolo dove metterla. Due passaggi contano di più.

Vlad Tkachenko11 min di lettura
Un foglio con un buco a forma di chiave, e dietro una cassa sigillata con un lucchetto chiuso e la chiave visibile da uno sportellino.

In breve

  • Niente nel browser sa tenere un segreto, quindi nascondere una chiave API vuol dire spostarla dove si può. Una Edge Function Supabase è il server più piccolo che puoi avere.
  • Lo spostamento sono quattro passaggi. Sostituisci prima la chiave che hai già pubblicato, perché la copia nel tuo vecchio bundle resta leggibile anche dopo che la togli.
  • Una function in produzione pretende un token di default, e la chiave pubblica del tuo stesso frontend soddisfa quella pretesa. Controllare chi sta chiamando è un passaggio a parte, ed è quello che impedisce a uno sconosciuto di consumare la tua quota.

Ogni guida su una chiave API finita in giro si chiude allo stesso punto: spostala su un server. Poi si ferma. Ti resta un'app costruita in Lovable, Bolt o Cursor, nessun server all'orizzonte, e una frase che dà per scontato che tu sappia già cosa fare dopo.

Ecco la parte che guida dopo guida lascia fuori. Spostare la chiave in una Edge Function Supabase la toglie dalla tua pagina, e da solo questo non impedisce a uno sconosciuto di usarla. La documentazione di Supabase dice perché: il controllo che una function in produzione esegue di default accetta anche la tua chiave pubblica, e la tua chiave pubblica sta nel tuo frontend, a disposizione di chi voglia copiarla. Nascondere la chiave è la metà semplice. Dell'altra metà non scrive nessuno: decidere a chi risponde la tua nuova function.

Pensa alla chiave come al passe-partout di un magazzino. Adesso è attaccato dentro la vetrina, dove lo legge chiunque si fermi. Una Edge Function è un retrobottega con uno sportello: la chiave va lì dentro, e i visitatori chiedono allo sportello invece di entrare e servirsi. Che sia un miglioramento dipende da per chi si apre lo sportello.

Dove metto la mia chiave API, se non nel frontend?

Ovunque tranne il browser. Una Edge Function è la versione più piccola di quel posto.

Un browser non sa tenere un segreto. Tutto quello che serve alla tua pagina per funzionare viene scaricato da ogni visitatore, e un visitatore può leggerlo tutto: il codice, le immagini, i valori compilati dentro il codice. Non è un difetto del tuo strumento. È quello che è una pagina web. Quali chiavi API sono sicure nel frontend è la versione lunga; quella corta è che una chiave sk_, una chiave OpenAI o una chiave segreta Supabase non sarebbe mai sopravvissuta a un viaggio nel browser.

Una Edge Function Supabase è un piccolo pezzo di codice che gira sulle macchine di Supabase. Può leggere i segreti che hai impostato sul progetto, risponde a un indirizzo web tutto suo, e la tua app la chiama per nome. Scrivi un file, Supabase lo esegue, e non c'è nessun server da affittare, aggiornare o tenere sveglio.

Sopra: la chiave sta nel file che ogni visitatore scarica, quindi ce l'ha ogni visitatore. Sotto: il browser chiede alla function, e la chiave non esce mai da Supabase.

Sostituisci la chiave già pubblicata, prima di spostare qualsiasi cosa

La chiave che oggi sta nel tuo frontend è già pubblica, e resta pubblica dopo che la togli dal codice.

Ogni visitatore che ha caricato il tuo sito l'ha scaricata. Ogni crawler pure, e ce ne sono di automatici che percorrono tutto il web cercando esattamente queste stringhe. Il tuo vecchio bundle sta inoltre ancora nella tua cronologia delle versioni e in ogni cache che ha una copia della tua pagina. Cancellare una riga da un file che controlli non cambia niente su un valore già distribuito.

Quindi l'ordine è: creare una chiave nuova dal fornitore, tenerla da parte un momento, e revocare la vecchia appena la function qui sotto è attiva. Poi leggi la tua pagina di fatturazione e i log dei consumi per tutto il periodo in cui la vecchia chiave è stata fuori. Sostituirla ferma quello che viene; su quello che è già successo non ha nessun effetto. Se la chiave era una chiave segreta Supabase, l'ordine in cui lavorare ha una sfumatura che conviene leggere prima.

I quattro passaggi

Salva il segreto, scrivi la function, mettila in produzione, poi cambia la tua app perché chiami la function invece del fornitore.

PassaggioDa riga di comandoNella dashboard
1. Salvare il segretosupabase secrets set MY_API_KEY=…Edge Functions → Secrets, poi Key e Value
2. Scrivere la functionsupabase functions new forward-requestEdge Functions → Deploy a new function → Via Editor
3. Metterla in produzionesupabase functions deployIl bottone Deploy sotto l'editor
4. Chiamarla dalla tua appsupabase.functions.invoke('…')Uguale, nel codice della tua app

Dentro la function il segreto arriva come variabile di ambiente, cioè un valore con un nome che il codice può leggere e nessuno da fuori. La documentazione Supabase sui segreti delle Edge Functions ha il riferimento completo, e tre dettagli lì dentro risparmiano un'ora di confusione:

  • Il nome di un segreto non può iniziare con SUPABASE_. Quel prefisso è riservato ai valori che Supabase imposta per te, e lo rifiutano sia la dashboard sia l'API.
  • Un segreto nuovo è leggibile subito. Non devi rimettere in produzione la function dopo averne cambiato uno.
  • Locale e produzione sono separati. Lo stack locale legge supabase/functions/.env, che è un posto diverso dai segreti del tuo progetto attivo, quindi imposta il valore in entrambi, altrimenti la function funziona sulla tua macchina e fallisce una volta in produzione.

Poi cancella la vecchia variabile dal tuo frontend. Se si chiamava VITE_, NEXT_PUBLIC_ o EXPO_PUBLIC_, quel prefisso era l'istruzione di compilare il valore dentro il bundle, e il prefisso è tutta la storia di come ci è finito.

Se serve un token, vuol dire che chiamano solo i miei utenti?

No, ed è il punto di questo articolo che vale due letture.

Supabase attiva di default un controllo chiamato verify_jwt. Guarda l'intestazione Authorization prima che il tuo codice parta e rifiuta la richiesta se lì non c'è niente di valido. Sembra una porta che aprono solo i tuoi utenti. Non lo è, perché Supabase documenta anche che lo stesso controllo accetta una chiave pubblica o segreta su entrambe le intestazioni, per compatibilità con i progetti più vecchi, e aggiunge chiaramente che il controllo da solo non autentica chi chiama mandando soltanto una chiave API.

La tua chiave pubblica sta nel tuo frontend. È corretto ed è pensata per stare lì. Vuol dire però anche che chi apre il tuo sito, legge la chiave e la manda alla tua nuova function passa il controllo della piattaforma ed entra nel tuo codice.

Nel magazzino, verify_jwt è un portiere che controlla che tu abbia un pass da visitatore. Ce l'ha ogni visitatore, perché li distribuisci tu all'ingresso. Chi sta allo sportello fa un altro mestiere, ed è quello che chiede quale visitatore sei.

Tutti e due passano il controllo della piattaforma. Solo il secondo passa un controllo su chi sta chiamando, e quel secondo controllo devi aggiungerlo tu.

Chiudere la function, per non aver costruito un proxy aperto

Decidi quali chiamanti accetta la tua function, e scrivi quella decisione dentro la function.

Supabase fornisce un wrapper per questo, quindi è una riga e non un progetto. Metti auth: 'user' e la function accetta il token di un utente autenticato e passa al tuo codice un client di database già limitato alle regole di Row Level Security di quell'utente:

import { withSupabase } from 'npm:@supabase/server@1'

export default {
  fetch: withSupabase({ auth: 'user' }, async (_req, ctx) => {
    // ctx.userClaims dice chi sta chiamando. Rifiuta qui quello che non vuoi,
    // poi chiama il fornitore con il segreto preso dall'ambiente.
    return Response.json({ ok: true })
  }),
}

La pagina Securing Edge Functions elenca gli altri modi, e due contano per un'app normale. Una function chiamata da un'altra macchina invece che da un browser usa auth: 'secret', con la chiave segreta mandata nell'intestazione apikey. Una function che riceve webhook da Stripe o da GitHub non può usare nessuno dei due, perché quei fornitori non hanno nessun token tuo; mette verify_jwt = false e verifica la firma del fornitore dentro l'handler.

Qualunque cosa tu scelga, decidi con cura cosa succede quando arriva un chiamante che non ti aspettavi. L'esempio che fa Supabase di una function che può accettare chiunque è un controllo di stato, e un controllo di stato non costa niente quando lo chiama uno sconosciuto. Una function che inoltra una chiamata API a pagamento te la fattura una per una.

Posso usare service_role in una Edge Function?

Sì. Una function è l'unico posto in cui una chiave segreta Supabase ha senso.

E non devi nemmeno incollarla. Supabase mette le chiavi del progetto nell'ambiente della function, quindi il codice le legge da lì e non da un segreto impostato da te. I progetti nuovi ricevono SUPABASE_SECRET_KEYS e SUPABASE_PUBLISHABLE_KEYS, ciascuno un piccolo dizionario di chiavi con nome; i progetti più vecchi ricevono SUPABASE_SERVICE_ROLE_KEY e SUPABASE_ANON_KEY con i loro nomi di sempre. Cos'è cambiato quando Supabase ha rinominato le sue chiavi ti dice quale coppia hai.

Usa la chiave segreta per il lavoro che deve davvero vedere ogni riga, per esempio scrivere una traccia che l'utente non possa modificare. Per tutto quello che un utente legge su di sé, usa il suo token e lascia filtrare le tue regole di Row Level Security, che servono a quello.

Verificarlo dal browser, l'unica prova che conta

Carica il tuo sito attivo, apri la scheda di rete, e guarda cosa manda davvero il tuo browser.

Le due cose da controllare:

  • Nessuna richiesta porta la chiave. Percorri la parte della tua app che prima chiamava il fornitore. La richiesta in uscita deve andare a …supabase.co/functions/v1/la-tua-function, e l'unica credenziale lì dentro deve essere la tua chiave pubblica o il token del tuo utente.
  • La chiave non sta nel codice scaricato. Usa la ricerca del browser su tutti i file caricati e incolla la prima dozzina di caratteri della vecchia chiave. Nessun risultato è la risposta che vuoi.

Solo una ricompilazione e una nuova messa in produzione tolgono un valore dal bundle. Una chiave che compare ancora nella ricerca di solito vuol dire che il frontend è uscito prima che ne uscisse la variabile.

La nostra scansione gratuita fa il secondo controllo da fuori e nomina quello che riesce a leggere nel tuo bundle attivo, compresa quale chiave Supabase hai pubblicato. Impiega circa 20 secondi e non serve nessun account: scansiona la tua app.

Come farlo da Lovable, Bolt o Replit

Usa la dashboard di Supabase. Lì dentro non c'è nessun passaggio da terminale.

Apri il tuo progetto, scegli Edge Functions nella barra laterale, poi Deploy a new function → Via Editor. La guida rapida della dashboard di Supabase la percorre con gli screenshot, e tra i modelli che propone ce n'è uno per inoltrare a un fornitore di IA, cioè esattamente la forma di questo lavoro. La messa in produzione impiega tra i dieci e i trenta secondi, e la function resta attiva a un indirizzo tutto suo. Il tuo segreto va sulla pagina Edge Function Secrets, nella stessa sezione.

Un avvertimento da conoscere prima di affidartici: Supabase scrive che l'editor della dashboard non ha controllo di versione, né versioni, né ritorno indietro, e lo consiglia per il lavoro veloce invece che per codice destinato a restare. Premere Deploy sovrascrive quello che c'era. Se la function finisce per fare qualcosa a cui tieni, scaricala da quella pagina e tieni il file da parte.

C'è anche una versione di questa domanda a forma di Replit, perché Replit ha un suo archivio di segreti e la tua app lì esegue un suo server. Cosa fanno davvero i Replit Secrets è quella versione.

Quando una Edge Function è la risposta sbagliata

Due casi, e in tutti e due lo spostamento ti costa lavoro e non porta niente.

La chiave era pubblica per progetto. Una chiave Stripe pk_live_, una chiave pubblica Supabase o una chiave anon, un token pubblico Mapbox: sono fatte per stare in un browser, e la protezione sta altrove. Metterne una dietro una function aggiunge un giro e non toglie nessun rischio. Quali chiavi sono è l'elenco.

La chiave si può limitare al tuo sito. Le chiavi browser di Google sono l'esempio solito: nella console Google puoi limitarne una ai tuoi domini, ed è proprio la soluzione che Google prevede per questo caso. La chiave resta leggibile e smette di servire a chi la copia.

Tutto il resto sta su un server. Una chiave OpenAI o Anthropic non ha variante pubblica, ed è per questo che una chiave di fornitore di IA nel tuo frontend non ha nessuna impostazione che la salvi, e nessuna minificazione nasconde una chiave segreta Stripe a chi legge la tua pagina.

Cosa fare adesso

Cosa fare

  • Sostituisci prima la chiave. Creane una nuova dal fornitore, mettila nel segreto della function, e revoca la vecchia appena la function è attiva.
  • Salva il segreto sul tuo progetto Supabase, non nel tuo repository e non in una variabile VITE_. Impostalo separatamente per lo sviluppo locale e per la produzione.
  • Metti in produzione una function che usa il segreto, e cambia la tua app perché chiami la function per nome invece del fornitore.
  • Aggiungi un controllo su chi sta chiamando. verify_jwt da solo accetta la chiave pubblica del tuo stesso frontend, quindi non tiene fuori uno sconosciuto.
  • Cancella la vecchia variabile, ricompila, e conferma nella scheda di rete del browser che niente in uscita porta la chiave.
  • Leggi fatturazione e consumi per tutto il periodo in cui la vecchia chiave è stata pubblica. Sostituirla ferma il prossimo addebito e non l'ultimo.

Se preferisci passare in rassegna tutta la tua app invece di una sola chiave, la checklist di sicurezza in 10 minuti copre questo insieme alle altre cose da chiudere in un'app appena lanciata.

FAQ

Dove metto la mia chiave API, se non nel frontend?

Ovunque tranne il browser. Una Edge Function Supabase è l'opzione più piccola: salvi la chiave come segreto sul tuo progetto, scrivi un file piccolo che la usa, e la tua app chiama quella function per nome invece di chiamare il fornitore direttamente. Una rotta serverless dal tuo hosting o un server tuo funzionano allo stesso modo. Quello che hanno in comune è che la chiave viene letta dove il tuo visitatore non la vede.

Che cos'è una Edge Function Supabase?

Un piccolo pezzo di codice che gira sulle macchine di Supabase invece che nel browser del tuo visitatore. Risponde a un indirizzo web tutto suo, può leggere i segreti che hai impostato sul progetto, e la tua app lo chiama con supabase.functions.invoke. Scrivi un file, Supabase lo esegue, e non c'è nessun server da affittare o mantenere.

Devo sostituire la chiave dopo averla spostata?

Sì, e per prima cosa. La chiave che stava nel tuo frontend è stata scaricata da ogni visitatore e da ogni crawler che ha letto il tuo sito, e toglierla dal codice non cambia niente su quelle copie. Crea una chiave nuova dal fornitore, mettila nel segreto della tua Edge Function e revoca la vecchia. Poi controlla fatturazione e consumi per tutto il periodo in cui la vecchia chiave era attiva.

Chiunque può chiamare la mia Edge Function?

Di default una function rifiuta una richiesta senza nessun token, ma quel controllo è più debole di quanto sembri. Supabase documenta che il controllo della piattaforma accetta anche la tua chiave pubblica o segreta, e la tua chiave pubblica sta nel tuo frontend, dove chiunque può leggerla. Quindi il controllo ferma una richiesta vuota e non una decisa. Per accettare solo i tuoi utenti autenticati, verifica chi chiama dentro la function.

Posso usare service_role in una Edge Function?

Sì. Una Edge Function è l'unico posto in cui una chiave segreta Supabase ha senso, e Supabase mette le chiavi del progetto nell'ambiente della function per te, quindi non ne incolli mai una. Usala per il lavoro che deve vedere ogni riga, e usa il token di chi chiama per tutto ciò che deve rispettare le tue regole di Row Level Security.

Posso farlo senza terminale?

Sì. La dashboard di Supabase ha una sezione Edge Functions dove scrivi una function nel browser, premi Deploy e imposti il tuo segreto sulla pagina Edge Function Secrets. Supabase avverte che l'editor della dashboard non tiene nessuna cronologia delle versioni, quindi lo consiglia per il lavoro veloce e lascia la riga di comando per quello che vuoi tenere.

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.