[SQL / MySQL] SQL Injection mogelijkheden en bescherming

Pagina: 1
Acties:
  • 315 views sinds 30-01-2008
  • Reageer

Acties:
  • 0 Henk 'm!

  • Thomasje
  • Registratie: Augustus 2002
  • Laatst online: 29-05-2024

Thomasje

Semacode

Modbreak:Let op: topic is afgesplitst van [rml][ PHP] Link Plaatje laden via SQL *[/rml]
T-MOB schreef op 16 oktober 2004 @ 02:16:
http://jouwsite/pagina?id=1'; DROP TABLE `site`; :Y)

Serieus: volgens mij kom je er wel uit als je even hier kijkt. Je komt dan op iets als:
PHP:
1
2
3
4
while ($list = mysql_fetch_object($query)) 
{
echo '[img]"'[/img]kolom_met_src .'" alt="" />';
}
Ik denk niet dat hij dat door heeft dat je zomaar zijn tabel kan verwijderen.

Een goeie link naar een artikel op php freakz voor beveiliging:
http://www.phpfreakz.nl/artikelen.php?aid=106


Dan kan je zien wat er allemaal kan gebeuren.

[ Voor 8% gewijzigd door gorgi_19 op 16-10-2004 11:33 ]


Acties:
  • 0 Henk 'm!

  • Y0ur1
  • Registratie: Oktober 2000
  • Niet online
T-MOB schreef op 16 oktober 2004 @ 02:16:
http://jouwsite/pagina?id=1'; DROP TABLE `site`; :Y)

Serieus: volgens mij kom je er wel uit als je even hier kijkt. Je komt dan op iets als:
PHP:
1
2
3
4
while ($list = mysql_fetch_object($query)) 
{
echo '[img]"'[/img]kolom_met_src .'" alt="" />';
}
Ik heb dat artikel op phpfreakz ook al es gelezen en heb het getest of het zo werkt. Maar ik krijg het niet voor elkaar. Ik zet de get variabele direct in de query en doe het volgende: index.php?page=news&news=37' DROP TABLE `news`; Maar de tabel wordt niet verwijderd.

Acties:
  • 0 Henk 'm!

  • Thomasje
  • Registratie: Augustus 2002
  • Laatst online: 29-05-2024

Thomasje

Semacode

Y0ur1 schreef op 16 oktober 2004 @ 11:11:
[...]


Ik heb dat artikel op phpfreakz ook al es gelezen en heb het getest of het zo werkt. Maar ik krijg het niet voor elkaar. Ik zet de get variabele direct in de query en doe het volgende: index.php?page=news&news=37' DROP TABLE `news`; Maar de tabel wordt niet verwijderd.
In het artikel staat ook:

script.php?order=desc; DROP TABLE users

Dus die punt koma staat verkeerd

Acties:
  • 0 Henk 'm!

  • Y0ur1
  • Registratie: Oktober 2000
  • Niet online
Thomasje schreef op 16 oktober 2004 @ 11:14:
[...]


In het artikel staat ook:

script.php?order=desc; DROP TABLE users

Dus die punt koma staat verkeerd
Werkt ook niet, of werkt het alleen als je de records nog sorteerd met ORDER?

Acties:
  • 0 Henk 'm!

  • simon
  • Registratie: Maart 2002
  • Laatst online: 08-09 19:03
verder zal hij alles tussen de ' en de ' niet uitvoeren. Daarnaast, gebruik mysql_fetch_assoc, wat een array retourneert, die je via $list['veld'] uitleest..
Y0ur1 schreef op 16 oktober 2004 @ 11:19:
[...]


Werkt ook niet, of werkt het alleen als je de records nog sorteerd met ORDER?
Nee, werkt alleen als er geen ' en geen ' staat, en het aan het eind van de query is :)

[ Voor 51% gewijzigd door simon op 16-10-2004 11:20 ]

|>


Acties:
  • 0 Henk 'm!

  • Y0ur1
  • Registratie: Oktober 2000
  • Niet online
Simon schreef op 16 oktober 2004 @ 11:19:
verder zal hij alles tussen de ' en de ' niet uitvoeren. Daarnaast, gebruik mysql_fetch_assoc, wat een array retourneert, die je via $list['veld'] uitleest..


[...]


Nee, werkt alleen als er geen ' en geen ' staat, en het aan het eind van de query is :)
hm ik doe het zo:
$result = database("SELECT * FROM news WHERE news_id='$_GET['news']'");
$result = database("SELECT * FROM news WHERE news_id=$_GET['news']");

