Inspektör för procentkodning

Procentkodning är inte en regel utan flera, och de är oense om ett dussintal tecken. Den här sidan kör samma text genom encodeURIComponent, encodeURI och serialiseringen application/x-www-form-urlencoded sida vid sida, markerar tecknen där de är oense, skriver ut en rad per tecken med dess UTF-8-byte och RFC 3986-klass, och anger hur många gånger en sträng har kodats. Avkodningssidan läser en sträng med en strikt eller tolerant regel och anger vilken escape som är trasig och var, i stället för att tyst lämna tillbaka ett felaktigt resultat.
All bearbetning sker lokalt i webbläsaren — data laddas aldrig upp till någon server.

Osäker på vad procentkodning är? Procentkodning förklarad på enkel svenska — vad det är, varför ett blanksteg blir %20 eller +, och vad det betyder när en URL visar %25.
Vad detta gör:
Så läser man escapes:
encodeURIComponent
encodeURI
form (application/x-www-form-urlencoded)
Ctrl+Enter i indatan kör det aktiva läget.
Välj ett läge och klicka på dess åtgärdsknapp. Koda & inspektera visar de tre kodningarna av texten plus en rad per tecken; Avkoda läser escapes tillbaka och räknar lagren. Självtestknappen kör om varje dokumenterat exempel, varje utskrivbart ASCII-tecken och lagerkedjorna som sparats i sidans datafil, och rapporterar antalet här.
Där kodarna är oense:endast tecknen nedan behandlas olika av de tre metoderna. Allt annat går genom alla tre oförändrat, eller escapas identiskt av alla tre.
Varje tecken, en rad vardera:UTF-8-byten ett tecken blir, klassen RFC 3986 placerar det i, och escapen varje sammanhang skriver. De fyra sammanhangskolumnerna är WHATWG:s procentkodningsuppsättningar, som är vad en webbläsare använder när den bygger en sökväg, en fråga eller ett fragment; en skuggad rad är ett tecken som de tre kodarna behandlar olika.
Den exakta uppsättning som sidan escapar för varje del av en URL:skrivs ut från kodaren själv, så den här tabellen och koden som fyller kolumnerna ovan kan inte glida isär. Varje uppsättning escapar också C0-kontrolltecknen (U+0000 till U+001F) och varje kodpunkt över U+007E, vilket är varför icke-ASCII-text alltid escapas i varje sammanhang.
De uppsättningarna härleds från WHATWG:s procentkodningsuppsättningar och skiljer sig från dem på två avsiktliga ställen, som båda gör handihopsättning säker: ett nummertecken escapas inuti en fråga, eftersom ett rått sådant skulle starta fragmentet; och komponentuppsättningen är låst till exakt de tecken som encodeURIComponent escapar, apostrof inräknad, medan formuppsättningen är låst till det som URLSearchParams escapar. De två kolumnerna i tabellen ovan är därför plattformens eget beteende snarare än en ombyggnad av det. Listan förklarar också ett resultat som folk tycker är förvånande: ett procenttecken escapas av encodeURIComponent och av encodeURI, men det finns i ingen av WHATWG:s sammanhangsuppsättningar, så sökvägs-, frågo- och fragmentkolumnerna i tabellen per tecken lämnar det orört. Ett rått procenttecken i en URL som satts ihop för hand är ändå en risk, eftersom standarden förväntar sig att indatat har validerats först: %41 inuti en fråga är ett enda A för många serverramverk, och sidans egna strikta läsning behandlar ett ensamt % som en trasig escape. Att escapa det själv som %25 är den säkrare vanan, och tabellen per tecken visar båda stavningarna bredvid varandra.
Hur många gånger den här strängen har kodats:varje omgång avkodning är ett lager. Ett värde som har kodats två gånger börjar med%25där en enda kodning skulle lämna%20, och ett värde som har kodats tre gånger börjar med%2525.
Tecknen som en URL får bära som de är:RFC 3986 delar upp ASCII-området i tre grupper, och escapreglerna blir bara begripliga mot den uppdelningen.
De reserverade tecknen är inte förbjudna, de är upptagna: de betyder något för URL-tolkaren. Regeln som följer av listan är kort — om ett tecken är reserverat och man menar det som data, escapa det; om det är oreserverat behöver man aldrig göra det.
De tre metoderna, och vad var och en lämnar orört:
Listorna är de exakta uppsättningar som varje metod lämnar oescapade i ASCII-området; självtestet kontrollerar varje utskrivbart ASCII-tecken mot dem, så tabellen kan inte glida ifrån kodarna den beskriver.
Hur stor indata sidan accepterar, och varför:procentkodning är en enda genomgång av texten, så till skillnad från radixomvandlarna på den här webbplatsen växer kostnaden i rät linje i stället för kvadratiskt: det finns ingen längd där själva aritmetiken blir problemet. Gränserna nedan finns eftersom tabellen per tecken ritas i dokumentet, och en tabell med en miljon rader är precis vad som faktiskt får en webbläsare att stanna.
Vad är procentkodning?

