Le chiavi API spiegate: lunghezza, formato, archiviazione e rotazione

Aggiornato:

Una chiave API è una lunga stringa casuale che un programma invia con ogni richiesta per indicare quale applicazione sta chiamando. A differenza di una password, nessuno deve ricordarla, quindi può essere lunga e casuale quanto serve. A differenza di un token di accesso dopo il login, di solito vale finché qualcuno non la revoca. La documentazione di Google riassume il rischio: le chiavi API sono credenziali al portatore, quindi chi ne possiede una può usarla.

Questa quarta parte della serie sulla sicurezza segue tutta la vita di una chiave: quanti bit casuali le servono, in quale formato scriverla, perché esistono prefissi come sk_live_, come inviarla, che cosa deve salvare il server e come ruotarla e revocarla.

Chiave API, password o token di accesso?

Tutti e tre dimostrano qualcosa a un server, ed è per questo che vengono spesso confusi. La differenza sta in chi li crea, quanto durano e che cosa conserva il server:

CredenzialeCreata daDurataIl server conserva
Passworduna personafinché la persona non la cambiaun hash lento (Argon2id, scrypt, bcrypt)
Chiave APIun generatore casualefinché non viene ruotata o revocataun hash veloce, come SHA-256
Token di accesso (OAuth, JWT)il server di accesso, dopo un loginda minuti a oreuna firma da verificare, o un record del token

Una chiave API di solito identifica un’applicazione o un’integrazione, non una persona. Quando una chiave farebbe le veci di una persona che accede, un token di accesso ottenuto con un vero flusso di login è in genere lo strumento migliore, come spiega la parte precedente sui JSON Web Token.

Quanto deve essere lunga una chiave

Ciò che protegge una chiave è il numero di bit casuali che contiene, non la sua lunghezza in caratteri. Ogni bit raddoppia il numero di chiavi possibili: 128 bit casuali danno circa 3,4 × 10³⁸ chiavi, quindi provarne anche solo un milionesimo a un miliardo di tentativi al secondo richiederebbe circa 10¹⁶ anni, quasi un milione di volte l’età dell’universo. 128 bit sono quindi un minimo solido, e 256 bit costano solo qualche carattere in più.

I bit devono venire da un generatore casuale crittografico. Nel browser è crypto.getRandomValues(); MDN avverte che Math.random() non fornisce numeri crittograficamente sicuri e non va usato per nulla che riguardi la sicurezza. I linguaggi lato server hanno i loro equivalenti, come il modulo secrets di Python o crypto.randomBytes() in Node.js.

GitHub ha portato i suoi token OAuth da 160 a 178 bit di entropia quando ne ha cambiato il formato nel 2021. Un caso richiede ancora di più: un segreto che firma JSON Web Token con HMAC. L’RFC 7518 richiede una chiave lunga almeno quanto l’output dell’hash: 256 bit per HS256, 384 per HS384 e 512 per HS512.

Formati: base62, esadecimale o base64url

I bit casuali vanno scritti come testo. L’alfabeto decide quanti caratteri servono e dove si può incollare la chiave senza problemi:

FormatoCaratteriBit per carattereLunghezza per 256 bit
base62a–z, A–Z, 0–95,9543
hex0–9, a–f464
base64urla–z, A–Z, 0–9, - e _643

Il base62 è diventato comune per le chiavi perché contiene solo lettere e cifre: nulla che un URL, una shell o un file di configurazione trattino in modo speciale, e un doppio clic seleziona l’intera chiave. L’esadecimale è più lungo ma funziona dove sono accettati solo 0–9 e a–f. Il base64url, dell’RFC 4648, è il più corto, ma il suo - può interrompere la selezione con doppio clic. Il Base64 standard è una scelta poco adatta, perché + e / vanno codificati negli URL.

Scrivere correttamente in base62 richiede attenzione: un byte casuale ha 256 valori, e 256 non è un multiplo di 62, quindi un semplice byte % 62 rende un po’ più probabili i primi otto caratteri. Un buon generatore scarta i byte da 248 in su, come fa il generatore di nTools.

Prefissi e scansione dei segreti

Molti fornitori fanno iniziare le loro chiavi con un prefisso fisso e leggibile. Le chiavi segrete di Stripe iniziano con sk_live_ o sk_test_, le chiavi pubblicabili con pk_ e quelle limitate con rk_. Dal 2021 i token di GitHub iniziano con ghp_ per i token di accesso personali, gho_ per i token OAuth e ghs_ per quelli da server a server.

Un prefisso non è segreto e non aggiunge robustezza, ma svolge tre compiti utili. Si capisce a colpo d’occhio a che cosa serve una chiave e se è di prova. GitHub ha scelto il trattino basso perché non è un carattere Base64, così stringhe casuali come gli hash non possono sembrare un token per caso, e perché un doppio clic seleziona l’intero token. Soprattutto, un prefisso permette agli scanner di trovare le chiavi trapelate: la scansione dei segreti di GitHub esamina tutta la cronologia Git di ogni ramo e, quando trova il segreto di un fornitore partner, avvisa quel fornitore, che può revocare la chiave.

GitHub chiude inoltre i suoi token con un checksum CRC32 a 32 bit scritto in sei caratteri base62. Non aggiunge sicurezza, ma permette a uno scanner di scartare stringhe casuali offline, senza chiedere al server se il token esiste.

Inviala in un header, mai nell’URL