en ik kan de tabel op beide manieren niet verwijderen:
index.php?page=news&news=41; DROP TABLE news

Acties:
  • 0 Henk 'm!

  • simon
  • Registratie: Maart 2002
  • Laatst online: 08-09 19:03
Y0ur1 schreef op 16 oktober 2004 @ 11:26:
[...]


hm ik doe het zo:
$result = database("SELECT * FROM news WHERE news_id='$_GET['news']'");
$result = database("SELECT * FROM news WHERE news_id=$_GET['news']");

en ik kan de tabel op beide manieren niet verwijderen:
index.php?page=news&news=41; DROP TABLE news
nee, vrij logisch, die variabele die die get geeft wordt niet uitgevoerd door mysql.

Verder is het netter om "bla" . $_GET['news'] . "bla" te gebruiken, ipv de variabele 'hard' in de string te zetten :)

|>


Acties:
  • 0 Henk 'm!

  • Y0ur1
  • Registratie: Oktober 2000
  • Niet online
Simon schreef op 16 oktober 2004 @ 11:28:
[...]

nee, vrij logisch, die variabele die die get geeft wordt niet uitgevoerd door mysql.

Verder is het netter om "bla" . $_GET['news'] . "bla" te gebruiken, ipv de variabele 'hard' in de string te zetten :)
waarom wordt die dan niet uitgevoerd door de mysql? Ik snap het effe niet helemaal meer...

Acties:
  • 0 Henk 'm!

  • gorgi_19
  • Registratie: Mei 2002
  • Laatst online: 13:29

gorgi_19

Kruimeltjes zijn weer op :9

Topicstarter
Modbreak:Let op: topic is afgesplitst van [rml][ PHP] Link Plaatje laden via SQL *[/rml]

Digitaal onderwijsmateriaal, leermateriaal voor hbo


Acties:
  • 0 Henk 'm!

  • simon
  • Registratie: Maart 2002
  • Laatst online: 08-09 19:03
Y0ur1 schreef op 16 oktober 2004 @ 11:31:
[...]


waarom wordt die dan niet uitgevoerd door de mysql? Ik snap het effe niet helemaal meer...
Doordat je in je WHERE bla = '1' hebt staan lijkt het alsof je het over een string, en geen uitvoerbare code meer hebt. Netter is met intval de integere waarde eruit te halen, en dan die ' en die ' daar achterwege te laten.

|>


Acties:
  • 0 Henk 'm!

  • Y0ur1
  • Registratie: Oktober 2000
  • Niet online
Simon schreef op 16 oktober 2004 @ 11:35:
[...]

Doordat je in je WHERE bla = '1' hebt staan lijkt het alsof je het over een string, en geen uitvoerbare code meer hebt. Netter is met intval de integere waarde eruit te halen, en dan die ' en die ' daar achterwege te laten.
Als we toch een nieuw topic hebben, kun je dan niet effe concreet twee querys geven waarbij het bij de ene wel gaat en bij de andere niet? Want dit is toch wel enigsinds belangrijk om te weten als je met php bezig bent.

