Het voorkomen van SQL injection met PHP

SQL injection is het manipuleren van SQL queries door middel middel van $_POST en $_GET variabelen. Zo kan iemand die gebruik maakt van SQL injection zomaar je hele database legen. Als voorbeeld nemen we een formulier voor het inschrijven van een nieuwsbrief. Het inschrijfformulier heeft maar één veld, namelijk e-mail adres. Hieronder een voorbeeld van hoe het xhtml formulier eruit ziet.

E-mail adres:

We gaan hierbij van de simpelste vorm van inschrijven uit. We controleren niet of het e-mail adres al bestaat en voegen het e-mail adres direct toe aan de tabel users. De SQL query zou er zo uitzien:

mysql_query("
  INSERT INTO users (email)
  VALUES ('" . $_POST['email'] . "')
");

óf

mysql_query("
  INSERT INTO users (email)
  VALUES ('{$_POST['email']}')
");

Wanneer een gebruiker als e-mail adres opgeeft email@domain.ext, dan zal de query als volgt door MySQL worden verwerkt:

INSERT INTO users (email)
VALUES ('email@domain.ext')

Dit is een correcte query en zal geen problemen opleveren. Maar wat als iemand per ongeluk het volgende invoert: em”a’il@domain.ext Dit zal de volgende query opleveren:

INSERT INTO users (email)
VALUES ('em"a'il@domain.ext')

Dit levert een MySQL error op, omdat er een niet afgesloten quote in de query staat. Dit is al een vorm van SQL injection, maar wat als iemand kwade bedoelingen heeft? Stel iemand vult als e-mail email@dom”ain.ext’); DELETE FROM users; in. Dat levert de volgende query op:

INSERT INTO users (email)
VALUES ('email@dom"ain.ext');
DELETE FROM users;

Dit heeft als resultaat dat de de hele users-tabel geleegd wordt. Hier zijn natuurlijk wel oplossing voor. Dat bespreken we in de rest van het artikel.

Let op: we gaan er in dit artikel vanuit dat de php-setting magic_quotes_gpc uit staat op de server. Klik hier om magic_quotes_gpc uit te zetten.

Het voorkomen van SQL injection
Het voorkomen van SQL injection kan met één enkele functie worden opgelost, namelijk mysql_real_escape_string. Een voorbeeld:

$email = mysql_real_escape_string($_POST['email']);
mysql_query("
  INSERT INTO users (email)
  VALUES ('$email')
");

óf

mysql_query("
  INSERT INTO users (email)
  VALUES ('" . mysql_real_escape_string($_POST['email']) . "'
");

Als we nu weer email@domain.ext’); DELETE FROM users; invoegen als e-mail adres, dan resulteert dit in de volgende MySQL query:

INSERT INTO users (email)
VALUES ('email@dom"ain.ext');
DELETE FROM users;');

Het enige verschil is dat ‘ is vervangen door ‘. De ‘ is dus ge-escaped, door middel van een backslash. De ‘ wordt nu niet meer als een enkele quote gezien en daarom is het e-mail adres in de database nu opgeslagen als email@domain.ext’); DELETE FROM users;.

Het is een erg gemakkelijke oplossing, maar het wordt heel vaak vergeten, of weggelaten omdat het teveel tijd kost. Het kost inderdaad meer tijd, maar het bespaart je later zóveel tijd dat het een must is om te gebruiken. In het bovenstaande voorbeeld zou e-mail validatie ook niet een overbodige luxe zijn natuurlijk :)