Codificatore e decodificatore di entità HTML

Esegui l’escape del testo perché sia sicuro dentro HTML, o decodifica entità con nome e numeriche riportandole ai caratteri che rappresentano.

Testo
Codificato

L’output apparirà qui

Che cosa fa questo strumento

HTML non ha modo di distinguere un segno di minore che intendevi come testo da uno che apre un tag. Il linguaggio risolve la cosa con le entità: brevi sequenze di escape che rappresentano un carattere senza essere lette come markup. Questo strumento converte in entrambi i versi — dal testo normale alla sua forma con escape, e dall’HTML con escape ai caratteri che rappresenta.

Entrambi i versi girano interamente nel tuo browser. Qui conta più che nella maggior parte degli strumenti, perché il testo che si incolla in un decodificatore di entità è di solito una pagina raccolta da uno scraper, l’email di un cliente o una colonna di database che qualcuno sta cercando di interpretare.

I cinque caratteri che contano

Esistono migliaia di entità, ma per rendere sicuro il testo ne bastano pochissime. La codifica qui sostituisce esattamente cinque caratteri e lascia leggibile tutto il resto:

  • La e commerciale diventa & — questa per prima, altrimenti ogni altra sostituzione verrebbe riletta come entità.
  • Il segno di minore diventa < — il carattere che può aprire un tag.
  • Il segno di maggiore diventa > — meno pericoloso da solo, sottoposto a escape per simmetria e per sopravvivere ai parser distratti.
  • Le virgolette doppie diventano " — servono dentro i valori di attributo scritti con virgolette doppie.
  • L’apostrofo diventa ' — serve dentro i valori di attributo scritti con virgolette singole.

Quest’ultimo è scritto in forma numerica di proposito. Un nome esiste, ', ma viene da XML e HTML 4 non lo ha mai definito, così i parser vecchi lo lasciano passare come testo letterale e l’escape fallisce in silenzio. La forma numerica ha sempre funzionato ovunque, ed è per questo che le librerie di escape serie la emettono.

Con nome, decimali ed esadecimali

Qualsiasi carattere si può scrivere in tre modi, e la decodifica li accetta tutti. La lineetta lunga è un buon esempio:

named     —      la forma leggibile, ma solo per i caratteri che hanno un nome
decimal   —      il punto di codice in base 10
hex       —     lo stesso punto di codice in base 16, la notazione delle tabelle Unicode

In decodifica questo strumento riconosce i 255 nomi dell’insieme di HTML 4 — le lettere latine, l’alfabeto greco, le frecce, i simboli matematici e la punteggiatura tipografica, che insieme coprono praticamente tutto ciò che compare nel testo reale. HTML 5 ne definisce circa 2200 in più, quasi tutti simboli matematici poco comuni; portarsi dietro quella tabella aggiungerebbe decine di kilobyte a ogni caricamento di pagina in cambio di nomi che la maggior parte dei documenti non usa mai. Un nome fuori dall’insieme viene segnalato anziché scartato, quindi il costo è un avviso, mai una risposta sbagliata.

La codifica va nel verso opposto e resta volutamente semplice: nomi per i cinque caratteri di cui sopra, numeri decimali per tutto il resto. Nulla diventa … o ’ quando il carattere stesso è perfettamente valido in un documento UTF-8.

L’escape non è una regola ma quattro

Viene naturale considerare l’escape delle entità la risposta all’injection, e dentro il contenuto di un elemento lo è davvero. Ma HTML è in realtà quattro linguaggi annidati l’uno nell’altro, ciascuno con regole proprie. L’escape delle entità è corretto nei primi due di questi punti e inutile negli altri:

  • Contenuto di un elemento — testo tra i tag. L’escape delle entità è esattamente ciò che serve.
  • Valori di attributo tra virgolette. L’escape delle entità va bene, purché l’attributo sia davvero tra virgolette; senza, uno spazio nel valore chiude l’attributo e l’escape non ti serve a nulla.
  • Dentro un blocco script. Valgono le regole di escape delle stringhe JavaScript, non le entità — lì il browser non decodifica entità, così una virgoletta con escape arriva come i caratteri letterali ".
  • Attributi di URL come href e src. L’escape delle entità non ferma un URL javascript:, perché il pericolo sta nello schema e non in un carattere che un’entità sostituirebbe.

La regola pratica: esegui l’escape nel punto in cui il testo viene inserito in un documento, con le regole del contesto esatto in cui atterra — non una volta sola, presto, sperando che regga ovunque più avanti.

Perché a volte si vede &

È di gran lunga il motivo più comune per ricorrere a un decodificatore di entità. Il testo ha subito l’escape, e poi di nuovo — di solito una volta dal codice applicativo e una da un motore di template o da un framework che già esegue l’escape del proprio output:

original     Fish & Chips
escaped      Fish & Chips
escaped x2   Fish & Chips

