Percentage-encoding-inspecteur

Procentcodering is niet één regel maar meerdere, en ze zijn het oneens over een dozijn tekens. Deze pagina haalt dezelfde tekst door encodeURIComponent, encodeURI en de serialisatie application/x-www-form-urlencoded naast elkaar, markeert de tekens waarop ze verschillen, drukt een rij per teken af met de UTF-8-bytes en de RFC 3986-klasse, en zegt hoe vaak een string is gecodeerd. Aan de decodeerkant leest de pagina een string met een strikte of een toegestane regel en vertelt u welke escape kapot is en waar, in plaats van stilzwijgend een verkeerd resultaat terug te geven.
Alle verwerking gebeurt lokaal in uw browser — uw gegevens worden nooit naar een server geüpload.

Weet u niet wat procentcodering is? Procentcodering in gewone taal uitgelegd — wat het is, waarom een spatie %20 of + wordt, en wat het betekent wanneer een URL %25 toont.
Wat dit doet:
Zo leest u de escapes:
encodeURIComponent
encodeURI
formulier (application/x-www-form-urlencoded)
Ctrl+Enter in de invoer voert de actieve modus uit.
Kies een modus en klik op de actieknop. Coderen & inspecteren toont de drie coderingen van uw tekst plus een rij per teken; Decoderen leest de escapes terug en telt de lagen. De knop Zelftest draait elk gedocumenteerd voorbeeld, elk afdrukbaar ASCII-teken en de laagketens die in het gegevensbestand van deze pagina zijn vastgelegd, opnieuw, en meldt het aantal hier.
Waar de encoders het oneens zijn:alleen de onderstaande tekens worden door de drie methoden verschillend behandeld. Al het andere gaat door alle drie ongewijzigd, of wordt door alle drie identiek geëscapet.
Elk teken, één rij per teken:de UTF-8-bytes waarin een teken wordt omgezet, de klasse die RFC 3986 eraan toekent, en de escape die elke context schrijft. De vier contextkolommen zijn de WHATWG-procentcodeersets, die een browser gebruikt wanneer hij een pad, een query of een fragment opbouwt; een gearceerde rij is een teken dat de drie encoders verschillend behandelen.
De exacte set die deze pagina voor elk onderdeel van een URL escapet:afgedrukt door de encoder zelf, dus deze tabel en de code die de kolommen hierboven vult, kunnen niet uit elkaar lopen. Elke set escapet ook de C0-besturingstekens (U+0000 tot U+001F) en elk codepunt boven U+007E, en daarom wordt niet-ASCII-tekst in elke context altijd geëscapet.
Deze sets zijn afgeleid van de WHATWG-procentcodeersets en wijken er op twee bewuste punten van af, beide om handmatige samenstelling veilig te maken: een hekje wordt binnen een query geëscapet, omdat een kaal hekje het fragment zou beginnen; en de componentset is vastgezet op precies de tekens die encodeURIComponent escapet, inclusief de apostrof, terwijl de formulierset is vastgezet op wat URLSearchParams escapet. De twee kolommen in de tabel hierboven zijn daarom het eigen gedrag van het platform en geen herimplementatie ervan. Die lijst verklaart ook een resultaat dat mensen verrassend vinden: een procentteken wordt door encodeURIComponent en door encodeURI geëscapet, maar het staat in geen van de WHATWG-contextsets, dus de kolommen pad, query en fragment van de tabel per teken laten het ongemoeid. Een kaal procentteken in een URL die u met de hand hebt samengesteld, is nog steeds een risico, omdat de standaard verwacht dat de invoer eerst is gevalideerd: %41 in een query is voor heel wat serverframeworks één A, en de strikte lezing van deze pagina behandelt een los % als een kapotte escape. Het zelf escapen als %25 is de veiligere gewoonte, en de tabel per teken toont beide schrijfwijzen naast elkaar.
Hoe vaak deze string is gecodeerd:elke decoderingsronde is één laag. Een waarde die twee keer is gecodeerd begint met%25waar een enkele codering zou blijven%20, en een waarde die drie keer is gecodeerd begint met%2525.
De tekens die een URL als zodanig mag dragen:RFC 3986 verdeelt het ASCII-bereik in drie groepen, en de escaperegels hebben pas zin tegen die indeling.
De gereserveerde tekens zijn niet verboden, ze zijn ingenomen: ze betekenen iets voor de URL-parser. De regel die daaruit volgt is kort — als een teken gereserveerd is en u het als data bedoelt, codeer het; als het niet-gereserveerd is, hoeft u het nooit te coderen.
De drie methoden, en wat elk ongemoeid laat:
De lijsten zijn de exacte sets die elke methode in het ASCII-bereik ongecodeerd laat; de zelftest controleert elk afdrukbaar ASCII-teken daartegen, zodat de tabel niet kan afwijken van de encoders die hij beschrijft.
Hoe groot een invoer deze pagina accepteert, en waarom:procentcodering is één doorloop over de tekst, dus anders dan de radix-converters op deze site groeit de werklast recht evenredig in plaats van kwadratisch: er is geen lengte waarbij de rekenkunde zelf het probleem wordt. De limieten hieronder bestaan omdat de tabel per teken in het document wordt getekend, en een tabel met een miljoen rijen is wat een browser echt doet vastlopen.
Wat is procentcodering?

