Inspektør for procentkodning

Procent-kodning er ikke én regel, men flere, og de er uenige om et dusin tegn. Siden kører den samme tekst gennem encodeURIComponent, encodeURI og serialiseringen application/x-www-form-urlencoded side om side, markerer tegnene, hvor de er uenige, udskriver en række pr. tegn med dets UTF-8-bytes og dets RFC 3986-klasse, og siger, hvor mange gange en streng er blevet kodet. Dekodningssiden læser en streng med en streng eller en tilgivende regel og fortæller, hvilken escape der er ødelagt, og hvor, i stedet for at returnere et stille forkert resultat.
Al behandling sker lokalt i browseren — data uploades aldrig til nogen server.

Usikker på, hvad procent-kodning er? Procent-kodning forklaret på klart dansk — hvad det er, hvorfor et mellemrum bliver til %20 eller +, og hvad det betyder, når en URL viser %25.
Hvad det gør:
Sådan læses escapes:
encodeURIComponent
encodeURI
formular (application/x-www-form-urlencoded)
Ctrl+Enter i input kører den aktive tilstand.
Vælg en tilstand, og klik på dens handlingsknap. Kod & undersøg viser de tre kodninger af teksten plus en række pr. tegn; Dekod læser escapes tilbage og tæller lagene. Selvtest-knappen kører alle dokumenterede eksempler, hvert udskrivbart ASCII-tegn og de lagkæder, der er registreret i sidens datafil, igen og rapporterer antallet her.
Hvor koderne er uenige:kun tegnene nedenfor behandles forskelligt af de tre metoder. Alt andet går uændret gennem alle tre eller escapes identisk af alle tre.
Hvert tegn, én række pr. tegn:de UTF-8-bytes, et tegn bliver til, den klasse RFC 3986 placerer det i, og den escape hver kontekst skriver. De fire kontekstkolonner er WHATWG-procent-kodningssættene, som en browser bruger, når den bygger en sti, en forespørgsel eller et fragment; en skygget række er et tegn, som de tre kodere behandler forskelligt.
Det nøjagtige sæt, siden escaper for hver del af en URL:udskrevet fra selve koderen, så denne tabel og koden, der udfylder kolonnerne ovenfor, ikke kan glide fra hinanden. Hvert sæt escaper også C0-styretegnene (U+0000 til U+001F) og hvert kodepunkt over U+007E, og det er grunden til, at ikke-ASCII-tekst altid escapes i enhver kontekst.
Disse sæt er afledt af WHATWG-procent-kodningssættene og adskiller sig fra dem på to bevidste punkter, som begge gør håndsamling sikker: et nummertegn escapes inde i en forespørgsel, fordi et råt et ville starte fragmentet; og komponentsættet er låst til præcis de tegn, encodeURIComponent escaper, apostrof og alt, mens formularsættet er låst til det, URLSearchParams escaper. De to kolonner i tabellen ovenfor er derfor platformens egen adfærd og ikke en genimplementering af den. Listen forklarer også et resultat, folk finder overraskende: et procenttegn escapes af både encodeURIComponent og encodeURI, men det findes i ingen af WHATWG-kontekstsættene, så sti-, forespørgsels- og fragmentkolonnerne i tabellen pr. tegn lader det være. Et råt procenttegn i en URL, der er samlet i hånden, er stadig en risiko, fordi standarden forventer, at inputtet er valideret først: %41 inde i en forespørgsel er et enkelt A for mange serverrammeworks, og denne sides egen streng læsning behandler et alene % som en ødelagt escape. At escape det selv som %25 er den sikrere vane, og tabellen pr. tegn viser begge stavemåder side om side.
Hvor mange gange denne streng er blevet kodet:hver runde dekodning er ét lag. En værdi, der er kodet to gange, begynder med%25hvor en enkelt kodning ville efterlade%20, og en værdi, der er kodet tre gange, begynder med%2525.
De tegn, en URL må bære som de er:RFC 3986 opdeler ASCII-området i tre grupper, og escape-reglerne giver kun mening i forhold til den opdeling.
De reserverede tegn er ikke forbudt, de er optaget: de betyder noget for URL-parseren. Reglen, der følger af listen, er kort — hvis et tegn er reserveret, og man mener det som data, så escape det; hvis det er ureserveret, behøver man aldrig.
De tre metoder, og hvad hver lader være:
Listerne er de nøjagtige sæt, hver metode lader være uescapet i ASCII-området; selvtesten kontrollerer hvert udskrivbart ASCII-tegn mod dem, så tabellen ikke kan glide væk fra de kodere, den beskriver.
Hvor stort et input siden accepterer, og hvorfor:Procent-kodning er ét enkelt gennemløb af teksten, så omkostningen, i modsætning til radixkonverterne på dette websted, vokser lineært og ikke kvadratisk: der er ingen længde, hvor selve regningen bliver problemet. Grænserne nedenfor findes, fordi tabellen pr. tegn tegnes i dokumentet, og en tabel med en million rækker er netop det, der standser en browser.
Hvad er procent-kodning?