Procentkodning — även kallad URL-kodning, och i äldre dokument escapning — är regeln som gör att en URL kan bära tecken den inte får innehålla. En procentescape är tre ASCII-tecken: ett procenttecken och två hexadecimala siffror, som står för en byte. %20 är byten 0x20, vilket är ett blanksteg. %2F är byten 0x2F, vilket är ett snedstreck. Ingenting annat är inblandat: escapen anger en byte, inte ett tecken.

Eftersom escapen anger byte måste texten först göras om till byte, och byteinkodningen är UTF-8. Därifrån kommer de långa escapesekvenserna: ett kinesiskt tecken är tre UTF-8-byte och därmed tre escapes, och en emoji är fyra byte och tolv ASCII-tecken.

Versionen på 30 sekunder: ett blanksteg är %20 som URL-värde men + i formdata; # är %23; ett bokstavligt procenttecken är %25. De tre kodarna på den här sidan är överens om bokstäverna, siffrorna och -_., och är oense om ett dussintal skiljetecken, tilde bland dem — vilket är precis varför encodeURIComponent och encodeURI ger olika strängar för samma text, och varför fel av de två kan dela upp ett värde i två frågeparametrar.
Varför en URL behöver det alls

En URI definieras som en följd av ASCII-tecken (RFC 3986 avsnitt 2.1), och inom det ASCII-området är vissa tecken reserverade: de är skiljetecknen som ger URL:en dess struktur. Ett kolon och två snedstreck avgränsar schemat, ett at-tecken avgränsar användaruppgifterna från värden, ett frågetecken inleder frågan, ett nummertecken inleder fragmentet, ett et-tecken avgränsar frågeparametrar, och ett procenttecken inleder en escape.

Det skapar ett problem och ett kryphål. Problemet: ett värde som självt innehåller ett av de tecknen — en sökruta med "a&b", ett filnamn med ett snedstreck, ett lösenord med ett kolon — skulle annars ändra URL:ens struktur på sin väg. Kryphålet: procenttecknet självt, så kodaren kan helt enkelt utöka sitt eget alfabet. % skrivs som %25, och de två hexadecimala siffrorna efter det är vanliga oreserverade tecken.

Arbetade exempel

Det här är de exakta utdata från de tre kodare som sidan kör, med indata valda för att de beter sig olika mot varandra. Sidan räknar om dem framför ögonen på användaren, och självtestet kontrollerar varje rad mot plattformens egna implementationer.

De tre metoderna, och när var och en är rätt

Den korta versionen som förhindrar de flesta buggarna: allt som går in i ett enskilt parametervärde går genom encodeURIComponent (eller genom en formserialiserare, som gör samma jobb med en annan säker uppsättning), och en komplett URL som redan är ihopsatt går genom encodeURI om den alls behöver escapas. Att ta fel av de två är det enskilt vanligaste procentkodningsmisstaget i webbkod.

Vilka tecken en URL får bära, i sin helhet
Två plustecken, två betydelser

Ett blanksteg är det enda tecken vars kodning beror på vart texten ska, och plustecknet är anledningen.

Fällan ligger mitt i den tabellen. I formdata betyder ett plustecken ett blanksteg, så ett värde som verkligen innehåller ett plustecken — ett telefonnummer skrivet som +44 20 7946 0958, ett aritmetiskt uttryck, en e-postadress med en plus-tagg — måste färdas som %2B. Om det färdas som ett bokstavligt plustecken läser mottagaren tillbaka det som ett blanksteg, och +44 kommer fram som " 44".

