Ispettore di codifica percentuale

La codifica percentuale non è un'unica regola ma diverse, e sono in disaccordo su una dozzina di caratteri. Questa pagina fa passare lo stesso testo attraverso encodeURIComponent, encodeURI e la serializzazione application/x-www-form-urlencoded affiancate, evidenzia i caratteri su cui divergono, stampa una riga per carattere con i suoi byte UTF-8 e la sua classe RFC 3986, e dice quante volte una stringa è stata codificata. Il lato decodifica legge una stringa con una regola rigorosa o tollerante e le dice quale escape è rotto e dove, invece di restituire in silenzio un risultato sbagliato.
Tutta l'elaborazione avviene localmente nel suo browser — i suoi dati non vengono mai caricati su alcun server.

Non sa che cosa sia la codifica percentuale? La codifica percentuale spiegata in parole semplici — che cos'è, perché uno spazio diventa %20 o +, e che cosa significa quando un URL mostra %25.
Che cosa fa:
Come leggere gli escape:
encodeURIComponent
encodeURI
modulo (application/x-www-form-urlencoded)
Ctrl+Invio nell'input esegue la modalità attiva.
Scelga una modalità e prema il suo pulsante di azione. Codifica e ispeziona mostra le tre codifiche del suo testo più una riga per carattere; Decodifica legge gli escape a ritroso e conta i livelli. Il pulsante Autoverifica riesegue ogni esempio documentato, ogni carattere ASCII stampabile e le catene di livelli registrate nel file dati di questa pagina, e riporta qui il conteggio.
Dove i codificatori sono in disaccordo:solo i caratteri sotto vengono trattati diversamente dai tre metodi. Tutto il resto passa attraverso tutti e tre invariato, oppure viene escapato in modo identico da tutti e tre.
Ogni carattere, una riga ciascuno:i byte UTF-8 in cui si trasforma un carattere, la classe in cui lo colloca RFC 3986, e l'escape che ogni contesto scrive. Le quattro colonne di contesto sono gli insiemi di codifica percentuale WHATWG, cioè ciò che un browser usa quando costruisce un percorso, una query o un frammento; una riga ombreggiata è un carattere che i tre codificatori trattano in modo diverso.
L'insieme esatto che questa pagina escapa per ciascuna parte di un URL:stampato dal codificatore stesso, così questa tabella e il codice che riempie le colonne qui sopra non possono divergere. Ogni insieme escapa anche i caratteri di controllo C0 (da U+0000 a U+001F) e ogni punto di codice oltre U+007E, per questo il testo non ASCII viene sempre escapato in ogni contesto.
Questi insiemi derivano dagli insiemi di codifica percentuale WHATWG e ne differiscono in due punti voluti, entrambi pensati per rendere sicura la composizione a mano: un cancelletto viene escapato dentro una query, perché un cancelletto grezzo avvierebbe il frammento; e l'insieme dei componenti è fissato esattamente ai caratteri che encodeURIComponent escapa, apostrofo compreso, mentre l'insieme dei moduli è fissato a ciò che URLSearchParams escapa. Le due colonne della tabella sopra sono quindi il comportamento della piattaforma stessa, non una sua reimplementazione. Questo elenco spiega anche un risultato che molti trovano sorprendente: un segno di percentuale viene escapato da encodeURIComponent e da encodeURI, ma non è in nessuno degli insiemi di contesto WHATWG, quindi le colonne di percorso, query e frammento della tabella per carattere lo lasciano stare. Un segno di percentuale grezzo in un URL composto a mano resta un rischio, perché lo standard si aspetta che l'input sia stato prima validato: %41 dentro una query è una singola A per molti framework server, e la lettura rigorosa di questa pagina tratta un % isolato come escape rotto. Escaparlo a mano come %25 è l'abitudine più sicura, e la tabella per carattere mostra le due grafie affiancate.
Quante volte questa stringa è stata codificata:ogni giro di decodifica è un livello. Un valore codificato due volte inizia con%25dove una singola codifica avrebbe lasciato%20, e un valore codificato tre volte inizia con%2525.
I caratteri che un URL può trasportare così come sono:RFC 3986 divide l'intervallo ASCII in tre gruppi, e le regole di escape hanno senso solo rispetto a questa divisione.
I caratteri riservati non sono vietati, sono occupati: hanno un significato per il parser dell'URL. La regola che ne segue è semplice — se un carattere è riservato e lo intende come dato, lo escapi; se è non riservato, non serve mai.
I tre metodi, e che cosa lascia stare ciascuno:
Gli elenchi sono gli insiemi esatti che ciascun metodo lascia non escapati nell'intervallo ASCII; l'Autoverifica controlla ogni carattere ASCII stampabile rispetto a essi, così la tabella non può allontanarsi dai codificatori che descrive.
Quanto grande un input accetta questa pagina, e perché:la codifica percentuale è un unico passaggio sul testo, quindi, a differenza dei convertitori di base di questo sito, il costo cresce in linea retta e non come un quadrato: non esiste una lunghezza alla quale l'aritmetica stessa diventi il problema. I limiti qui sotto esistono perché la tabella per carattere viene disegnata nel documento, e una tabella con un milione di righe è ciò che davvero blocca un browser.
Che cos'è la codifica percentuale?

