In een ander topic is de vraag naar voren gekomen of een query string ook escaped dient te worden. Om dat topic niet helemaal te kapen en omdat deze vraag toch interessant is wil ik hier in dit topic graag verder discussiëren.

De vraag is dus: Kan ik in de output van mijn script het volgende plaatsen zonder (html) escaping:


<a href="index.php?lang=nl&aantal=12">homepage</a>


of is het beter om dit te escapen en dus het volgende als output te genereren:


<a href="index.php?lang=nl&amp;aantal=12">homepage</a>


Daarnaast:

Voor onveilige data moet ik natuurlijk htmlentities() gebruiken:
<?php echo $language; ?> vs <?php echo htmlentities($language;) ?>

Maar als een variabele veilig genoeg is (altijd een nummer of een tekst uit een whitelist) dan lijkt mij dat weer wat overkill.

Reacties zijn welkom :-)


cannot generate system identifier for general entity "aantal"
<a href="index.php?lang=nl&aantal=12">homepage</a>

Kortom: beter om te escapen (maar ik denk dat je met heel wat weg komt voordat een browser d'r echt over struikelt)

Edit: O, ik zie dat ik wat gemist heb in de originele thread; daar wordt hetzelfde al uitgekauwd. Ook hier weer handig om d'r gewoon een functie voor te maken, en nooit meer naar om te kijken.
Als ik dit in de validator plak dan krijg ik geen foutmeldingen.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Mijn titel</title>
    </head>
    <body>
        <a href="index.php?lang=nl&aantal=12">homepage</a>
    </body>
</html>
Hm, interessant geval. Bij mij stond HTML4.01 standaard aan, en daar kreeg ik die foutmelding.

Blijkt dat het in HTML5 *mag* zolang er maar geen verwarring kan ontstaan: https://www.w3.org/TR/html5/syntax.html#syntax-ambiguous-ampersand . Zolang je key dus geen punt-komma bevat kan het eigenlijk niet fout gaan.

"?lang=nl&aantal;foo=12" (key = "aantal;foo") geeft dus wel een error, en zal dus geschreven moeten worden als "?lang=nl&amp;aantal;foo=12".

Maar gewoon alles door de escape-molen halen kan geen kwaad.
Oke dank je Rob.

Meestal verzin je als programmeur zelf de keys en ik zal daar niet snel een ; of iets dergelijks bijhalen maar mocht er ooit met een variabele key gewerkt worden wordt het wel even opletten ;-)
Frank Nietbelangrijk op 26/10/2017 16:16:21
De vraag is dus: Kan ik in de output van mijn script het volgende plaatsen zonder (html) escaping:

Ja, maar dat is niet aan te raden. Vanwege mogelijke ambiguïteit.

Frank Nietbelangrijk op 26/10/2017 16:16:21
of is het beter om dit te escapen en dus het volgende als output te genereren:

Ja, dat verdient de voorkeur. Vanwege mogelijke ambiguïteit.

Frank Nietbelangrijk op 26/10/2017 16:16:21
Voor onveilige data moet ik natuurlijk htmlentities() gebruiken:

Actually, htmlspecialchars() is beter omdat je anders in de knoei komt in sommige situaties (XML en afgeleiden, de bron hiervoor moet ik je schuldig blijven, maar dit was min of meer de uitkomst van mijn htmlentities() vs htmlspecialchars() onderzoekje).

Frank Nietbelangrijk op 26/10/2017 16:16:21
Maar als een variabele veilig genoeg is (altijd een nummer of een tekst uit een whitelist) dan lijkt mij dat weer wat overkill.

Ik ga hier herhalen wat ik in de andere thread al zei: het is gewoon makkelijker om regels consequent toe te passen. Als je namelijk een keer escaping weglaat omdat dit niet nodig is dan moet iemand (anders die jouw code bekijkt) nadenken en dingen controleren om na te gaan dat het echt de bedoeling was dat iets niet geescaped diende te worden. Al deze overhead heb je niet als je gewoon overal escaped, of dit nu nodig is of niet. Keep It Simple.

Overkill wellicht, maar dit traint je ook om beducht te zijn op user input / externe data in je applicatie, die altijd aandacht verdient.

En als je dan niet escaped, vermeld dan een expliciete reden waarom dit niet hoeft. Dan stuur je een collega ook niet met een kluitje het riet in. En als het al geescaped was, heb je hier geen omkijken naar.

Rob Doemaarwat op 26/10/2017 19:19:48
Edit: O, ik zie dat ik wat gemist heb in de originele thread; daar wordt hetzelfde al uitgekauwd.

Voorgekauwd? Uitgekauwd? Meh.

Anyhoo, in een hele simpele optiek moet je het denk ik als volgt zien. Je produceert meestal een HTML document. Daarin zitten dynamische delen afkomstig van een externe bron. Deze delen dienen meestal niet als HTML geïnterpreteerd te worden. Hiervoor zet je escaping-functionaliteit in om dit te garanderen.

Daarnaast heb je in wat verder ontwikkelde applicaties een interne navigatie. Om je applicatie vrij verplaatsbaar te houden zouden alle interne links dynamisch gegenereerd moeten worden. Op basis van servervariabelen, configuratie-instellingen et cetera. Idealiter introduceer je hiervoor een soort van linkfunctie.

Het is echter niet de taak van deze functie om aannames te doen in welke context deze gebruikt gaat worden. Deze linkfunctie zou namelijk ook in JavaScript ingezet kunnen worden en dan gelden er andere regels. Voor het escapen is een functie natuurlijk ook handig. Dit omdat je dan centraal -ook weer op grond van een serverinstelling of configuratie-variabele- bijvoorbeeld een character encoding kunt regelen maar misschien nog belangrijker (in beide gevallen): je voorkomt hardcoding.

