Base64, codifica URL e JWT spiegati
Tutti e tre compaiono ovunque i dati debbano viaggiare come testo: negli URL, negli header HTTP, nel JSON e nei token di accesso. Nessuno dei tre nasconde qualcosa. Questa guida spiega come funziona ciascuno, dove lo incontri e gli errori che causano la maggior parte dei problemi.
Codifica non è cifratura
Una codifica cambia il modo in cui i dati sono scritti, così che possano passare per un canale che accetta solo determinati caratteri. Chiunque può invertirla, perché non c’è alcuna chiave. Anche la cifratura trasforma i dati, ma solo chi possiede la chiave può riportarli all’originale. Un hash è un’altra cosa ancora: un’impronta a senso unico che non si può invertire affatto, come spiegato in Le funzioni di hash spiegate.
Base64 e la codifica URL sono codifiche. Un JWT di solito è firmato, il che dimostra chi lo ha emesso e che il contenuto non è stato modificato, ma il contenuto è solo codificato. Se un valore deve restare segreto, serve la cifratura, non uno di questi formati.
Come funziona Base64
Base64 legge i dati tre byte (24 bit) alla volta e li divide in quattro gruppi di sei bit. Ogni gruppo è un numero da 0 a 63 e viene scritto come un carattere di un alfabeto di 64 caratteri: A–Z per 0–25, a–z per 26–51, 0–9 per 52–61, poi + e /. La parola «Man» mostra i passaggi:
| Passaggio | M | a | n |
|---|---|---|---|
| Valore del byte | 77 | 97 | 110 |
| Bit | 01001101 | 01100001 | 01101110 |
| Quattro gruppi da 6 bit | 010011 · 010110 · 000101 · 101110 = 19 · 22 · 5 · 46 | ||
| Base64 | T W F u → TWFu | ||
Se la lunghezza dell’input non è un multiplo di tre, l’ultimo gruppo viene completato e segnalato con il padding: un byte rimasto dà due caratteri e ==, due byte rimasti danno tre caratteri e =. «Ma» diventa TWE= e «M» diventa TQ==.
Poiché ogni tre byte diventano quattro caratteri, Base64 aumenta i dati di circa un terzo, più le interruzioni di riga che alcuni formati inseriscono. Una foto da 3 MB inviata come allegato email viaggia come circa 4 MB di testo, e un’immagine incorporata in un foglio di stile come data URL pesa circa un terzo in più del file.
Dove incontri Base64
- Allegati email (MIME): testo Base64 spezzato a 76 caratteri per riga.
- Data URL come
data:image/png;base64,iVBORw0…inseriscono un piccolo file direttamente in HTML o CSS. - Autenticazione HTTP Basic: invia
user:passwordin Base64 nell’header Authorization. Questo nasconde la password solo a un’occhiata veloce, quindi è sicuro solo su HTTPS. - Certificati e chiavi nei file PEM sono Base64 tra le righe
-----BEGIN …-----e-----END …-----, spezzati a 64 caratteri. - JSON e XML trasportano valori binari, come firme o miniature, come stringhe Base64, perché non hanno un tipo binario.
Base64URL e padding
I caratteri + e / del Base64 standard hanno un significato negli URL e nei percorsi dei file, quindi un secondo alfabeto, Base64URL, usa al loro posto - e _. Di solito omette anche il padding =, perché il decodificatore può calcolare la lunghezza. JWT, molti token API e alcuni nomi di file usano Base64URL. Per trasformare il Base64 standard in Base64URL, sostituisci + con -, / con _ ed elimina gli = finali; per tornare indietro, inverti le sostituzioni e aggiungi = finché la lunghezza non è un multiplo di quattro.
La percent-encoding negli URL
Un URL può contenere solo un insieme limitato di caratteri, e alcuni di essi danno struttura all’indirizzo: / e ? separano percorso e query, & e = separano i parametri, # apre il frammento. Quando un valore contiene uno di questi caratteri, o qualsiasi carattere fuori dall’ASCII, viene codificato in percentuale: ogni byte della sua forma UTF-8 è scritto come % seguito da due cifre esadecimali. «café» diventa caf%C3%A9, perché é corrisponde ai due byte C3 A9.
Lettere, cifre e - . _ ~ non vanno mai codificati. Questi caratteri causano più spesso problemi:
| Carattere | Codificato | Perché conta |
|---|---|---|
| spazio | %20 | Non ammesso in un URL |
| & | %26 | Aprirebbe un nuovo parametro |
| = | %3D | Chiuderebbe il nome del parametro |
| ? | %3F | Aprirebbe la query |
| # | %23 | Aprirebbe il frammento, che non viene inviato al server |
| / | %2F | Aggiungerebbe un segmento al percorso |
| + | %2B | Viene letto come spazio nei dati dei moduli |
| % | %25 | Aprirebbe una sequenza di escape |
Codifica ogni valore separatamente, poi componi l’URL. In JavaScript, encodeURIComponent lo fa per un singolo valore e codifica anche / ? & = #, mentre encodeURI è pensata per un indirizzo completo e lascia quei caratteri come sono. Usare encodeURI per un singolo valore è una causa comune di stringhe di query rotte.
Segni più, spazi e doppia codifica
I moduli HTML inviano i campi come application/x-www-form-urlencoded, dove uno spazio diventa +. Nella sintassi URL generica, + è solo un segno più. Così una ricerca di «C++» inviata come q=C++ arriva a un server che decodifica i dati dei moduli come «C» seguito da due spazi; andrebbe inviata come q=C%2B%2B. Quando decodifichi a mano una stringa di query, verifica prima con quale regola è stata prodotta.
La doppia codifica avviene quando un valore già codificato viene codificato di nuovo: %20 diventa %2520, perché lo stesso % diventa %25. Se in un URL vedi %25 seguito da due cifre esadecimali, un passaggio della catena ha codificato due volte; decodifica una volta e cerca dove avviene il passaggio in più.
Che cosa contiene un JWT
Un JSON Web Token (JWT) trasporta delle affermazioni (claim), come chi è l’utente e fino a quando vale il token, dal sistema che lo emette a quello che lo riceve. La forma firmata comune è composta da tre parti Base64URL separate da punti: header, payload e firma. Decodificate, le prime due sono normale JSON:
Header: {"alg": "HS256", "typ": "JWT"}
Payload: {"sub": "user-42", "role": "editor", "iat": 1767225600, "exp": 1767229200}
Lo standard (RFC 7519) definisce alcuni claim registrati; le applicazioni ne aggiungono di propri, come role qui sopra.
| Claim | Significato |
|---|---|
| iss | Emittente: chi ha creato il token |
| sub | Soggetto: di solito l’ID dell’utente o dell’account |
| aud | Destinatario: il servizio a cui il token è destinato |
| exp, nbf, iat | Scadenza, non prima di, emissione: ore Unix in secondi |
| jti | ID del token, per rilevarne il riuso o revocarne uno singolo |
Poiché il payload è solo codificato, chiunque abbia il token può leggerlo. Non inserire dati personali né segreti e tieni il token piccolo: viaggia con ogni richiesta, e molti server limitano la dimensione degli header.
Firmato non significa segreto: come si verifica un JWT
La firma è calcolata su header e payload. Con HS256, chi emette e chi riceve condividono un segreto e usano HMAC con SHA-256. Con RS256 o ES256, chi emette firma con una chiave privata e chi riceve verifica con la chiave pubblica corrispondente, quindi non può creare token per conto proprio.
Un server che riceve un token deve verificare la firma con l’algoritmo e la chiave attesi, e controllare exp, nbf, iss e aud, prima di fidarsi di qualsiasi claim. La sola decodifica, come quella dello strumento LeedyBox, salta tutto questo: mostra che cosa c’è nel token, non se il token è autentico. Le librerie dovrebbero inoltre rifiutare l’algoritmo «none», che indica un token non firmato, a meno che i token non firmati siano attesi di proposito. Se il contenuto stesso va nascosto, il token viene cifrato (JWE) ed è composto da cinque parti illeggibili senza chiave.
Problemi comuni e soluzioni
| Sintomo | Causa probabile | Soluzione |
|---|---|---|
| Il Base64 decodificato è testo senza senso | I dati sono binari, o testo in un’altra codifica | Verifica che cosa è stato codificato; salva i dati binari come file |
| «Carattere non valido» in decodifica | Prefisso data:, virgolette o alfabeto Base64URL | Elimina tutto fino alla virgola; usa un decodificatore che accetti - e _ |
| Un parametro arriva troncato | Un & o un # non codificato dentro il valore | Codifica ogni valore con encodeURIComponent |
| %2520 nell’URL | Valore codificato due volte | Codifica una sola volta, dove l’URL viene composto |
| Token rifiutato anche se exp è nel futuro | Destinatario o emittente sbagliato, orologi sfasati o chiave diversa | Confronta aud, iss e chiave di firma con quelli attesi dal server |
| Scadenza tra migliaia di anni | exp scritto in millisecondi anziché in secondi | Memorizza exp, nbf e iat in secondi |
Domande frequenti
Base64 può proteggere una password o una chiave API?
No. Chiunque può invertire Base64 in un secondo. Le credenziali codificate in Base64, per esempio in un file di configurazione o in un URL, sono esposte quanto il testo in chiaro.
Posso mettere dati personali nel payload di un JWT?
Meglio di no. Chiunque abbia il token può leggere il payload, comprese estensioni del browser, log e proxy che vedono la richiesta. Metti un ID nel token e recupera i dettagli sul server.
La codifica URL protegge dagli attacchi di iniezione?
Solo nel senso che un valore non può rompere la struttura dell’URL. Il server deve comunque validare ed eseguire l’escape del valore decodificato per il contesto in cui lo usa, come SQL, HTML o un comando di shell.
Perché la mia stringa Base64 è più lunga del file?
Ogni tre byte diventano quattro caratteri, quindi il testo è circa il 33% più grande, e le interruzioni di riga aggiungono un po’ di più. Per i file grandi, inviare i byte direttamente è più efficiente che usare Base64.