[ Voor 3% gewijzigd door Y0ur1 op 16-10-2004 11:45 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Kwestie van
- Bij string: of double quotes en de quotes van je waarde goed escapen
- Bij integer: gebruik sprintf of intval

Zelf geef ik de voorkeur om mijn hele query op te bouwen met sprintf
PHP:
1
sprintf("SELECT veld FROM table WHERE intfield = %d AND stringfield = %s'", $int, addslashes($string));

[ Voor 9% gewijzigd door Verwijderd op 16-10-2004 11:46 . Reden: code tags ]


Acties:
  • 0 Henk 'm!

  • justmental
  • Registratie: April 2000
  • Niet online

justmental

my heart, the beat

Welke mogelijkheden er bij welke query's zijn is niet zo relevant aangezien er universele oplossingen voor zijn.
Zie: P&W FAQ - Hoe beveilig ik een website?

[ Voor 4% gewijzigd door justmental op 16-10-2004 11:46 ]

Who is John Galt?


Acties:
  • 0 Henk 'm!

  • simon
  • Registratie: Maart 2002
  • Laatst online: 08-09 19:03
Y0ur1 schreef op 16 oktober 2004 @ 11:42:
[...]


Als we toch een nieuw topic hebben, kun je dan niet effe concreet en querys geven waarbij het bij de ene wel gaat en bij de andere niet? Want dit is toch wel enigsinds belangrijk om te weten als je met php bezig bent.
Ok, het is vrij simpel, mysql ziet alles wat na een ; komt als een nieuwe query, mits de vorige query eindigt, en de nieuwe query niet als string behandelt wordt.

Dus als je nieuwe injection tussen quotes (') staat, ziet MySQL het als een string. Zo niet, dan is het of een integer, of er kan een nieuwe query komen.

Dus dan doet een kwaadwillend persoon ; en laat de nieuwe query volgen.

MAAR: dan moet de vorige query wel kloppen, dus niet overal werkt het :)

|>


Acties:
  • 0 Henk 'm!

  • Y0ur1
  • Registratie: Oktober 2000
  • Niet online
ok tnx! ik ga effe mee klooien.

Acties:
  • 0 Henk 'm!

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Toevallig kan MySQL niet meerdere SQL-commando's verwerken via de mysql_query-functie, waardoor de voorbeelden met "; DROP TABLE xx" niet werken. Dat maakt het gevaar van SQL-injection voor MySQL echter niet ineens weg, je hebt nog allerlei voorbeelden waar de SQL-injection behoorlijk gevaarlijk kan zijn. Stel je bijvoorbeeld voor dat er een formulier gegeven wordt om je wachtwoord te wijzigen en je userid staat in een hidden-form-field.

Oftewel, je kan hem meegeven via de HTTP-request. Als je dan zo'n query hebt:
PHP:
1
$q = "UPDATE users SET password='" . md5($newpassword) . "' WHERE userid=" . $userid;

Dan wordt het erg interessant om als userid "1234 OR userid=1" mee te geven, waar ik er voor het gemak even van uitga dat de eigenaar van de specifieke site 1 als userid heeft.
Op die manier weet je dus het password van die specifieke beheerder, omdat ie door de SQL-injection aan je eigen password is gelijkgesteld.

Je krijgt tenslotte uiteindelijk de volgende query dan:
PHP:
1
$q = "UPDATE users SET password='md5_van_$newpassword' WHERE userid=1234 OR userid=1";

[ Voor 3% gewijzigd door ACM op 16-10-2004 12:11 ]


Acties:
  • 0 Henk 'm!

  • gorgi_19
  • Registratie: Mei 2002
  • Laatst online: 13:29

gorgi_19

Kruimeltjes zijn weer op :9

Topicstarter
Dan wordt het erg interessant om als userid "1234 OR userid=1" mee te geven, waar ik er voor het gemak even van uitga dat de eigenaar van de specifieke site 1 als userid heeft.
Als je toch de boel finaal wil slopen (en dat gebeurd dan toch wel), zal hij waarschijnlijk eerder iets als
SQL:
1
UPDATE users SET password='md5_van_$newpassword' WHERE userid=1234 OR userid>0

doen. Zo komt hij er in ieder geval in :)

Digitaal onderwijsmateriaal, leermateriaal voor hbo


Acties:
  • 0 Henk 'm!

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Ach, als je het subtiel wilt doen niet. Want een beheerder is vaak langdurig ingelogd, dus die zal het wijzigen van zijn wachtwoord niet zo snel opmerken... Terwijl als je van iedereen het wachtwoord wijzigt, men het gelijk door heeft.

Acties:
  • 0 Henk 'm!

  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
gorgi_19 schreef op 16 oktober 2004 @ 12:14:
SQL:
1
2
UPDATE users SET password='md5_van_$newpassword' WHERE userid=1234 
OR userid>0
code:
1
UPDATE users SET password='md5_van_$newpassword' WHERE userid=1234 OR 1=1

om nog maar es wat te varieeren ;)

[ Voor 23% gewijzigd door Glimi op 16-10-2004 12:25 ]


Acties:
  • 0 Henk 'm!

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Glimi schreef op 16 oktober 2004 @ 12:24:
om nog maar es wat te varieeren ;)
Hij heeft wel als voordeel dat je niet eens de kolomnamen van die tabel hoeft te weten.

Acties:
  • 0 Henk 'm!

  • flowerp
  • Registratie: September 2003
  • Laatst online: 11-09 18:20
Zijn deze SQL injection attacks niet op te vangen door alleen maar van prepared statements gebruik te maken?

