Een leven zonder feesten is als een verre reis zonder logement (Demokritos ca. 500 B.C.)
&Creative
[ Voor 21% gewijzigd door stfn345 op 06-11-2010 10:45 ]
Tabel antwoorden:
id int(11) auto_increment
vraag varchar(255)
antwoord varchar(255)
testid int(11)
Tabel test:
id int(11) auto_increment
ip varchar(50)
tijd datetime
oplossingen int(9)
Een leven zonder feesten is als een verre reis zonder logement (Demokritos ca. 500 B.C.)
&Creative
2 rij(en) toegevoegd.
Toegevoegd rij nummer: 131
Warning: #1264 Out of range value adjusted for column 'tijd' at row 2
Warning: #1366 Incorrect integer value: '' for column 'oplossingen' at row 2
Gebruikte query:
INSERT INTO `XXXXXDATABASENAAMXXX`.`test` (`id`, `ip`, `tijd`, `oplossingen`) VALUES ('129', '123.123.123.123', '2010-11-06 11:26:00', '12'), (NULL, '', '', '');
[ Voor 25% gewijzigd door Frankster op 06-11-2010 11:54 ]
Een leven zonder feesten is als een verre reis zonder logement (Demokritos ca. 500 B.C.)
&Creative
Probeer dit dan gewoon eens?
INSERT INTO `XXXXXDATABASENAAMXXX`.`test` (`ip`, `tijd`, `oplossingen`) VALUES ( '123.123.123.123', '2010-11-06 11:26:00', '8')
[ Voor 3% gewijzigd door Trucker Her op 06-11-2010 11:56 ]
Gestoord word je toch...
INSERT INTO `XXXXXDATABASENAAMXXX`.`test` (`ip`, `tijd`, `oplossingen`) VALUES ( '123.123.123.123', '2010-11-06 11:26:00', '8')
Doet het prima! En nu komen er records boven de 128 uit. Ik heb dus het vermoeden dat er gewoon geen mensen de test hebben gedaan...
Een leven zonder feesten is als een verre reis zonder logement (Demokritos ca. 500 B.C.)
&Creative
Gestoord word je toch...
Die doet het nu prima!Trucker Her schreef op zaterdag 06 november 2010 @ 11:57:
Probeer: INSERT INTO `XXXXXDATABASENAAMXXX`.`test` (`ip`, `tijd`, `oplossingen`) VALUES ( '123.123.123.123', '2010-11-06 11:26:00', '12') eens dan? Toch even voor de zekerheid. En ik had het laatste stukje weg gehaald. Die probeert volgens mij een tweede record erin te zetten met lege waardes, dat kan dus bij primary index.
Een leven zonder feesten is als een verre reis zonder logement (Demokritos ca. 500 B.C.)
&Creative
Gestoord word je toch...
Een leven zonder feesten is als een verre reis zonder logement (Demokritos ca. 500 B.C.)
&Creative
Wanneer je default waardes gebruikt zoals bv. een auto_increment, mag je deze
1) in het geheel weglaten (dus de kolomnaam)
2) NULL gebruiken bij de values
3) DEFAULT gebruiken bij de values
DEFAULT gebruiken heeft mijn voorkeur, dan is het ook later voor een ander duidelijk wat de bedoeling is zonder dat men het datamodel hoeft te kennen.
Je weet dat je daar twee inserts probeert te doen, één met echte waardes en één compleet nietszeggend record met lege waardes die niet eens ondersteund worden door de velden waarin je ze wil inserten?Frankster schreef op zaterdag 06 november 2010 @ 11:27:
Gebruikte query:
INSERT INTO `XXXXXDATABASENAAMXXX`.`test` (`id`, `ip`, `tijd`, `oplossingen`) VALUES ('129', '123.123.123.123', '2010-11-06 11:26:00', '12'), (NULL, '', '', '');
'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.
Zoals andere al aangeven voeg je hier twee rijen toe, niet een. Doordat die tweede ws niet toegestaan is, faalt de eerste ook. Wat me wel opvalt is dat je quotes om je getallen hebt staan, en dus strings gebruikt, waarom gebruik je strings voor getallen? Nu is het misschien zo dat SQL implementaties die moeten omzetten naar getallen, maar je kan het net zo goed beter niet doen. Daarmee geef je voor iemand die je query leest ook duidelijk aan dat het betreffende veld geen string is.Frankster schreef op zaterdag 06 november 2010 @ 11:27:
Als ik in tabel 'test' een rij toevoeg krijg ik de volgende foutmelding:
2 rij(en) toegevoegd.
Toegevoegd rij nummer: 131
Warning: #1264 Out of range value adjusted for column 'tijd' at row 2
Warning: #1366 Incorrect integer value: '' for column 'oplossingen' at row 2
Gebruikte query:
INSERT INTO `XXXXXDATABASENAAMXXX`.`test` (`id`, `ip`, `tijd`, `oplossingen`) VALUES ('129', '123.123.123.123', '2010-11-06 11:26:00', '12'), (NULL, '', '', '');
Ik gok dat hij in phpMyAdmin alle velden ingevuld heeft en daarbij ok geklikt heeft in één van de velden van het tweede record dat je eventueel kan toevoegen. Dan krijg je namelijk standaard dit gedrag, inclusief quotes.
'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.
Dat er quotes om staan, wil nog niet zeggen dat het dan een string betreft. Of wil je soms beweren dat een DATE ook een string is? Een DATE is een DATE en eist ook quotes. En zo zijn er nog vele andere datatypes waar dit ook voor geldt.Remus schreef op zaterdag 06 november 2010 @ 15:32:
Wat me wel opvalt is dat je quotes om je getallen hebt staan, en dus strings gebruikt, waarom gebruik je strings voor getallen?
Verder is het ook nog zo dat wanneer je bv. mysql_real_escape_string() gebruikt om SQL injection tegen te gaan, je verplicht bent om quotes te gebruiken. Zonder quotes is het nog steeds mogelijk om SQL injection toe te passen:
1
2
3
4
5
| <?php $var = "1 OR true"; $query = "SELECT * FROM tabel WHERE id = ".mysql_real_escape_string($var); echo $query; ?> |
En ziedaar, een fijn gehackte query die alle data ophaalt i.p.v. slechts de records met id=1
Mét quotes gaat het wél goed:
1
2
3
4
5
| <?php $var = "1 OR true"; $query = "SELECT * FROM tabel WHERE id = '".mysql_real_escape_string($var)."'"; echo $query; ?> |
Nu zal de query keihard mislukken, er is geen enkel id aanwezig met de waarde '1 OR true'. (aangenomen dat het id een integer is)
Databases zijn slimmer dan dat en een programmeur zal toch iets tegen SQL injection moeten doen en dus toch al moeten nadenken. Liever een paar quotes teveel dan te weinig, dit kan het verschil maken tussen een veilige applicatie en een gehackte database.Nu is het misschien zo dat SQL implementaties die moeten omzetten naar getallen, maar je kan het net zo goed beter niet doen. Daarmee geef je voor iemand die je query leest ook duidelijk aan dat het betreffende veld geen string is.
Leuk bedacht, als het niet zo was dat de functie mysql_real_escape_string() helemaal niet bedoeld is om SQL injections met integers te voorkomen. Zoals de naam het al zegt moet je deze functie gebruiken voor strings. Voor integers zou ik eerder denken aan functies als is_int() of intval(). Als vast staat dat een ingevoerde waarde werkelijk een integer is, mag deze echt zonder quotes in de query.cariolive23 schreef op zaterdag 06 november 2010 @ 15:42:
PHP:
1 2 3 4 5 <?php $var = "1 OR true"; $query = "SELECT * FROM tabel WHERE id = ".mysql_real_escape_string($var); echo $query; ?>
En ziedaar, een fijn gehackte query die alle data ophaalt i.p.v. slechts de records met id=1
Mét quotes gaat het wél goed:
PHP:
1 2 3 4 5 <?php $var = "1 OR true"; $query = "SELECT * FROM tabel WHERE id = '".mysql_real_escape_string($var)."'"; echo $query; ?>
Nu zal de query keihard mislukken, er is geen enkel id aanwezig met de waarde '1 OR true'. (aangenomen dat het id een integer is)
Indien beschikbaar zou het gebruik van prepared statements overigens het einde van de discussie betekenen, omdat je dan toch het meeste werk uit handen wordt genomen.
[ Voor 7% gewijzigd door doeternietoe op 06-11-2010 15:55 ]
In 9 van de 10 tutorials over SQL injection i.c.m. PHP en MySQL wordt dit echter wel als oplossing aangedragen en 9 van de 10 stukken code gebruiken het ook op deze wijze. Dan is het wel zo handig om het goed te doen.doeternietoe schreef op zaterdag 06 november 2010 @ 15:53:
[...]
Leuk bedacht, als het niet zo was dat de functie mysql_real_escape_string() helemaal niet bedoeld is om SQL injections met integers te voorkomen.
Mee eens, alleen jammer dat PHP de hele wereld aanziet voor een stringZoals de naam het al zegt moet je deze functie gebruiken voor strings.
Helemaal waar, je voelt er alleen helemaal niets van door er quotes om te zetten. Dat is echt niet fout en het kan problemen voorkomen.Voor integers zou ik eerder denken aan functies als is_int() of intval(). Als vast staat dat een ingevoerde waarde werkelijk een integer is, mag deze echt zonder quotes in de query.
MySQL heeft een beroerde ondersteuning voor prepared statements en alleen wanneer je de mysqli-functies van PHP gebruikt (of PDO die mysqli gebruikt) heb je de beschikking over prepared statements. Dat levert dan wel weer extra werk op, wat in feite onnodig is.Indien beschikbaar zou het gebruik van prepared statements overigens het einde van de discussie betekenen, omdat je dan toch het meeste werk uit handen wordt genomen.
PHP heeft dit voor PostgreSQL vele malen eenvoudiger geïmplementeerd, pg_query_params() neemt al het werk voor jou uit handen en je bent direct van het "gezeur" af met quotes:
1
2
3
4
5
| <?php $var = "1 OR true"; $query = "SELECT * FROM tabel WHERE id = $1"; // $1 is een placeholder, quotes heb je nooit nodig voor placeholders $result = pg_query_params($query, array($var)); // zal keihard falen, $var is geen integer ?> |
9 van de 10 tutorials zouden het ook fout(of minder goed) kunnen hebben.cariolive23 schreef op zaterdag 06 november 2010 @ 16:45:
[...]
In 9 van de 10 tutorials over SQL injection i.c.m. PHP en MySQL wordt dit echter wel als oplossing aangedragen en 9 van de 10 stukken code gebruiken het ook op deze wijze. Dan is het wel zo handig om het goed te doen.
PHP heeft wat quirks.[...]
Mee eens, alleen jammer dat PHP de hele wereld aanziet voor een string
Het zou op het gebruik van type casting of intval() geen invloed mogen hebben, dacht ik.
Bij SELECT queries niet nee, je krijgt dan gewone een lege resultset. Bij INSERT of UPDATE queries levert het invoeren van een string met niet numerieke gegevens echter een error op die je vrij simpel kunt voorkomen. Ik ben van mening dat je betere code produceert als je bij iedere query goed nadenkt over het type data en de controle dan wanneer je "omdat het kan" gewoon overal maar mysql_real_escape_string() voor gebruikt en overal quotes omheen zet.[...]
Helemaal waar, je voelt er alleen helemaal niets van door er quotes om te zetten. Dat is echt niet fout en het kan problemen voorkomen.
Ik bedoel inderdaad mysqli en PDO. Het gebruik daarvan levert over het algemeen meer regels code op, maar om nu te zeggen dat het veel meer werk is dan met de hand escapen...[...]
MySQL heeft een beroerde ondersteuning voor prepared statements en alleen wanneer je de mysqli-functies van PHP gebruikt (of PDO die mysqli gebruikt) heb je de beschikking over prepared statements. Dat levert dan wel weer extra werk op, wat in feite onnodig is.
'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.
Waarom zou je het resultaat van de query nog weer moeten binden? Dat is met bv. Firebird, Oracle of PostgreSQL niet nodig, een resultaat is een resultaat, het preparen gaat om de input, niet om de output.The parameter markers must be bound to application variables using mysqli_stmt_bind_param() and/or mysqli_stmt_bind_result() before executing the statement or fetching rows.
Daarnaast kun je in MySQL een bestaand prepared statement zonder problemen overschrijven:
Dit kan weer tot bijzondere bugs leiden.If a prepared statement with the given name already exists, it is deallocated implicitly before the new statement is prepared. This means that if the new statement contains an error and cannot be prepared, an error is returned and no statement with the given name exists.
In vergelijking met diverse andere databases, is de implementatie van prepared statements in MySQL en de ondersteuning daarvan met PHP, verre van ideaal.
Gestoord word je toch...
Maakt dat wat uit als het probleem van de topicstarter is opgelost?Trucker Her schreef op zaterdag 06 november 2010 @ 22:13:
Ehm, ja. Leuke discussie. Heeft dit nog maar iets met het topic te maken?
@cariolive23: dat iets anders werkt dan jij gewend bent maakt het nog geen brakke implementatie. Jouw bind-argument gaat dus niet op, IMO. Wat betreft het overschrijven van prepared statements geef ik je gelijk, dat is verwarrend. Maar ook dat maakt niet de hele implementatie brak.
Prepared statements i.c.m. mysqli werken prima en doen gewoon wat ze moeten doen.
'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.
Los van de prepared statements en object georiënteerde opzet dient PDO ook nog een ander doel, namelijk de gemakkelijke portability naar andere DBMS'enNMe schreef op zaterdag 06 november 2010 @ 23:28:
[...]
Prepared statements i.c.m. mysqli werken prima en doen gewoon wat ze moeten doen.
Overigens denk ik zelfs dat het op den duur zelfs makkelijker is om gewoon iets te doen van:
1
| $query->execute(array('foo', 'bar')); |
Dan telkens:
1
2
3
| $foo = mysql_real_escape_string($foo); $bar= mysql_real_escape_string($bar); mysql_query('blabla $foo blabla $bar'); |
Uiteraard op voorwaarde dat je je queries al geprepared hebt, maar dat zul je wel vaker tegenkomen bij grote(re) applicaties.
1
| $db->Query("SELECT * FROM tabel WHERE foo = %bar", array('bar' => $bar)); |
Onder water wordt het netjes uitgewerkt tot een veilige query voor die specifieke DB-engine. Mijn punt is dan ook dat het niet zozeer uitmaakt hoe de standaardfuncties precies werken, aangezien je meestal toch je eigen interface maakt om te zorgen dat je framework niet afhankelijk wordt van de gekozen database.
Voor de T-SQL-implementatie van die class heb ik nog een nifty methode geschreven om te zorgen dat LIMIT-query's alsnog blijven werken, scheelde wat herschrijven van query's.
'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.
Hoe weet jouw interface dan welk datatype $bar is/moet zijn? Ik bedoel, bij PDO kun je aangeven wat voor parameter je variabele moet hebben.NMe schreef op zondag 07 november 2010 @ 02:19:
Mwah, op kantoor hebben we een eigen databaseclass die een afgeleide class heeft voor elke database-engine die we ooit nodig gehad hebben. Daar roepen we een query als volgt aan:
PHP:
1 $db->Query("SELECT * FROM tabel WHERE foo = %bar", array('bar' => $bar));
Onder water wordt het netjes uitgewerkt tot een veilige query voor die specifieke DB-engine. Mijn punt is dan ook dat het niet zozeer uitmaakt hoe de standaardfuncties precies werken, aangezien je meestal toch je eigen interface maakt om te zorgen dat je framework niet afhankelijk wordt van de gekozen database.
Voor de T-SQL-implementatie van die class heb ik nog een nifty methode geschreven om te zorgen dat LIMIT-query's alsnog blijven werken, scheelde wat herschrijven van query's.
[ Voor 4% gewijzigd door Matis op 07-11-2010 16:22 ]
If money talks then I'm a mime
If time is money then I'm out of time
Gewoon overal quotes omheen zetten, zo uit het hoofd.Matis schreef op zondag 07 november 2010 @ 16:22:
[...]
Hoe weet jouw interface dan welk datatype $bar is/moet zijn? Ik bedoel, bij PDO kun je aangeven wat voor parameter je variabele moet hebben.
'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.