Het niet uitschrijven van interne links maar het variabel houden hiervan middels een functie zorgt ervoor dat je applicatie niet verandert in een baksteen. Het escapen van output via een functie stelt je in staat om dit mogelijk aan extra regels te onderwerpen of dingen centraal aan te passen. Maar elk van deze functies heeft een eigen, en enkele verantwoordelijkheid: de een is bedoeld voor het (in welke context dan ook) genereren van links, het ander is (specifiek) bedoeld voor het escapen van (het ontdoen van enige speciale betekenis van) DATA in HTML.

En dan zijn er nog aparte regels voor de key-value paren van querystringparameters (voor de duidelijkheid: we hebben het hier over hyperlinks in <a href="..."> tags). Deze dienen (eigenlijk) beide ge-urlencode() te worden zodat, wederom, deze data wordt ontdaan van enige speciale betekenis binnen de URL. Vaak kies je de keys zelf zodat dit om te beginnen niet voor problemen zorgt met interpretatie (toch?) maar better safe than sorry wellicht. Dit alles kun je opnemen in de eerdergenoemde linkfunctie waaraan je een $args (array van key-value paren) argument ofzo toevoegt.

Vervolgens is deze gebakken link... weer DATA die van buitenaf afkomstig is en niet als HTML geïnterpreteerd dient te worden, dus hier gooi je, als je deze in een <a href="..."> tag gebruikt weer de escaping-functie over, dit verschilt verder niet van andere externe data in je applicatie en deze behandel je dus ook gewoon weer precies hetzelfde.

Ik heb nu een heel relaas geschreven om te onderbouwen waarom ik doe wat ik doe. Het is simpel, het is veilig, het is consistent. Deze aanpak zou ik niet zondermeer opvolgen, maar ook niet op voorhand afschrijven omdat je niet dezelfde weg hebt afgelegd als ik. Misschien is de enige manier om tot deze conclusies te komen zelf deze weg te bewandelen. Ook ben ik zeer benieuwd of er ook echt andere aanpakken zijn die hetzelfde bereiken, maar dan ben ik vooral geïnteresseerd in het waarom, en niet zozeer in de vorm.

Dit klinkt misschien arrogant, maar zo is het niet bedoeld. We bevinden ons allen in hetzelfde landschap, maar afhankelijk van waar je staat zie je mogelijk andere dingen. Of zie je dezelfde dingen vanuit een andere hoek.
Ik gebruik een templating systeem waarbij elke input (een "tag" in het template) automatisch ge-escaped wordt (je moet bewust aangeven als iets *niet* ge-escaped moet worden). Over die stap hoef ik dus eigenlijk niet na te denken.

Ter info/hetzelfde: Interne links (die ik dus via een tag invoeg) komen uit de (reverse) router, en die werkt standaard met urlencode() en http_build_query() (maar dus nog wel een bewuste actie; ik zou 'm ook gewoon uit kunnen schrijven).

Rob Doemaarwat op 27/10/2017 20:05:34
automatisch ge-escaped

Uhh, maar dat is dan escape-on-input? Vaak is het handiger om escaping zo lang mogelijk uit te stellen, tot net voor het weergeven, en dit dan expliciet doen, zodat je dit dus ook kunt zien dat het gebeurt (anders heb je weer het eerdergenoemde probleem dat je dit dus niet (zeker) weet. En wat als je het daar vergeet te doen?). Tenzij dit templating systeem enkel voor HTML (maar in één context) wordt gebruikt? Maar dan nog. escape-on-input lijkt mij alleen zinvol/handig in enkele uitzonderlijke gevallen. Ik zou nooit op voorhand escapen (voordat het de database in gaat bijvooorbeeld). En altijd expliciet. Of ik moet je verkeerd begrijpen.
Met "input" bedoelde ik de input naar het templating systeem toe. Ik geef de ruwe, originele waarden dus mee aan de template functie, en het resultaat daarvan is een stuk HTML waarin de data ge-escaped is.

En dit is dus inderdaad alleen voor HTML. Bij een JSON response wordt er niks ge-escaped (json_encode() doet dat wel ,amar aan de andere komt komt het weer op dezelfde manier uit). Dan moet je er dus zelf aan denken als je het bijvoorbeeld in een javascript context gaat gebruiken (bijvoorbeeld jQuery's .text() vs .html()).
@Thomas, Dank voor je uitgebreide antwoord.
@Rob, Gebruik je net als ik Twig?

Wat Rob zegt dat doe ik ook. Ik gebruik (voor het echte werk) eigenlijk altijd een template parser. Deze zal alle variabelen escapen tenzij ik dat expliciet anders aangeef. Ik durf echter niet te zeggen of dat ook geldt voor de interne links (routes) die opgebouwd worden. Ondanks dat de template parser voor mij escaped vind ik het belangrijk om mijn kennis hierin te verruimen of anders up to date te houden.

Een lekke HTML pagina kan vervelende gevolgen hebben voor de gebruiker. Ik moet eerlijk bekennen dat ik daar meestal weinig aandacht aan besteedt. Maar data dat van buitenaf mijn applicatie in komt daar ben ik wel altijd zo secuur mogelijk in geweest.

[edit]
Twig escaped zijn routes ook. Ik had toch nog maar wat testjes gedaan maar gelukkig geen gebreken gevonden.
Twig: Nee. Een doe-het-zelf ding "sinds 2009". Het doet dus precies wat ik wil (maar grofweg vergelijkbaar met Twig).

Reageren