Op die manier kunnen de IN parameters die je invoegt ook echt alleen maar values zijn en geen SQL constructs bevatten.

Eventueel kun je daar nog een laagje bovenop bouwen voor als je toch dynamic SQL nodig hebt; bouw je query eerst (dynamisch) op, waarbij al je extra SQL constructs natuurlijk niet uit je request string komt. Voor de IN parameters (placeholders) zet je dan een ? neer in je SQL string. Deze opgebouwde query geeft je eerst op via Connection.prepareStatement(); en vervolgens zet je variablen in volgorde via de API.

Een voorbeeld uit de standaard documentatie:

Java:
1
2
3
4
PreparedStatement pstmt = con.prepareStatement(
      "UPDATE EMPLOYEES SET SALARY = ? WHERE ID = ?");
pstmt.setBigDecimal(1, 153833.00);
pstmt.setInt(2, 110592);


Doordat je de IN parameters op volgorde zet lijkt het een beetje op de sprintf aanpak die hierboven ook al genoemt werd.

De getallen kunnen natuurlijk uit de request string komen. Wat wel een nadeel is is dat dit volgens mij alleen werkt als je DB driver pre-compilation ondersteund. Doet ie dit niet, dan werkt de API wel hetzelfde maar gaat de SQL er alsnog in 1 keer heen. (geen idee of de SQL lib al checked of de parameters alleen values zijn of dat de DB dit pas doet, maar dit is natuurlijk alleen een issue bij setString(), setInt etc is sowiezo safe).

[ Voor 4% gewijzigd door flowerp op 16-10-2004 13:58 ]

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


Acties:
  • 0 Henk 'm!

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Ja, dat is één van de manieren. Helaas is die alleen niet zo goed toepasbaar in PHP. Sowieso ben je in Java en andere strong-typed talen al behoorlijk goed beschermd tegen dit soort grappen als je netjes alles naar het goede type converteert, dan hoef je alleen maar naar je strings te kijken.

't Grootste nadeel van de prepared statements vind ik de administratie van de getalletjes en het feit dat je geen dynamische lijsten in een IN-statement kan stoppen, althans ik heb daar nooit echt iets voor gevonden, zeker niet een collection-based oplossing.

Ook bij de setString wordt de waarde goed geescaped in de query opgenomen, ongeacht of de DB dat pas doet of de client is er gegarandeerd dat dat gebeurt :)

[ Voor 13% gewijzigd door ACM op 16-10-2004 14:08 ]


Acties:
  • 0 Henk 'm!

  • flowerp
  • Registratie: September 2003
  • Laatst online: 11-09 18:20
ACM schreef op 16 oktober 2004 @ 14:07:
Ja, dat is één van de manieren. Helaas is die alleen niet zo goed toepasbaar in PHP.
Hmmm, ok, maar dat is toch niet echt een probleem van de methode zelf? Of anders gezegd, dit is toch niet een PHP topic? Er staat [SQL / MySQL] in de topic title :)
't Grootste nadeel van de prepared statements vind ik de administratie van de getalletjes
Inderdaad ja. Ik snap ook niet dat ze niet meteen voor -ook- een name-based solution zijn gegaan. In een sprintf achtige situatie is het wel handig, maar voorderest is het heel vervelend bij te houden welk getalletje waar voor stond, zeker als dezelfde IN parameter meerdere malen voorkomt en de querie groot is.

Ik zat zelf te denken om een eigen SQL formaat te maken waarbij je namen gebruikt ipv ? icm wat wrapper code om Connection en PreparedStatement heen die de namen naar ? omzet en de positie van de namen bijhoudt zodat bijv een setInt( "naam" , 1) resulteerd in bijv setInt( 1, 1 ) en setInt(2, 1).
en het feit dat je geen dynamische lijsten in een IN-statement kan stoppen, althans ik heb daar nooit echt iets voor gevonden, zeker niet een collection-based oplossing.
Je bedoelt iets als:

SQL:
1
select * from bla where bar in (?);


Ik heb dat nog nooit geprobeerd, je zou zeggen dat het met setString zou kunnen, maar dat kan dus niet?

Als je toch met wrappers bezig bent kun je dat wel dynamisch gaan herschrijven. Bijv, bij 3 parameters die je doorkrijgt via de request string maak je eerst:

SQL:
1
select * from bla where bar =  ? or bar =? or bar = ?;