La codifica percentuale — detta anche codifica URL, e nei documenti più vecchi escaping — è la regola che permette a un URL di trasportare caratteri che non gli è consentito contenere. Un escape percentuale sono tre caratteri ASCII: un segno di percentuale e due cifre esadecimali, che rappresentano un byte. %20 è il byte 0x20, cioè uno spazio. %2F è il byte 0x2F, cioè una barra. Non c'è altro: l'escape indica un byte, non un carattere.

Poiché indica dei byte, il testo deve prima essere trasformato in byte, e la codifica dei byte è UTF-8. Da qui vengono gli escape lunghi: un carattere cinese sono tre byte UTF-8 e quindi tre escape, e un'emoji sono quattro byte e dodici caratteri ASCII.

La versione in 30 secondi: uno spazio è %20 come valore di URL ma + nei dati di modulo; # è %23; un segno di percentuale letterale è %25. I tre codificatori di questa pagina concordano sulle lettere, sulle cifre e su -_., e sono in disaccordo su una dozzina di segni di punteggiatura, la tilde compresa — ed è esattamente per questo che encodeURIComponent e encodeURI producono stringhe diverse per lo stesso testo, e perché usare quello sbagliato dei due può spezzare un valore in due parametri di query.
Perché un URL ne ha bisogno

Un URI è definito come una sequenza di caratteri ASCII (RFC 3986, sezione 2.1), e dentro quell'intervallo ASCII alcuni caratteri sono riservati: sono la punteggiatura che dà la struttura all'URL. Due punti e due barre separano lo schema, una chiocciola separa le informazioni utente dall'host, un punto interrogativo avvia la query, un cancelletto avvia il frammento, una e commerciale separa i parametri di query, e un segno di percentuale introduce un escape.

Questo crea un problema e una scappatoia. Il problema: un valore che contiene da sé uno di quei caratteri — una casella di ricerca che contiene «a&b», un nome di file con una barra, una password con due punti — altrimenti cambierebbe la struttura dell'URL in cui viaggia. La scappatoia: il segno di percentuale stesso, quindi il codificatore può semplicemente estendere il proprio alfabeto. % si scrive %25, e le due cifre esadecimali che seguono sono normali caratteri non riservati.

Esempi svolti

Questi sono gli output esatti dei tre codificatori che questa pagina esegue, su input scelti perché si comportano in modo diverso l'uno dall'altro. La pagina li ricalcola davanti a lei, e la sua Autoverifica riverifica ogni riga rispetto alle implementazioni della piattaforma stessa.

I tre metodi, e quando ciascuno è quello giusto

La versione breve che evita la maggior parte dei bug: tutto ciò che va in un singolo valore di parametro passa per encodeURIComponent (oppure per un serializzatore di moduli, che fa lo stesso lavoro con un insieme sicuro diverso), e un URL completo già composto passa per encodeURI se mai deve essere escapato. Scegliere l'uno o l'altro dei due sbagliati è in assoluto l'errore più comune di codifica percentuale nel codice web.

I caratteri che un URL può trasportare, per intero
Due segni più, due significati

Lo spazio è l'unico carattere la cui codifica dipende da dove va il testo, e il segno più è la ragione.