Kodad två gånger, och hur man känner igen det

En dubbel kodning sker när en sträng redan är escapad och sedan escapas igen. Procenttecknet i den första escapen är ett tecken som vilket annat som helst, så det blir %25, och lagret under överlever som synlig text.

Att känna igen det är en fråga om att räkna: avkoda strängen en gång och titta på resultatet. Om det fortfarande innehåller escapes som ser avsiktliga ut — en giltig %XX eller ett formplustecken där ett blanksteg förväntas — finns ett lager till. Om den innehåller %25 följt av två hexadecimala siffror är nästa lager säkert en escape.

Två praktiska anmärkningar. För det första är dubbel avkodning inte ofarlig: det är ett väl känt sätt att få ett säkerhetsfilter och koden bakom det att se olika strängar, så ett filter som körs före en andra avkodning filtrerar fel text. För det andra är lagerantalet här en egenskap hos strängen, inte ett löfte om avsikt: ett legitimt värde kan vara ett lager djupt, och ett trasigt kan vara två.

Misstagen som den här sidan är byggd för att fånga
  • Ett värde kodat med encodeURI. Separatorerna överlever, så ett värde som innehåller & eller = delas i flera parametrar på andra sidan. Jämför de två kolumnerna överst på sidan för indata a&b=c: komponentkolumnen escapar båda tecknen, encodeURI-kolumnen behåller dem.
  • Ett bokstavligt plustecken skickat som formdata. Det kommer fram som ett blanksteg. Använd %2B.
  • Ett värde escapat två gånger "för säkerhets skull". Det är inte säkrare. Mottagaren avkodar en gång och får en sträng som fortfarande innehåller procentescapes, vilket är samma fel som ett fel teckenformat, ett lager längre ned.
  • Att avkoda något som aldrig kodats. Ett bokstavligt procenttecken i text — 100% sure — är inte en escape. I en strikt läsning är den en trasig sådan, vid den position sidan anger; i en tolerant läsning överlever den, vilket är varför tolerant är standard här och inte tvärtom.
  • Att anta att escapen måste vara versal. Avkodning behandlar %2f och %2F som samma byte, och båda är giltiga. Versaler är bara den kanoniska stavningen som RFC 3986 rekommenderar, och den som andra verktyg producerar.
  • Att anta att procentkodning gör text säker. Den gör text entydig inuti en URL. Den stoppar inte injektion, den är inte kryptering och den är inte en sanerare: escapningen måste vara sista steget före ihopsättning, för det sammanhang som texten hamnar i.
Snabba svar

“Är ett plustecken ett blanksteg i en URL?”

Bara i formdata, och bara i frågedelen av en formulärsändning. I en sökväg, ett fragment och i frågan i en vanlig länk är ett plustecken ett bokstavligt plustecken. Därför har den här sidan en omkopplare för det i stället för att gissa.

“Varför visar min länk %25?”

För att något kodade ett procenttecken. Antingen gick ett bokstavligt procenttecken genom en kodare — 100% sure blir 100%25%20sure — eller så kodades en sträng som redan innehöll en escape en gång till. Avkoda strängen en gång och titta på vad som kommer tillbaka: om resultatet fortfarande innehåller %XX finns det ett lager till.

“Spelar det någon roll om ett snedstreck i ett frågevärde är %2F eller /?”

För URL-tolkaren, nej: båda når servern som ett snedstreck, och WHATWG:s frågeuppsättning lämnar ett snedstreck orört. För interoperabilitet, ibland ja: många serverramverk kör sin egen avkodning över en rå frågesträng, och några av dem är oense om tecken som aldrig escapades. När ett värde sätts ihop för hand snarare än av en serialiserare är det en säkrare vana att escapa de reserverade tecknen uttryckligen.

“Varför escapar formkolumnen ett tilde som de två andra lämnar orört?”