En deze vul je dan in met iets als setCollection( "mycollection", "1,2,3") waarbij de wrapper code dan bijhoudt wat mycollection is en naar welke nummertjes de 1, 2 & 3 gemapped zijn.

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


Acties:
  • 0 Henk 'm!

  • Y0ur1
  • Registratie: Oktober 2000
  • Niet online
ACM schreef op 16 oktober 2004 @ 12:11:
Toevallig kan MySQL niet meerdere SQL-commando's verwerken via de mysql_query-functie, waardoor de voorbeelden met "; DROP TABLE xx" niet werken. Dat maakt het gevaar van SQL-injection voor MySQL echter niet ineens weg, je hebt nog allerlei voorbeelden waar de SQL-injection behoorlijk gevaarlijk kan zijn. Stel je bijvoorbeeld voor dat er een formulier gegeven wordt om je wachtwoord te wijzigen en je userid staat in een hidden-form-field.

Oftewel, je kan hem meegeven via de HTTP-request. Als je dan zo'n query hebt:
PHP:
1
$q = "UPDATE users SET password='" . md5($newpassword) . "' WHERE userid=" . $userid;

Dan wordt het erg interessant om als userid "1234 OR userid=1" mee te geven, waar ik er voor het gemak even van uitga dat de eigenaar van de specifieke site 1 als userid heeft.
Op die manier weet je dus het password van die specifieke beheerder, omdat ie door de SQL-injection aan je eigen password is gelijkgesteld.

Je krijgt tenslotte uiteindelijk de volgende query dan:
PHP:
1
$q = "UPDATE users SET password='md5_van_$newpassword' WHERE userid=1234 OR userid=1";
Maar je bent ook wel een beetje stom als je het user_id in een hidden field zet en deze zomaar gebruikt in je query, zoiets kun je beter uit de de sessie halen.

@flowerp: het orginele topic ging over php en mijn vraag ook.

[ Voor 7% gewijzigd door Y0ur1 op 16-10-2004 15:52 ]


Acties:
  • 0 Henk 'm!

  • flowerp
  • Registratie: September 2003
  • Laatst online: 11-09 18:20
Y0ur1 schreef op 16 oktober 2004 @ 15:50:

@flowerp: het orginele topic ging over php en mijn vraag ook.
Is dat werkelijk relevant?

Volgens mij staat in de title toch echt nix over PHP. Als jij wilt dat het alleen over PHP gaat moet je even een modje aanschieten dat de title veranderd wordt in:

[SQL / PHP] SQL injecten door php: mogelijkheden en bescherming.

Aangezien dat er nu niet staat ga ik ervan uit dat dit topic over in het algemeen over SQL injection gaat. Of dat nou aan de hand van C++ en CGI is, of Perl, of Java, of PHP. Volgens mij is SQL injection een algemeen probleem. Niet de hele web applicatie wereld is PHP hoor! ;)

It's shocking to find how many people do not believe they can learn, and how many more believe learning to be difficult.


Acties:
  • 0 Henk 'm!

  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
flowerp schreef op 16 oktober 2004 @ 15:46:
Inderdaad ja. Ik snap ook niet dat ze niet meteen voor -ook- een name-based solution zijn gegaan. In een sprintf achtige situatie is het wel handig, maar voorderest is het heel vervelend bij te houden welk getalletje waar voor stond, zeker als dezelfde IN parameter meerdere malen voorkomt en de querie groot is.
Simpelheid neem ik aan. Named solutions geven je collisions en dan moeten daar dan ook weer oplossingen voor gezocht worden dmv escaping. Verder maakt een ? duidelijk dat het een placeholder is een de query en
code:
1
Select id from user where name='name'
heeft dat voordeel niet echt ;)
Je bedoelt iets als:

SQL:
1
select * from bla where bar in (?);


Ik heb dat nog nooit geprobeerd, je zou zeggen dat het met setString zou kunnen, maar dat kan dus niet?
Dat kan heel goed, maar dan wordt:
1) Je string escaped en gequote
2) Je typesafety is weg
Als je toch met wrappers bezig bent kun je dat wel dynamisch gaan herschrijven. Bijv, bij 3 parameters die je doorkrijgt via de request string maak je eerst:
Imho moet er gewoon voor elke setXXX een setXXXs komen, welke een array als type meekrijgt. Ik weet alleen niet in hoeverre dat problemen gaat opleveren met de interne transaltie naar SQL data objecten.
Pagina: 1