Procent-kodning — også kaldet URL-kodning, og i ældre dokumenter escaping — er reglen, der gør det muligt for en URL at bære tegn, den ikke må indeholde. En procent-escape er tre ASCII-tegn: et procenttegn og to hexadecimale cifre, der står for én byte. %20 er byten 0x20, som er et mellemrum. %2F er byten 0x2F, som er en skråstreg. Der er ikke andet involveret: escapen navngiver en byte, ikke et tegn.

Fordi den navngiver bytes, skal teksten først omdannes til bytes, og byte-kodningen er UTF-8. Det er her de lange escapes kommer fra: ét kinesisk tegn er tre UTF-8-bytes og derfor tre escapes, og én emoji er fire bytes og tolv ASCII-tegn.

Version på 30 sekunder: et mellemrum er %20 som URL-værdi, men + i formulardata; # er %23; et bogstaveligt procenttegn er %25. De tre kodere på siden er enige om bogstaverne, cifrene og -_., og de er uenige om et dusin tegnsætningstegn, tilden blandt dem — og det er netop derfor, encodeURIComponent og encodeURI giver forskellige strenge for den samme tekst, og hvorfor den forkerte af de to kan dele en værdi i to forespørgselsparametre.
Hvorfor en URL overhovedet har brug for det

En URI er defineret som en række ASCII-tegn (RFC 3986 afsnit 2.1), og inden for det ASCII-område er nogle tegn reserverede: det er tegnsætningen, der giver URL'en dens struktur. Et kolon og to skråstreger adskiller skemaet, et @-tegn adskiller brugeroplysningerne fra værten, et spørgsmålstegn starter forespørgslen, et nummertegn starter fragmentet, et et-tegn adskiller forespørgselsparametre, og et procenttegn indleder en escape.

Det skaber ét problem og ét smuthul. Problemet: en værdi, der selv indeholder et af disse tegn — en søgeboks med „a&b“, et filnavn med en skråstreg, en adgangskode med et kolon — ville ellers ændre strukturen i den URL, den rejser i. Smuthullet: procenttegnet selv, så koderen simpelthen kan udvide sit eget alfabet. % skrives som %25, og de to hexadecimale cifre efter det er almindelige ureserverede tegn.

Gennemregnede eksempler

Dette er de nøjagtige output fra de tre kodere, siden kører, på input, der er valgt, fordi de opfører sig forskelligt fra hinanden. Siden genberegner dem live, og dens selvtest kontrollerer hver række mod platformens egne implementeringer.

De tre metoder, og hvornår hver er den rigtige

Den korte version, der sparer de fleste fejl: alt, der går ind i én parameterværdi, går gennem encodeURIComponent (eller gennem en formularserialiserer, der gør det samme job med et andet sikkert sæt), og en komplet URL, der allerede er samlet, går gennem encodeURI, hvis den overhovedet skal escapes. At tage den forkerte af de to er den enkeltstående hyppigste procent-kodningsfejl i webkode.

Tegnene, en URL må bære, i fuld liste
To plustegn, to betydninger

Et mellemrum er det ene tegn, hvis kodning afhænger af, hvor teksten skal hen, og plustegnet er grunden.

Fælden ligger midt i den tabel. I formulardata betyder et plustegn et mellemrum, så en værdi, der virkelig indeholder et plustegn — et telefonnummer skrevet som +44 20 7946 0958, et regneudtryk, en e-mailadresse med et plus-tag — skal sendes som %2B. Hvis den sendes som et bogstaveligt plustegn, læser modtageren den som et mellemrum, og +44 ankommer som " 44".

