Concatten
Ivo P op 13/02/2019 13:32:45:
ik weet niet wat voor versie van mysqli jij gebruikt, maar met een ? werkt dat daar normaal wel.
http://php.net/manual/en/mysqli-stmt.prepare.php
http://php.net/manual/en/mysqli-stmt.bind-param.php
http://php.net/manual/en/mysqli-stmt.prepare.php
http://php.net/manual/en/mysqli-stmt.bind-param.php
Mja.
Prepared statements in mysqli zijn "clunky as hell". Met enige discipline ben je beter af met zelf quoten + escapen.
Gewijzigd op 14/02/2019 00:24:33 door Thomas van den Heuvel
>> In jouw query kan $id niet iets anders zijn dan een INT, neem jij aan.
Ja, omdat die waarde van een auto increment veld komt en dus niets anders dan een integer kan zijn.
>> En dan missen ook nog de ' ' om $id.
Dat hoeft bij een numerieke waarde toch ook niet?
Het probleem van het weglaten van quotes als deze "niet nodig zijn" is het volgende:
Elke keer als je een stuk code ziet waarbij dit het geval is zou er een kleine alarmbel af moeten gaan waarbij je denkt: "Hé, hier zijn geen quotes gebruikt, wat is hier aan de hand?". Vervolgens ga je na dat er inderdaad een id gevalideerd wordt, en deze wordt ook niet geaccepteerd (als het goed is) voor opname in de query (laat staan het dan uitvoeren hiervan!) als deze niet van het goede formaat is. Vervolgens concludeer je dat alles okay is.
En dit doe jij, of mogelijk iemand anders, ELKE KEER WEER als je deze code ziet. Je moet je er elke keer van vergewissen dat dit klopt. Dit is je reinste tijdsverspilling.
Het is in ieder geval veel makkelijker (Don't Make Me Think) om ALLE externe data te voorzien van quotes, en gewoon al dit soort data te escapen. Je hoeft er dan NOOIT over na te denken of dit "veilig" is of niet.
Nogmaals, ten overvloede, het gebruik van real_escape_string() zonder quotes is niet veilig (interne link) omdat real_escape_string() niets escaped als er niets te escapen valt.
Gewijzigd op 14/02/2019 01:52:47 door Thomas van den Heuvel
Code (php)
1
2
3
4
5
2
3
4
5
//bar wordt ook echt null, en niet ''
$db->execute('update tabelnaam set foo = :foo, bar = :bar where id = :id',['foo' => 5,'bar' => null,'id' => 5]);
//en voor recht-toe-recht-aan werk nog korter
$db->update('tablenaam',['foo' => 5,'bar' => null],['id' => 5]);
$db->execute('update tabelnaam set foo = :foo, bar = :bar where id = :id',['foo' => 5,'bar' => null,'id' => 5]);
//en voor recht-toe-recht-aan werk nog korter
$db->update('tablenaam',['foo' => 5,'bar' => null],['id' => 5]);
Nooit meer na hoeven te denken over quotes, nooit meer na hoeven te denken over escapen.
>> Het is in ieder geval veel makkelijker (Don't Make Me Think) om ALLE externe data te voorzien van quotes
Ik meen ergens gelezen te hebben in de documentatie van MySQL dat het niet wordt aangeraden om een getal (bijvoorbeeld een ID) te quoten. Hoe zit dat dan?
dan is er toch geen externe data?
Deze is juist extern, en behoort sowieso van single-quotes te worden voorzien.
Én natuurlijk van de real_escape_string functionaliteit!!
Gewijzigd op 14/02/2019 11:14:26 door - Ariën -
maar zodra het wordt
$teller = 10;
...SET getal = $teller
vervallen we weer in het varhaal van Thomas: je moet weer terug kijken om te zien dat $teller inderdaad gevalideerd is.
Je 2e vraag met $_GET is heel duidelijk, maar elke andere variabele kán ook rare data bevatten.
Zo heb ik ooit een bug moeten zoeken waarom een van de medewerkers nooit een keuringsrapport kon opslaan en alle collega's wel.
Uiteindelijk bleek het probleem te zijn
UPDATE ..... SET medewerker = '$naam' .....
En helaas: zijn naam bevatte een '
Volkomen legitieme data, maar liet wel een query mislukken. En die naam kwam gewoon uit de database onder zijn user-gegevens (waarom niet volstaan kon worden met zijn user-id is weer een andere vraag.)
Maar ik meen dus ergens gelezen te hebben in de officiële documentatie dat je een getal niet behoort te quoten en dat dat werd afgeraden. Dus ik weet t nu ook niet.
En dit boeit echt niets of heel weinig. Althans niet voor MySQL.
Alleen wanneer je prepared statements gebruikt in MySQLi maak je gebruik van het zogenaamde "binaire protocol" waarbij er iets met types gedaan wordt, anders zijn het toch allemaal strings.
Hetzelfde geldt voor PDO, als je daar de emulatie van prepared statements niet uitzet zet PDO ook doodleuk overal quotes omheen en negeert deze de typehints als je values bind(t). En je moet de logs induiken om dit na te gaan, omdat je op geen enkele andere manier kunt zien welke concrete SQL-code nu daadwerkelijk aan je database wordt gevoerd.
Daarbij, dit is wederom een tradeoff: mogelijk lever je een heel klein beetje performancewinst in voor een eenvoudige en eenduidige werkwijze die altijd veilig is.
Of je bouwt een wrapper zoals @Rob voorstelt. Maar zelfs dan. Stuur je dan altijd eerst SQL-templates naar de database met een prepare(), zelfs als het SELECT-queries betreft? Ik zie best mogelijkheden om een mooie wrapper te bouwen, maar dat is in zekere zin allemaal abstractie en de vraag is dan wat voor meerwaarde dat heeft? Boven een rechttoe-rechtaan-aanpak in MySQLi?
En het wordt natuurlijk weer anders als je een echte database abstractie laag maakt (waar het tweede fragment van @Rob op zinspeelt?) maar ook daar zul je je moeten afvragen of die abstractie echt nodig is en zich ooit terugverdient.
Deze hele discussie is ook gewoon "backwards" omdat we het de hele tijd hebben over "het beste gereedschap" zonder hier een concrecte "klus" in te betrekken. Het laatste bepaalt het eerste, niet andersom...
Gewijzigd op 14/02/2019 17:53:23 door Thomas van den Heuvel
Ja, als ik dat nog had kunnen terugvinden had ik het er uiteraard bij gezet.
In ieder geval wel stof tot nadenken.
>> mogelijk lever je een heel klein beetje performancewinst in voor een eenvoudige en eenduidige werkwijze die altijd veilig is.
Ja, dat stukje performancewinst boeit me niet eerlijk gezegd. Het stond er alsof het niet goed was om een getal te quoten.
Voor de rest met je eens hoor. Wat ik me wel afvraag ... ga je bijv. een id in een query dan nog escapen? Of zorg je ervoor dat je vantevoren die id al hebt gevalideerd? Wat is het beste? Eerst controleren of het een nummer is én dan ook nog escapen lijkt me overdreven?
Nee dat is niet overdreven. Je wilt geen enkele ruimte aan het toeval overlaten.
Je escaped dan dus nog steeds omdat je anders weer in de eerdere situatie terecht komt waarbij je niet weet of de quotes bewust zijn weggelaten of toch per ongeluk zijn vergeten. Je wilt hier niet elke keer weer over nadenken.
Je komt dan ook elke keer weer in een programmeerspagaat. Wel of geen quotes aanbrengen? Gewoon niet doen :p. Behandel alle externe data gewoon hetzelfde, is een stuk makkelijker.
Quoten + escapen en klaar.
En indien invoer in eerste instantie niet voldoet de query niet eens uitvoeren omdat dat zelden tot nooit een zinnig resultaat oplevert.
Gewijzigd op 14/02/2019 18:18:09 door Thomas van den Heuvel
Thomas van den Heuvel op 14/02/2019 17:19:52:
... echte database abstractie laag ... maar ook daar zul je je moeten afvragen of die abstractie echt nodig is en zich ooit terugverdient.
Zelf heb ik het idee dat ik er sneller door kan werken omdat ik minder code hoef te schrijven. Niet steeds die standaard stukken SQL, minder kans op fouten. Maar ook minder PHP omdat een groot deel van de afhandeling - lijstjes - er al in zit. Daarnaast loop je gewoon veel meer risico op een "misser" als je steeds handmatig moet escapen, en dat kan een *dure* grap worden.
Voor de duidelijkheid, dan wordt het dus bijv. zoiets?
Code (php)
1
2
3
4
5
2
3
4
5
<?php
$sql = "SELECT '" . $conn->real_escape_string($foo) . "' FROM bar WHERE id = '" . $conn->real_escape_string($id) . "'";
?>
$sql = "SELECT '" . $conn->real_escape_string($foo) . "' FROM bar WHERE id = '" . $conn->real_escape_string($id) . "'";
?>
Correct?
Sure, zoals ik al zei, je moet enige discipline hebben. Bij elk stuk data moet de realisatie zijn waar deze vandaan komt en hoe je deze zou moeten behandelen. Escaping hoort daar eigenlijk altijd bij (filter input, escape output). Uitzonderingen daarop zijn ook echt uitzonderingen.
> Correct?
Euh, $foo (kolomnaam?) waarom zou je dat dynamisch willen maken? Kolomnamen staan ook niet tussen quotes. Ik zou daar misschien een whitelist voor gebruiken ofzo. Voor $id lijkt het mij in orde. Quoten en escapen gaat bijna altijd over waarden, niet zozeer over kolomnamen, die liggen meestal vast.
Pffff, ik lag blijkbaar te slapen. Haha, dat sloeg inderdaad totaal nergens op.
Thomas van den Heuvel op 14/02/2019 23:23:19:
Euh, $foo (kolomnaam?) waarom zou je dat dynamisch willen maken? Kolomnamen staan ook niet tussen quotes.
Als je één (abstract) class wilt kunnen gebruiken voor verschillende tabellen, dan zijn dynamische kolomnamen onmisbaar. Dat kan overigens ook een constante zijn in plaats van een variabele:
Code (php)
Hier zijn zowel de tabelnaam als alle kolomnamen dynamisch.
Ah ja, oké ... dan werk je dus niet echt met queries maar met aparte functies om bijv. iets te inserten of updaten en als parameters geef je dan de kolomnaam en argumenten mee aan de functie. Dat kan inderdaad ook.