externe input 'opschonen'
Stel je krijgt bijv. een $_POST['foo'] binnen. Hoe ontdoe ik die $_POST['foo'] dan van eventuele schadelijke karakters?
Als je de gebruiker geen foute invoer wilt laten geven kan het vaak helpen als je de gebruiker simpelweg geen keuze geeft; gebruik een radiobutton of dropdown voor invoer in plaats van een open tekstveld. Of construeer zelf nieuwe formulierelementen waarbij het beter vastligt wat "correcte" invoer is.
En dan heb je daar nog altijd validatie voordat je het formulier verwerkt. Keur "foute" invoer gewoon af als deze niet goed is.
Tot slot heb je escaping zoals htmlspecialchars() voor de HTML-context en real_escape_string() voor de (my)SQL(i)-context (en zo zijn er wel meer voor andere contexten). Als je dit consequent toepast kan data nooit iets kwaads uitspoken.
Dit alles bij elkaar lijkt mij beter dan wat jij probeert te doen wat veel lijkt op "escape on input". Dit is waarschijnlijk niet de oplossing voor jouw probleem. Het beste devies wat gegeven kan worden lijkt mij nog steeds "filter input, escape output" - en dit gewoon overal consequent toepassen.
Gewijzigd op 29/01/2019 00:31:04 door Thomas van den Heuvel
Ik heb het dus niet over het controleren van het datatype (is het een string, is het een getal enz.), maar puur over het wegwerken van schadelijke tekens. PHP heeft bijv. wat sanitize functies: http://php.net/manual/en/filter.filters.sanitize.php
>> Als je de gebruiker geen foute invoer wilt laten geven kan het vaak helpen als je de gebruiker simpelweg geen keuze geeft; gebruik een radiobutton of dropdown voor invoer in plaats van een open tekstveld.
Dat klopt wel, maar jij weet ook dat je dat kunt manipuleren en dat je dus nooit zeker bent van wat je binnenkrijgt.
Quote:
kwalijke elementen
Deze verschillen per context. Dus als je gewoon escaped in de context waarin je deze data gebruikt is er niets aan de hand. Wat jij probeert te doen is alles escapen voor alle contexten waar je data mogelijk in gaat gebruiken? Dit is simpelweg niet praktisch. En zorgt ook voor problemen als je data later wilt bewerken, dan moet je mogelijk de escaping weer ongedaan maken. Het is gewoon handiger om de originele, rauwe, data op te slaan en deze gewoon op de juiste manier te behandelen wanneer je deze in een bepaalde context wilt gebruiken.
Quote:
Dat klopt wel, maar jij weet ook dat je dat kunt manipuleren en dat je dus nooit zeker bent van wat je binnenkrijgt.
Daarom zou je alles ook altijd (aan de serverzijde) moeten valideren als het van een voorgeschreven vorm dient te zijn.
Dus stel je krijgt in plaats van een naam iets als "jan ' OR 1=1" binnen, dat die generieke functie daar dan bijv. "jan OR 1=1" van zou maken. Maar als een dergelijke generieke functie niet bestaat dan houdt het op.
Daarnaast is het ook geen goede oplossing om schadelijke karakters te verwijderen, omdat je daarmee effectief de oorspronkelijke invoer aanpast wat sowieso niet iets is wat je zou moeten doen. Escapen in het gebruik lijkt mij beter. Op die manier pas je de mogelijke betekenis van invoer ook niet aan. Als iemand een enkele quote bedoeld dan is het niet aan het programma om te "second guessen" wat dit betekent. De data dient enkel veilig in het gebruik te zijn. Bedoelde de gebruiker toch iets anders dan een quote kan 'ie deze gewoon aanpassen of krijgt 'ie te horen dat dit onjuiste invoer is nog voordat er iets wordt opgeslagen.
Beschouw het als het dragen van kleding. Je past deze aan aan het weer, je draagt niet alles over elkaar aan om maar voorbereid te zijn op elk weertype...
Gewijzigd op 29/01/2019 02:02:51 door Thomas van den Heuvel
Hmm ... misschien een gat in de markt? ;-)
Thanks voor je antwoord!
Ozzie PHP op 29/01/2019 01:21:26:
Dus stel je krijgt in plaats van een naam iets als "jan ' OR 1=1" binnen, dat die generieke functie daar dan bijv. "jan OR 1=1" van zou maken.
Ik doe juist het omgekeerde. Als ik in een parameter iets meekrijg wat ik niet verwacht (dus bijvoorbeeld iets-wat-geen-cijfer-is in een numeriek ID), dan krijgt zo iemand een vinkje (sessie, IP, ...). Bij meer dan x-vinkjes binnen een bepaalde tijd: voor een bepaalde tijd buitengesloten.
Niet alleen om die "prutsers" een beetje terug te pesten, maar andersom weet je het ook maar nooit. Het is elke week weer een keer maandagochtend, en je kunt natuurlijk altijd iets over het hoofd zien. Op deze manier kunnen ze in ieder geval niet heel veel proberen binnen een bepaald tijdsbestek (en dan heb ik ondertussen wel een keer de tijd gehad om me over het log te buigen).
Voorbeelden van dingen die "niet mogen":
- HTML opmaak (of in ieder geval <script> tag) in "normale tekstvelden" (bijvoorbeeld naam).
- <script> tag of accolades in user-agent string.
- ".." in iets wat naar een bestandsnaam vertaalt/verwijst.
Niet dat dit alles tegen houdt, maar het zet in ieder geval de rem op de "prutser" die z'n lijstje met mogelijke aanvalsvectoren eens langs loopt.
Ik snap wat je zegt Rob, maar dat kun je dus niet generiek (algemeen geldend) doen. Want dan krijg je wat Thomas zegt. Stel iemand schrijft een bericht met daarin .. "Ik stond daar .. en toen ineens". Dat is in dat geval valide input, maar in het geval van een bestandsnaam niet.
Ik snap de redenering van @Rob ook wel, het kan soms handig zijn om je voelsprieten te gebruiken maar daarmee geef je wel een waardeoordeel over ingevoerde data. En dat is interpretatie. De betekenis hangt dan ook weer af van de (mogelijk taalkundige) context waarin je deze gebruikt. Code en machines zijn daar niet zo sterk in, en het lijkt mij al helemaal lastig om hier iets generieks voor te maken.
Over dat "iets-wat-geen-cijfer-is in een numeriek ID". Hier kun je op verschillende manieren mee omgaan. Stel dat je een lap code hebt waarmee je een record kan ophalen: code.php?id=xyz. Nu kun je wél aannames doen, sterker nog, je mag hier afdwingen dat $_GET['id'] (xyz) een auto-increment id is. Hier kun je dus, nog voordat je een query uitvoert, $_GET['id'] valideren (filter input). Maar wat voor controle gebruik je hier? is_numeric()? filter_var() (met extra parameters zou dit kunnen wellicht)? Wat ook geregeld gebeurt is dat $_GET['id'] simpelweg getypecast wordt naar een integer.
Maar in sommige van deze "validaties" pas je dus mogelijk de invoer aan, dus dat zou dan al inhouden dat de oorspronkelijke vorm niet goed was. Op dat moment ben je dus mogelijk een query aan het uitvoeren die weliswaar syntactisch correct is, maar semantisch onzinnig is en/of nooit een (zinnig) resultaat zal opleveren.
De code zou zo moeten werken dat de query uberhaupt niet werd uitgevoerd indien de invoer niet van de goede vorm was. Of dus meer in het algemeen: is de invoer niet van het goede formaat dan mag je alles gewoon staken. Dit eindeloos proberen te (her)verpakken en om te vormen (en op voorhand "veilig" te maken) is eigenlijk niets meer dan recht proberen te buigen wat al krom was, en zelfs dan is er geen garantie voor succes omdat je simpelweg niet weet of de omzetting wel iets opleverde wat zowel geldig maar ook zinnig (en ook als zodanig bedoeld) was.
Omdat machines niet sterk zijn om te bepalen wat je eigenlijk bedoelde, kun je ze beter precies vertellen wat je bedoelde. En als dit niet voldoet: computer says no. Try again.
Eigenlijk zou een gebruiker gewoon vrij moeten zijn wat deze ergens invult, binnen de kaders die daarvoor bestaan. Treedt iemand echter buiten de gebaande paden moeten ze niet vreemd opkijken als ze op den duur poep aan de schoenen hebben.
Gewijzigd op 29/01/2019 16:53:03 door Thomas van den Heuvel
Wat is volgens jou in zo'n geval de beste validatie en waarom?
>> De code zou zo moeten werken dat de query uberhaupt niet werd uitgevoerd indien de invoer niet van de goede vorm was.
Dat is inderdaad ook mijn insteek. En toch zie je soms best opmerkelijke dingen.
Dan wordt bijv. www.schoenenkopen.nl/product.php?id=poepschoen onderwater keurig getypecast naar een integer met waarde 1 waardoor je daadwerkelijk een product krijgt te zien.
Ozzie PHP op 29/01/2019 17:12:43:
Dan wordt bijv. www.schoenenkopen.nl/product.php?id=poepschoen onderwater keurig getypecast naar een integer met waarde 1 waardoor je daadwerkelijk een product krijgt te zien.
Nee, dat is 0. ;-)
https://3v4l.org/qP2DQ
Je voorbeeld geeft wel duidelijk aan dat de vraag bepaalt wat vervolgens acceptabele antwoorden op die vraag zijn.
Enne, 42 is het antwoord op alles.
Haha lol ... ik had verwacht dat ie naar 1 zou typecasten, hahaha.
Quote:
Wat is volgens jou in zo'n geval de beste validatie en waarom?
Eh, je zou filter_var() kunnen gebruiken I suppose, maar ik gebruik een regexp.
Dit is niet zozeer "het beste", maar "het doet wat ik wil en wat de bedoeling is": een positief geheel getal met ten minste de waarde "1". Dit is wat het (zou) moet(en) zijn, dus dat is waar ik op controleer.
Kun je eens laten zien hoe?
Gotcha/little known fact: het meta karakter $ accepteert ook één linefeed karakter (\n), dus wellicht doe je er nog verstandig aan om $in te trimmen.
Alternatief is filter_var, maar blijkbaar boeit het die validatiefilter ook niet of er een linefeed in zit...
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
$test = "12\n";
function isIndex($in) {
return preg_match('#^[1-9][0-9]*$#', $in) === 1;
}
if (isIndex($test)) {
echo 'waffles<br>';
var_dump($test);
} else {
echo 'no waffles<br>';
var_dump($test);
}
?><hr><?php
function isIndexToo($in) {
return filter_var($in, FILTER_VALIDATE_INT, array('min_range' => 1));
}
$result = isIndexToo($test);
if ($result === false) {
echo 'no waffles<br>';
var_dump($result); // false
} else {
echo 'waffles<br>';
var_dump($result); // typecast to int
}
?>
$test = "12\n";
function isIndex($in) {
return preg_match('#^[1-9][0-9]*$#', $in) === 1;
}
if (isIndex($test)) {
echo 'waffles<br>';
var_dump($test);
} else {
echo 'no waffles<br>';
var_dump($test);
}
?><hr><?php
function isIndexToo($in) {
return filter_var($in, FILTER_VALIDATE_INT, array('min_range' => 1));
}
$result = isIndexToo($test);
if ($result === false) {
echo 'no waffles<br>';
var_dump($result); // false
} else {
echo 'waffles<br>';
var_dump($result); // typecast to int
}
?>
@rob ... je zou dan dus nog moeten testen of het een string is bedoel je?
Gewijzigd op 29/01/2019 21:17:06 door Ozzie PHP
Of bijvoorbeeld is_scalar().
Ozzie PHP op 29/01/2019 21:15:15:
je zou dan dus nog moeten testen of het een string is bedoel je?
Alles in $_GET en $_POST is sowieso een string als het geen samengesteld type (array) is.
Ah oké.
>> Alles in $_GET en $_POST is sowieso een string als het geen samengesteld type (array) is.
Ja klopt, maar het kan dus ook een array zijn.