Procentcodering — ook URL-codering genoemd, en in oudere documenten escaping — is de regel waarmee een URL tekens kan dragen die hij niet mag bevatten. Een procentescape is drie ASCII-tekens: een procentteken en twee hexadecimale cijfers, die samen één byte voorstellen. %20 is de byte 0x20, een spatie. %2F is de byte 0x2F, een schuine streep. Er speelt niets anders mee: de escape noemt een byte, geen teken.

Omdat de codering over bytes gaat, moet de tekst eerst in bytes worden omgezet, en die bytecodering is UTF-8. Daar komen de lange escapes vandaan: één Chinees teken is drie UTF-8-bytes en dus drie escapes, en één emoji is vier bytes en twaalf ASCII-tekens.

De versie in 30 seconden: een spatie is %20 als URL-waarde maar + in formuliergegevens; # is %23; een letterlijk procentteken is %25. De drie encoders op deze pagina zijn het eens over de letters, de cijfers en -_., en ze zijn het oneens over een dozijn leestekens, de tilde daaronder — precies daarom produceren encodeURIComponent en encodeURI verschillende strings voor dezelfde tekst, en kan de verkeerde van de twee een waarde in twee queryparameters splitsen.
Waarom een URL het überhaupt nodig heeft

Een URI is gedefinieerd als een reeks ASCII-tekens (RFC 3986 sectie 2.1), en binnen dat ASCII-bereik zijn sommige tekens gereserveerd: dat zijn de leestekens die de URL zijn structuur geven. Een dubbele punt en twee schuine strepen scheiden het schema, een apenstaartje scheidt de gebruikersinformatie van de host, een vraagteken start de query, een hekje start het fragment, een ampersand scheidt queryparameters, en een procentteken introduceert een escape.

Dat levert één probleem en één achterpoortje op. Het probleem: een waarde die zelf een van die tekens bevat — een zoekvak met "a&b", een bestandsnaam met een schuine streep, een wachtwoord met een dubbele punt — zou anders de structuur van de URL waarin hij reist veranderen. Het achterpoortje: het procentteken zelf, zodat de encoder zijn eigen alfabet gewoon kan uitbreiden. % wordt %25, en de twee hexadecimale cijfers erachter zijn gewone niet-gereserveerde tekens.

Uitgewerkte voorbeelden

Dit zijn de exacte uitvoer van de drie encoders die deze pagina draait, bij invoer die gekozen is omdat ze zich verschillend gedragen. De pagina berekent ze opnieuw voor uw ogen, en de zelftest controleert elke rij opnieuw tegen de eigen implementaties van het platform.

De drie methoden, en wanneer elk de juiste is

De korte versie die de meeste fouten voorkomt: alles dat in één parameterwaarde terechtkomt, gaat via encodeURIComponent (of via een formulierserialiser, die hetzelfde werk doet met een andere veilige set), en een volledige, al samengestelde URL gaat via encodeURI als hij al moet worden geëscapet. Naar de verkeerde van die twee grijpen is de meest voorkomende fout bij procentcodering in webcode.

De tekens die een URL mag bevatten, volledig
Twee plustekens, twee betekenissen

Een spatie is het enige teken waarvan de codering afhangt van waar de tekst naartoe gaat, en het plusteken is de reden daarvoor.

De valkuil zit midden in die tabel. In formuliergegevens betekent een plusteken een spatie, dus een waarde die werkelijk een plusteken bevat — een telefoonnummer geschreven als +44 20 7946 0958, een rekenkundige uitdrukking, een e-mailadres met een plus-tag — moet reizen als %2B. Als hij reist als een letterlijk plusteken, leest de ontvanger hem terug als spatie, en komt +44 aan als " 44".