La trappola sta al centro di quella tabella. Nei dati di modulo un segno più significa uno spazio, quindi un valore che contiene davvero un segno più — un numero di telefono scritto come +44 20 7946 0958, un'espressione aritmetica, un indirizzo e-mail con un tag più — deve viaggiare come %2B. Se viaggia come segno più letterale, il destinatario lo legge come spazio, e +44 arriva come « 44».

Codificato due volte, e come riconoscerlo

Una doppia codifica avviene quando una stringa è già escapata e poi viene escapata di nuovo. Il segno di percentuale del primo escape è un carattere come un altro, quindi diventa %25, e il livello sottostante sopravvive come testo visibile.

Riconoscerlo è una questione di conteggio: decodifichi la stringa una volta e guardi il risultato. Se contiene ancora escape che sembrano voluti — un %XX valido, oppure un segno più da modulo dove ci si aspetterebbe uno spazio — c'è un altro livello. Se contiene %25 seguito da due cifre esadecimali, il livello successivo è certamente un escape.

Due note pratiche. Primo, decodificare due volte non è innocuo: è un modo noto per far vedere a un filtro di sicurezza e al codice che sta dietro stringhe diverse, quindi un filtro che lavora prima di una seconda decodifica filtra il testo sbagliato. Secondo, il conteggio dei livelli qui è una proprietà della stringa, non una promessa sull'intenzione: un valore legittimo può avere un solo livello di profondità e uno rotto due.

Gli errori per cui questa pagina è costruita
  • Un valore codificato con encodeURI. I separatori sopravvivono, quindi un valore che contiene & o = si spezza in più parametri dall'altra parte. Confronti le due colonne in cima a questa pagina sull'input a&b=c: la colonna dei componenti escapa entrambi i caratteri, la colonna di encodeURI li conserva.
  • Un segno più letterale inviato come dati di modulo. Arriva come spazio. Usi %2B.
  • Un valore escapato due volte «per sicurezza». Non è più sicuro. Il destinatario decodifica una volta e ottiene una stringa che contiene ancora escape percentuali, lo stesso errore di un set di caratteri sbagliato, un livello più in basso.
  • Decodificare ciò che non è mai stato codificato. Un segno di percentuale letterale nel testo — 100% sicuro — non è un escape. In lettura rigorosa è un escape rotto, nella posizione che questa pagina riporta; in lettura tollerante sopravvive, ed è per questo che tollerante è il valore predefinito qui e non il contrario.
  • Credere che l'escape debba essere in maiuscolo. La decodifica tratta %2f e %2F come lo stesso byte, e sono entrambi validi. Il maiuscolo è solo la grafia canonica raccomandata da RFC 3986, e quella che producono gli altri strumenti.
  • Credere che la codifica percentuale renda il testo sicuro. Lo rende univoco dentro un URL. Non ferma l'iniezione, non è cifratura e non è un sanitizzatore: l'escape deve essere l'ultimo passaggio prima della composizione, per il contesto in cui il testo finisce.
Risposte rapide

«Un segno più in un URL è uno spazio?»

Solo nei dati di modulo, e solo nella parte di query di un invio di modulo. In un percorso, in un frammento e nella query di un link semplice, un segno più è un segno più letterale. Per questo questa pagina ha un interruttore per esso invece di tirare a indovinare.

«Perché il mio link mostra %25?»

Perché qualcosa ha codificato un segno di percentuale. Oppure un segno di percentuale letterale è passato attraverso un codificatore — 100% sicuro diventa 100%25%20sicuro — oppure una stringa che già conteneva un escape è stata codificata una seconda volta. Decodifichi la stringa una volta e guardi che cosa torna indietro: se il risultato contiene ancora %XX, c'è un altro livello.

«Conta se una barra dentro un valore di query è %2F oppure /?»

Per il parser dell'URL, no: entrambi arrivano al server come barra, e l'insieme di query WHATWG lascia stare la barra. Per l'interoperabilità, a volte sì: molti framework server eseguono una propria decodifica su una stringa di query grezza, e alcuni di essi non concordano su caratteri che non sono mai stati escapati. Quando un valore viene composto a mano invece che da un serializzatore, escapare esplicitamente i caratteri riservati è l'abitudine più sicura.

«Perché la colonna dei moduli escapa una tilde che gli altri due lasciano stare?»