L’RFC 6750, che definisce i token al portatore di OAuth 2.0, dice che i client devono inviarli nell’header Authorization e non metterli negli URL delle pagine, perché gli indirizzi finiscono nella cronologia del browser e nei log dei server. Google dà lo stesso consiglio per le proprie chiavi API e offre un header apposito. Lo stesso RFC richiede TLS: una chiave inviata in semplice http può essere letta da chiunque lungo il percorso.

Che cosa deve salvare il server

Il server non ha bisogno di conservare la chiave, solo un modo per riconoscerla. Salva un hash della chiave, come il suo SHA-256; quando arriva una richiesta, calcola l’hash della chiave ricevuta e cercalo. Se il database trapela, gli hash non servono a chiamare l’API e da essi non si ricavano le chiavi.

Le password hanno bisogno di un hash lento perché, come spiega il Password Storage Cheat Sheet di OWASP, un hash veloce come SHA-256 permette agli attaccanti di provare moltissimi tentativi in poco tempo. Quel ragionamento presuppone che le password si possano indovinare. Una chiave con 256 bit casuali non si può indovinare, quindi basta un solo hash veloce, e ogni richiesta resta rapida.

Poiché si conserva solo l’hash, la chiave completa può essere mostrata una volta sola, quando viene creata. Stripe funziona così per le chiavi segrete live che crei tu: la dashboard mostra la chiave una sola volta e non può rivelarla di nuovo. Conserva in chiaro il prefisso e gli ultimi caratteri se le persone devono distinguere le loro chiavi.

Tenere le chiavi fuori dal codice

Le chiavi trapelate si trovano soprattutto dove gli sviluppatori le hanno messe per comodità. Stripe avverte che malintenzionati scansionano di continuo i repository pubblici in cerca di chiavi API e che anche i repository privati possono essere esposti. I suoi consigli, ripresi da Google e da OWASP:

Rotazione e revoca

Ogni chiave dovrebbe poter essere sostituita con poco preavviso. Ruotare significa creare una nuova chiave, passare tutte le applicazioni alla nuova e poi eliminare la vecchia. Fatto con regolarità, limita il tempo per cui una chiave rubata continua a funzionare e, come osserva Stripe, dimostra che sai dove viene usata ogni chiave. La dashboard di Stripe mantiene attive insieme la vecchia e la nuova chiave fino a sette giorni, per rendere il passaggio graduale.

Quando una chiave viene esposta — caricata in un repository, incollata in una chat, scritta in un log — revocala subito, anche se non sei sicuro che qualcuno l’abbia vista. Eliminare il commit non basta: la chiave potrebbe essere già stata copiata, e resta nella cronologia Git e in ogni clone. Se il sistema lo consente, dai alle chiavi anche una data di scadenza, così una chiave dimenticata smette di funzionare da sola.

Generare una chiave

Il generatore di chiavi API di nTools crea chiavi in base62, esadecimale o base64url, da 128 a 512 bit, con un prefisso facoltativo e, se vuoi, l’hash SHA-256 da salvare sul server. Usa crypto.getRandomValues() e funziona interamente nel tuo browser: le chiavi non vengono inviate da nessuna parte.

Generatore di chiavi API

Domande frequenti

Posso usare un UUID come chiave API?

Un UUID versione 4 ha solo 122 bit casuali e una forma fissa e riconoscibile, ed è stato pensato come identificatore, non come segreto. Meglio generare una chiave dedicata da 128 bit o più.

Una chiave generata nel browser è sicura?

Sì, se viene da crypto.getRandomValues(), il generatore crittografico che il browser usa per la propria sicurezza, e la pagina non la invia da nessuna parte. Il generatore di nTools funziona così: nessuna richiesta lascia la pagina quando premi «Genera».

Le chiavi API devono scadere?

Se il sistema lo permette, sì: una data di scadenza limita il danno di una chiave dimenticata o trapelata senza che nessuno se ne accorga. Le chiavi di lunga durata sono comuni, e per questo rotazione e revoca rapida contano ancora di più.

Ho caricato una chiave su Git. E adesso?

Prima revocala o ruotala, poi pulisci il repository. Riscrivere la cronologia dopo non rende di nuovo sicura la chiave, perché potrebbe essere già stata copiata.

Meglio cifrare la chiave invece di salvarne l’hash?

Solo se il server stesso deve usare di nuovo la chiave, per esempio per chiamare un altro servizio. Una chiave che va solo verificata va salvata come hash: così non c’è nulla da decifrare se il database trapela.

Precedente della serie ← JSON Web Token spiegati: claim, firme e validazione sicura

Riferimenti

  1. GitHub — Behind GitHub’s new authentication token formats (April 2021)
  2. GitHub Docs — About secret scanning
  3. GitHub Docs — About secret scanning for partners
  4. Stripe — API keys
  5. Stripe — Best practices for managing secret API keys
  6. Google Cloud — Best practices for managing API keys
  7. RFC 6750 — The OAuth 2.0 Authorization Framework: Bearer Token Usage (October 2012)
  8. RFC 7518 — JSON Web Algorithms (JWA) (May 2015)
  9. RFC 4648 — The Base16, Base32, and Base64 Data Encodings (October 2006)
  10. OWASP — Secrets Management Cheat Sheet
  11. OWASP — Password Storage Cheat Sheet
  12. MDN — Crypto: getRandomValues()
  13. MDN — Math.random()

Guide correlate