La seconda riga compare nella pagina come l’originale. La terza compare come la seconda: il visitatore vede l’entità invece del carattere. La decodifica toglie esattamente uno strato, quindi una sola passata ti dice in quale caso ti trovi. Se l’output contiene ancora entità, gli strati erano più di uno. La soluzione quasi mai è togliere le entità in fase di visualizzazione: è trovare ed eliminare il passaggio di escape duplicato, perché lo stesso difetto sta corrompendo in silenzio anche i dati memorizzati.

Il non-ASCII e la relativa opzione

Una pagina servita in UTF-8 non ha bisogno di entità per lettere accentate, ebraico, arabo, cinese o emoji. Quei caratteri sono validi così come sono, e lasciarli leggibili mantiene leggibile il sorgente e più piccolo il file. È il comportamento predefinito qui.

L’opzione esiste perché alcune destinazioni pretendono ancora solo ASCII: vecchi modelli di posta, sistemi che rovinano i byte oltre il 127, o una colonna di database la cui codifica nessuno vuole cambiare. Attivandola ogni carattere di questo tipo viene scritto come riferimento decimale: é diventa é, א diventa א.

Gli emoji meritano una nota. Un emoji viene scritto come un unico riferimento per il suo intero punto di codice, ad esempio 🙂, mai come due metà. Se hai usato lo strumento di escape JSON, qui è l’opposto di quanto accade lì, dove il formato impone espressamente di dividere un emoji in due escape \u. Stesso carattere, due risposte corrette, perché i due formati definiscono l’escape su unità diverse.

Quando un riferimento non si può decodificare

Qui la decodifica non fallisce mai e non cancella mai nulla. Un riferimento che non si riesce a risolvere resta esattamente com’è stato trovato ed è elencato sopra l’output con la sua riga e la sua colonna. Vengono segnalate tre cose:

  • Un nome sconosciuto, come &nbps; — quasi sempre un errore di battitura per  . È l’avviso su cui vale la pena intervenire: significa che nel tuo contenuto manca uno spazio e nessuno se n’è accorto.
  • Un numero che non è un carattere, come � o un valore oltre l’intervallo Unicode. Non c’è nulla in cui decodificarlo, quindi resta testo.
  • Un numero tra 128 e 159. Vedi sotto.

Una e commerciale isolata non viene segnalata affatto. In HTML, R&D e Q&A sono testo ordinario, e trattare ogni e commerciale vagante come un errore seppellirebbe gli avvisi importanti sotto il rumore della prosa.

L’intervallo da 128 a 159 è una vera stranezza. Quei numeri indicano caratteri di controllo invisibili che nessuno intende mai scrivere; compaiono perché il documento è stato redatto in Windows-1252, dove gli stessi numeri sono virgolette curve, lineette e il simbolo dell’euro. I browser li leggono in sordina come Windows-1252 proprio per questo, e questo strumento fa lo stesso perché il risultato corrisponda a ciò che una pagina mostrerebbe davvero — ma lo dice negli avvisi, perché una sostituzione silenziosa che non hai chiesto è peggio di una rumorosa.

Domande frequenti

Basta l’escape di questi cinque caratteri per fermare l’XSS?
Nel contenuto di un elemento e nei valori di attributo tra virgolette sì: è esattamente a questo che serve. Non basta dentro un blocco script o style, in un attributo senza virgolette, né in un attributo di URL come href, ciascuno dei quali richiede regole proprie. L’escape è una proprietà del punto in cui il testo atterra, non del testo.
Perché l’apostrofo è codificato come ' e non come '?
Perché ' è un’entità XML che HTML 4 non ha mai definito. I parser precedenti a HTML 5 la lasciano come testo letterale, quindi un apostrofo che credevi protetto non lo è. La forma numerica funziona in ogni parser, ed è per questo la scelta sicura.
La mia pagina mostra & invece della e commerciale. Che cosa è successo?
Il testo ha subito l’escape due volte. Decodificalo qui: una passata trasforma & in &, a conferma dello strato di troppo. Correggi la catena di elaborazione anziché il testo — di solito il tuo codice esegue l’escape di un output che il motore di template stava già proteggendo.
Devo codificare ebraico, arabo o emoji?
Non per una pagina moderna servita in UTF-8: sono validi così come sono. Attiva l’opzione non-ASCII solo quando qualcosa più a valle pretende solo ASCII, come un vecchio modello di posta. Ogni emoji viene allora scritto come un unico riferimento numerico.
Perché lo strumento avvisa su “ se lo ha decodificato correttamente?
Perché 147 indica un carattere di controllo invisibile, non la virgoletta che vedi. I documenti che lo usano sono stati scritti in Windows-1252, dove 147 è una virgoletta curva: i browser lo leggono così, e anche questo strumento. L’avviso ti dice che è avvenuta una sostituzione, così puoi correggere la codifica di origine se sta a te farlo.
Il mio testo viene inviato da qualche parte?
No. Codifica e decodifica girano interamente nel tuo browser; nulla di ciò che incolli lascia il tuo dispositivo.