Kodet to gange, og hvordan man genkender det

Dobbeltkodning sker, når en streng allerede er escapet og derefter escapes igen. Procenttegnet i den første escape er et tegn som alle andre, så det bliver til %25, og laget under det overlever som synlig tekst.

At genkende det handler om at tælle: dekod strengen én gang, og se på resultatet. Hvis det stadig indeholder escapes, der ser tilsigtede ud — en gyldig %XX eller et formular-plustegn, hvor et mellemrum forventes — er der endnu et lag. Hvis der står %25 efterfulgt af to hexadecimale cifre, er næste lag helt sikkert en escape.

To praktiske noter. For det første er dobbeltdekodning ikke ufarlig: det er en velkendt måde at få et sikkerhedsfilter og koden bag det til at se forskellige strenge, så et filter, der kører før en anden dekodning, filtrerer den forkerte tekst. For det andet er lagtællingen her en egenskab ved strengen, ikke et løfte om hensigten: en legitim værdi kan være ét lag dyb, og en ødelagt kan være to.

De fejl, siden er bygget til at fange
  • En værdi kodet med encodeURI. Separatorerne overlever, så en værdi, der indeholder & eller =, deles i flere parametre på den anden side. Sammenlign de to kolonner øverst på siden for inputtet a&b=c: komponentkolonnen escaper begge tegn, encodeURI-kolonnen beholder dem.
  • Et bogstaveligt plustegn sendt som formulardata. Det ankommer som et mellemrum. Brug %2B.
  • En værdi escapet to gange „for en sikkerheds skyld“. Det er ikke sikrere. Modtageren dekoder én gang og får en streng, der stadig indeholder procent-escapes, hvilket er den samme fejl som et forkert tegnsæt, blot et lag dybere.
  • At dekode noget, der aldrig blev kodet. Et bogstaveligt procenttegn i tekst — 100% sure — er ikke en escape. I en streng læsning er det en ødelagt en, på den position siden rapporterer; i en tilgivende læsning overlever det, og det er grunden til, at tilgivende er standard her og ikke omvendt.
  • At antage, at escapen skal være med store bogstaver. Dekodning behandler %2f og %2F som den samme byte, og begge er gyldige. Store bogstaver er kun den kanoniske stavemåde, RFC 3986 anbefaler, og den, andre værktøjer producerer.
  • At antage, at procent-kodning gør tekst sikker. Den gør tekst entydig inde i en URL. Den stopper ikke injektion, den er ikke kryptering, og den er ikke en sanitizer: escapingen skal være det sidste trin før samling, for den kontekst, teksten skal ind i.
Hurtige svar

“Er et plustegn et mellemrum i en URL?”

Kun i formulardata, og kun i forespørgselsdelen af en formularindsendelse. I en sti, i et fragment og i forespørgslen i et almindeligt link er et plustegn et bogstaveligt plustegn. Derfor har siden en kontakt til det i stedet for at gætte.

“Hvorfor viser mit link %25?”

Fordi noget kodede et procenttegn. Enten er et bogstaveligt procenttegn gået gennem en koder — 100% sure bliver til 100%25%20sure — eller en streng, der allerede indeholdt en escape, blev kodet en gang til. Dekod strengen én gang, og se, hvad der kommer tilbage: hvis resultatet stadig indeholder %XX, er der endnu et lag.

“Betyder det noget, om en skråstreg i en forespørgselsværdi er %2F eller /?”

For URL-parseren nej: begge når serveren som en skråstreg, og WHATWG-forespørgselssættet lader en skråstreg være. For interoperabilitet nogle gange ja: mange serverrammeworks kører deres egen dekodning over en rå forespørgselsstreng, og nogle af dem er uenige om tegn, der aldrig blev escapet. Når en værdi samles i hånden i stedet for af en serialiserer, er det den sikrere vane at escape de reserverede tegn eksplicit.

“Hvorfor escaper formularkolonnen en tilde, som de to andre lader være?”

Fordi formularkoderens sikre sæt er smallere: kun bogstaver, cifre og -_.*. WHATWG-formularkoderen escaper ~ ! ' ( ), selv om RFC 3986 lister ~ som ureserveret. Begge stavemåder dekodes til samme værdi; forskellen betyder kun noget, når strenge sammenlignes byte for byte.