Därför att formkodarens säkra uppsättning är smalare: endast bokstäver, siffror och -_.*. WHATWG:s formkodare escapar ~ ! ' ( ) även om RFC 3986 listar ~ som oreserverad. Båda stavningarna avkodas till samma värde; skillnaden spelar bara roll när strängar jämförs byte för byte.

“Kan samma tecken förekomma oescapade i en webbläsares adressfält?”

Ja, och det är adressfältet, inte URL:en. Webbläsare visar en läsbar form av en URL och kodar den när den skickas, så 中文 i en länk blir %E4%B8%AD%E6%96%87 på tråden. Kopierar man URL:en från adressfältet kan man få endera formen beroende på webbläsare.

“Varje escape här ser giltig ut, men tecknet som kommer ut är inte det jag väntade mig. Varför?”

Två olika fel döljer sig bakom "det avkodades fel", och sidan rapporterar dem olika. Det första är en trasig escape: ett procenttecken som inte följs av två hexadecimala siffror, till exempel en avslutande 100% eller en %zz. Det är ett syntaxproblem, och den strikta läsningen vägrar det och anger var det sitter, medan den toleranta läsningen behåller det som bokstavlig text och listar det. Det andra är en escape vars byte är helt lagligt men vars byte inte stavar ett giltigt UTF-8-tecken, som %FF ensam eller ett trebytestecken avklippt som %E4%B8. Det är inget syntaxproblem, så båda läsningarna returnerar texten, ersätter de byte som inte kan avkodas med ersättningstecknet U+FFFD ("�") och listar en varning som säger det. Den varningen är den användbara delen: text som kommer fram som ersättningstecken betyder vanligtvis att avsändaren använde ett annat teckenformat, eller att strängen escapades en gång för mycket. Procentkodning anger byte, och den här sidan läser, precis som URL-standarden, alltid dessa byte som UTF-8.

“Vad gör inte den här sidan?”

Den delar inte upp en URL i schema, värd, sökväg och fråga — det är vad URL-tolkaren är till för. Den kontrollerar inte om en länk är säker att öppna, den kontaktar inget och den gissar inte teckenuppsättningen för byten bakom en escape: den läser dem alltid som UTF-8, vilket URL-standarden kräver.

“Vilken reserv behöver avkodning, och hur vet jag att den är rätt?”

Tryck på Självtest. Det jämför sidans egna kodare mot plattformens encodeURIComponent, encodeURI och URLSearchParams för varje utskrivbart ASCII-tecken, kör om varje exempel i tabellerna ovan och kontrollerar de sparade lagerkedjorna på nytt, och rapporterar sedan antalet kontroller här.

Standarder och omfattning

Procentkodning specificeras i RFC 3986 avsnitt 2.1, med definitionen av reserverade och oreserverade tecken i avsnitten 2.2 och 2.3 och versalregeln i avsnitt 6.2.2.1. De uppsättningar som en webbläsare faktiskt använder när den bygger en URL finns i WHATWG URL Standard, som delar upp dem efter sammanhang: en för fragment, en för fråga, en för speciell fråga, en för sökväg, en för användaruppgifter och den smalare som används för en enskild komponent. Formulärsändning, inklusive plustecknet, finns i HTML-standarden under application/x-www-form-urlencoded. De två funktionerna encodeURI och encodeURIComponent är en del av ECMAScript självt.

Sidan fyller sökvägs-, frågo- och fragmentkolumnerna från sina egna uppsättningar per sammanhang och skriver ut den exakta listan för varje sammanhang bredvid resultatet. De uppsättningarna härleds från WHATWG:s och skiljer sig från dem på två avsiktliga ställen: ett nummertecken escapas inne i en fråga, eftersom ett rått nummertecken skulle starta fragmentet, och komponentuppsättningen är låst till exakt de tecken som encodeURIComponent escapar. Där en plattformsfunktion är definitionen på svaret — encodeURIComponent, encodeURI och formserialiseringen — anropas den funktionen direkt, så de kolumnerna är webbläsarens eget beteende snarare än en ombyggnad av det.

Sidans text skrevs för den här webbplatsen. Varje värde i tabellerna ovan produceras av sidans egen kod vid inläsning och verifierades mot plattformens implementationer i den här webbplatsens testverktyg; självtestet upprepar den jämförelsen i webbläsaren.