Twee keer gecodeerd, en hoe u het herkent

Dubbele codering gebeurt wanneer een string al is geëscapet en daarna nog eens wordt geëscapet. Het procentteken van de eerste escape is een teken zoals elk ander, dus het wordt %25, en de laag eronder blijft als zichtbare tekst bestaan.

Herkennen is een kwestie van tellen: decodeer de string één keer en kijk naar het resultaat. Als het nog escapes bevat die bewust lijken — een geldige %XX of een formulierplusteken waar een spatie verwacht wordt — is er nog een laag. Als er %25 staat gevolgd door twee hexadecimale cijfers, is de volgende laag zeker een escape.

Twee praktische opmerkingen. Ten eerste is tweemaal decoderen niet onschuldig: het is een bekende manier om ervoor te zorgen dat een beveiligingsfilter en de code erachter verschillende strings zien, dus een filter dat vóór een tweede decodering draait, filtert de verkeerde tekst. Ten tweede is het aantal lagen hier een eigenschap van de string, geen belofte over de bedoeling: een legitieme waarde kan één laag diep zijn en een kapotte kan er twee hebben.

De fouten waarvoor deze pagina is gebouwd
  • Een waarde gecodeerd met encodeURI. De scheidingstekens blijven bestaan, dus een waarde met & of = splitst aan de andere kant in meerdere parameters. Vergelijk de twee kolommen bovenaan deze pagina met de invoer a&b=c: de componentkolom codeert beide tekens, de encodeURI-kolom behoudt ze.
  • Een letterlijk plusteken verzonden als formuliergegevens. Het komt aan als een spatie. Gebruik %2B.
  • Een waarde twee keer gecodeerd "voor de zekerheid". Dat is niet veiliger. De ontvanger decodeert één keer en krijgt een string die nog steeds procentescapes bevat, wat dezelfde fout is als een verkeerde tekenset, één laag dieper.
  • Decoderen wat nooit gecodeerd was. Een letterlijk procentteken in tekst — 100% sure — is geen escape. In een strikte lezing is het een kapotte escape, op de positie die deze pagina meldt; in een toegestane lezing blijft het bewaard, en daarom is toegestaan hier de standaard en niet andersom.
  • Aannemen dat de escape in hoofdletters moet staan. Decoderen behandelt %2f en %2F als dezelfde byte, en beide zijn geldig. Hoofdletters is alleen de canonieke schrijfwijze die RFC 3986 aanbeveelt, en die andere tools zullen produceren.
  • Aannemen dat procentcodering tekst veilig maakt. Het maakt tekst ondubbelzinnig binnen een URL. Het stopt geen injectie, het is geen versleuteling en het is geen sanitizer: de codering moet de laatste stap vóór de samenstelling zijn, voor de context waarin de tekst terechtkomt.
Korte antwoorden

“Is een plusteken in een URL een spatie?”

Alleen in formuliergegevens, en alleen in het queryonderdeel van een formulierverzending. In een pad, in een fragment en in de query van een gewone link is een plusteken een letterlijk plusteken. Daarom heeft deze pagina er een schakelaar voor in plaats van te raden.

“Waarom toont mijn link %25?”

Omdat iets een procentteken heeft gecodeerd. Ofwel is een letterlijk procentteken door een encoder gegaan — 100% sure wordt 100%25%20sure — ofwel is een string die al een escape bevatte, nog een keer gecodeerd. Decodeer de string één keer en kijk wat er terugkomt: als het resultaat nog steeds %XX bevat, is er nog een laag.

“Maakt het uit of een schuine streep in een querywaarde %2F of / is?”

Voor de URL-parser niet: beide bereiken de server als een schuine streep, en de WHATWG-queryset laat een schuine streep ongemoeid. Voor interoperabiliteit soms wel: heel wat serverframeworks voeren hun eigen decodering uit over een ruwe queryreeks, en een paar van hen zijn het oneens over tekens die nooit zijn geëscapet. Wanneer een waarde met de hand wordt samengesteld in plaats van door een serialiser, is het veiliger om de gereserveerde tekens expliciet te escapen.

“Waarom codeert de formulierkolom een tilde die de andere twee met rust laten?”

Omdat de veilige set van de formulierencoder smaller is: alleen letters, cijfers en -_.*. De WHATWG-formulierencoder codeert ~ ! ' ( ), hoewel RFC 3986 ~ als unreserved aanmerkt. Beide schrijfwijzen decoderen naar dezelfde waarde; het verschil telt alleen wanneer u strings byte voor byte vergelijkt.