“Kan de samme tegn optræde uescapet i en browsers adresselinje?”

Ja, og det er adresselinjen, ikke URL'en. Browsere viser en læsbar form af en URL og koder den, når de sender den, så 中文 i et link bliver til %E4%B8%AD%E6%96%87 på ledningen. Når en URL kopieres fra adresselinjen, kan man få enten form, afhængigt af browseren.

“Hver escape her ser gyldig ud, men tegnet, der kommer ud, er ikke det, jeg forventede. Hvorfor?”

Bag „den dekodedes forkert“ skjuler sig to forskellige fejl, og siden rapporterer dem forskelligt. Den første er en ødelagt escape: et procenttegn, der ikke efterfølges af to hexadecimale cifre, som et efterstillet 100% eller en %zz. Det er et syntaksproblem, og den strenge læsning afviser det og siger, hvor det står, mens den tilgivende læsning beholder det som almindelig tekst og lister det. Den anden er en escape, hvis byte er fuldt lovlig, men hvis bytes ikke staver et gyldigt UTF-8-tegn, som %FF alene eller et trebytes-tegn afkortet som %E4%B8. Det er ikke et syntaksproblem, så begge læsninger returnerer teksten, erstatter de bytes, der ikke kan dekodes, med erstatningstegnet U+FFFD ("�"), og lister en advarsel, der siger det. Den advarsel er den nyttige del: tekst, der ankommer som erstatningstegn, betyder typisk, at afsenderen brugte et andet tegnsæt, eller at strengen blev escapet én gang for meget. Procent-kodning navngiver bytes, og denne side læser, ligesom URL-standarden, altid disse bytes som UTF-8.

“Hvad gør denne side ikke?”

Den deler ikke en URL op i skema, vært, sti og forespørgsel — det er det, URL-parseren er til. Den kontrollerer ikke, om et link er sikkert at åbne, den kontakter ikke noget, og den gætter ikke tegnsættet for bytes bag en escape: den læser dem altid som UTF-8, hvilket URL-standarden kræver.

“Hvilken reserveløsning har dekodning brug for, og hvordan ved jeg, at den er rigtig?”

Tryk på Selvtest. Den sammenligner sidens egne kodere med platformens encodeURIComponent, encodeURI og URLSearchParams for hvert udskrivbart ASCII-tegn, kører alle eksempler i tabellerne ovenfor igen og kontrollerer de registrerede lagkæder, og rapporterer derefter antallet af kontroller her.

Standarder og omfang

Procent-kodning er specificeret i RFC 3986 afsnit 2.1, med definitionen af reserverede og ureserverede tegn i afsnit 2.2 og 2.3 og stavereglen i afsnit 6.2.2.1. De sæt, en browser faktisk bruger, når den bygger en URL, står i WHATWG URL-standarden, som opdeler dem efter kontekst: ét sæt for et fragment, ét for en forespørgsel, ét for en speciel forespørgsel, ét for en sti, ét for brugeroplysninger og det smallere sæt, der bruges til en enkelt komponent. Formularindsendelse, inklusive plustegnet, står i HTML-standarden under application/x-www-form-urlencoded. De to funktioner encodeURI og encodeURIComponent er en del af selve ECMAScript.

Denne side udfylder sti-, forespørgsels- og fragmentkolonnerne fra sine egne sæt pr. kontekst og udskriver den nøjagtige liste for hver kontekst ved siden af resultatet. Disse sæt er afledt af WHATWG-sættene og adskiller sig fra dem på to bevidste punkter: et nummertegn escapes inde i en forespørgsel, fordi et råt nummertegn ville starte fragmentet, og komponentsættet er låst til præcis de tegn, encodeURIComponent escaper. Hvor en platformsfunktion er definitionen af svaret — encodeURIComponent, encodeURI og formularserialiseringen — kaldes den funktion direkte, så disse kolonner er browserens egen adfærd og ikke en genimplementering af den.

Sidens tekst er skrevet til dette websted. Hver værdi i tabellerne ovenfor produceres af sidens egen kode ved indlæsning og er blevet verificeret mod platformens implementeringer i sitets testværktøjer; selvtesten gentager den sammenligning i browseren.