Perché l'insieme sicuro del codificatore dei moduli è più ristretto: solo lettere, cifre e -_.*. Il codificatore di moduli WHATWG escapa ~ ! ' ( ) anche se RFC 3986 elenca ~ come non riservato. Entrambe le grafie decodificano nello stesso valore; la differenza conta solo quando si confrontano stringhe byte per byte.

«Gli stessi caratteri possono comparire non escapati nella barra degli indirizzi di un browser?»

Sì, e questa è la barra degli indirizzi, non l'URL. I browser mostrano una forma leggibile di un URL e la codificano quando lo inviano, quindi 中文 in un link diventa %E4%B8%AD%E6%96%87 sulla rete. Se copia l'URL dalla barra degli indirizzi può ottenere l'una o l'altra forma a seconda del browser.

«Ogni escape qui sembra valido, ma il carattere che esce non è quello che mi aspettavo. Perché?»

Dietro «è stato decodificato male» si nascondono due guasti diversi, e la pagina li riporta in modo diverso. Il primo è un escape rotto: un segno di percentuale non seguito da due cifre esadecimali, come un 100% finale o un %zz. È un problema di sintassi, e la lettura rigorosa lo rifiuta dicendo dove si trova, mentre la lettura tollerante lo conserva come testo letterale e lo elenca. Il secondo è un escape il cui byte è perfettamente legale ma i cui byte non compongono un carattere UTF-8 valido, come %FF da solo o un carattere di tre byte troncato come %E4%B8. Non è un problema di sintassi, quindi entrambe le letture restituiscono il testo, sostituiscono i byte che non si possono decodificare con il carattere di sostituzione U+FFFD («�») e aggiungono un avviso che lo dice. Quell'avviso è la parte utile: un testo che arriva con caratteri di sostituzione di solito significa che il mittente ha usato un altro set di caratteri, oppure che la stringa è stata escapata una volta di troppo. La codifica percentuale indica byte, e questa pagina, come lo standard URL, legge sempre quei byte come UTF-8.

«Che cosa non fa questa pagina?»

Non divide un URL in schema, host, percorso e query — a questo serve il Analizzatore di URL. Non verifica se un link è sicuro da aprire, non contatta nulla, e non indovina il set di caratteri dei byte dietro un escape: li legge sempre come UTF-8, come richiede lo standard URL.

«Quale ripiego serve alla decodifica, e come so che è corretto?»

Prema Autoverifica. Confronta i codificatori della pagina con encodeURIComponent, encodeURI e URLSearchParams della piattaforma per ogni carattere ASCII stampabile, riesegue ogni esempio nelle tabelle qui sopra e riverifica le catene di livelli registrate, poi riporta qui il numero di controlli.

Standard e ambito

La codifica percentuale è specificata in RFC 3986, sezione 2.1, con la definizione dei caratteri riservati e non riservati nelle sezioni 2.2 e 2.3 e la regola sulle maiuscole nella sezione 6.2.2.1. Gli insiemi che un browser usa davvero quando costruisce un URL sono nello URL Standard di WHATWG, che li divide per contesto: un insieme per il frammento, uno per la query, uno per la query speciale, uno per il percorso, uno per le informazioni utente, e quello più ristretto usato per un singolo componente. L'invio dei moduli, incluso il segno più, è nell'HTML Standard sotto application/x-www-form-urlencoded. Le due funzioni encodeURI e encodeURIComponent fanno parte di ECMAScript stesso.

Questa pagina riempie le colonne di percorso, query e frammento con i propri insiemi per contesto, e stampa l'elenco esatto per ogni contesto accanto al risultato. Quegli insiemi derivano da quelli WHATWG e ne differiscono in due punti voluti: un cancelletto viene escapato dentro una query, perché un cancelletto grezzo avvierebbe il frammento, e l'insieme dei componenti è fissato esattamente ai caratteri che encodeURIComponent escapa. Dove una funzione della piattaforma è la definizione della risposta — encodeURIComponent, encodeURI e la serializzazione dei moduli — quella funzione viene chiamata direttamente, quindi quelle colonne sono il comportamento del browser stesso e non una sua reimplementazione.

Il testo della pagina è stato scritto per questo sito. Ogni valore nelle tabelle qui sopra è prodotto dal codice della pagina al caricamento ed è stato verificato rispetto alle implementazioni della piattaforma negli strumenti di test di questo sito; l'Autoverifica ripete il confronto nel suo browser.