“Kunnen dezelfde tekens ongecodeerd in een browseradresbalk voorkomen?”

Ja, en dat is de adresbalk, niet de URL. Browsers tonen een leesbare vorm van een URL en coderen die wanneer ze hem verzenden, dus 中文 in een link wordt %E4%B8%AD%E6%96%87 op de draad. Als u de URL uit de adresbalk kopieert, krijgt u afhankelijk van de browser de ene of de andere vorm.

“Elke escape hier lijkt geldig, maar het teken dat eruit komt is niet het teken dat ik verwachtte. Waarom?”

Achter "het is verkeerd gedecodeerd" schuilen twee verschillende fouten, en de pagina meldt ze verschillend. De eerste is een kapotte escape: een procentteken dat niet door twee hexadecimale cijfers wordt gevolgd, zoals een afsluitende 100% of een %zz. Dat is een syntaxisprobleem; de strikte lezing weigert het en zegt waar het zit, terwijl de toegestane lezing het als letterlijke tekst bewaart en het vermeldt. De tweede is een escape waarvan de byte perfect legaal is, maar waarvan de bytes geen geldig UTF-8-teken vormen, zoals %FF op zichzelf of een driebytesteken dat wordt afgekapt als %E4%B8. Dat is geen syntaxisprobleem, dus beide lezingen geven de tekst terug, vervangen de bytes die niet kunnen worden gedecodeerd door het vervangingsteken U+FFFD ("�"), en tonen een waarschuwing die dat zegt. Die waarschuwing is het nuttige deel: tekst die binnenkomt met vervangingstekens betekent meestal dat de afzender een andere tekenset gebruikte, of dat de string één keer te vaak is geëscapet. Procentcodering noemt bytes, en deze pagina leest die bytes, net als de URL-standaard, altijd als UTF-8.

“Wat doet deze pagina niet?”

Het splitst een URL niet in schema, host, pad en query — daarvoor is de URL-parser. Het controleert niet of een link veilig is om te openen, het neemt geen contact op met iets, en het raadt de tekenset van de bytes achter een escape niet: het leest ze altijd als UTF-8, wat de URL-standaard vereist.

“Welke terugval heeft decoderen nodig, en hoe weet ik dat die klopt?”

Druk op Zelftest. Die vergelijkt de eigen encoders van de pagina met de encodeURIComponent, encodeURI en URLSearchParams van het platform voor elk afdrukbaar ASCII-teken, draait elk voorbeeld in de tabellen hierboven opnieuw, controleert de vastgelegde laagketens opnieuw en meldt het aantal controles hier.

Standaarden en reikwijdte

Procentcodering is gespecificeerd in RFC 3986 sectie 2.1, met de definitie van gereserveerde en niet-gereserveerde tekens in de secties 2.2 en 2.3 en de hoofdlettercodering in sectie 6.2.2.1. De sets die een browser werkelijk gebruikt om een URL op te bouwen, staan in de WHATWG URL Standard, die ze per context splitst: één set voor een fragment, één voor een query, één voor een speciale query, één voor een pad, één voor gebruikersinformatie, en de smallere die voor één component wordt gebruikt. Formulierverzending, inclusief het plusteken, staat in de HTML-standaard onder application/x-www-form-urlencoded. De twee functies encodeURI en encodeURIComponent maken deel uit van ECMAScript zelf.

Deze pagina vult de kolommen pad, query en fragment uit de eigen sets per context, en drukt de exacte lijst voor elke context af naast het resultaat. Die sets zijn afgeleid van de WHATWG-sets en wijken op twee bewuste punten af: een hekje wordt binnen een query geëscapet, omdat een kaal hekje het fragment zou beginnen, en de componentset is vastgezet op precies de tekens die encodeURIComponent escapet. Waar een functie van het platform de definitie van het antwoord is — encodeURIComponent, encodeURI en de formulierserialisatie — wordt die functie rechtstreeks aangeroepen, dus die kolommen zijn het eigen gedrag van de browser en geen herimplementatie ervan.

De paginatekst is voor deze site geschreven. Elke waarde in de tabellen hierboven wordt door de eigen code van de pagina bij het laden geproduceerd en is geverifieerd tegen de implementaties van het platform in de testtools van deze site; de zelftest herhaalt die vergelijking in uw browser.