SQL-Injections sind der Albtraum eines jeden Webseitenbetreibers. Sie ist definitiv nicht die einzige Gefahr, aber absolut eine der schwerwiegendsten! Doch wie kann sich ein Programmierer gegen einen solchen Angriff wehren?

Die Antwort sind Prepared Statements! Diese sind meiner Meinung nach die Lösung, wenn es um sicheres Einspeichern von Daten in einer Datenbank geht. Doch was sind Prepared Statements genau und wie setzt man sie korrekt ein? Genau darum wird es jetzt gehen!

Was sind Prepared Statements?

Im Gegensatz zum einfachen Weg Daten in Datenbanken abzuspeichern, zu aktualisieren oder abzufragen, werden diese Daten nicht direkt mit der Datenbank in Verbindung gebracht, sondern mit Platzhaltern übergeben.

// UPDATE ohne Prepared Statements
$sql = "UPDATE users SET name = 'Max' WHERE id = 1";

// UPDATE mit Prepared Statements
$stmt = $con -> prepare('UPDATE users SET name = ? WHERE id = ?');

Erst innerhalb des Systems werden diese Platzhalter durch die eigentlichen Werte ersetzt. Dadurch entsteht ein perfekter Schutz gegen Manipulationsversuche von SQL-Anweisungen. Voraussetzung dafür ist allerdings, dass sämtliche Komponenten intern generiert werden. In der Praxis könnte das so aussehen.

Ein Beispiel (UPDATE)

Ich sage immer: „Learning by Doing“! Dementsprechend schauen wir uns das ganze einmal in einem Praxisbeispiel an. Man kann Prepared Statements bei sämtlichen Anwendungsbereichen verwenden. Egal ob man Daten einpflegen, aktualisieren oder abfragen möchte.

In diesem Beispiel geht es mir um den UPDATE-Befehl, mit dem Daten in einer Datenbank aktualisiert werden.

<?php

	/* 
		Vorher wird eine Verbindung aufgebaut.
		Diese Verbindung speichern wir in der Variable $con
	*/

	$stmt = $con -> prepare('UPDATE users SET name = ? WHERE id = ?');

	$name = 'Max';
	$id   = 1;

	if (
  		$stmt &&
  		$stmt -> bind_param('si', $name, $id) &&
  		$stmt -> execute() &&
  		$stmt -> affected_rows === 1
	) {
  		echo "Update erfolgreich!";
	} else {
  		echo $con  -> error;
        echo $stmt -> error;
	}

?>

Schauen wir uns den Code einmal genauer an. Im Voraus wird selbstverständlich eine Verbindung zur Datenbank hergestellt. Dies habe ich jetzt nicht näher ausgeführt, damit ich etwas Platz sparen kann. Die Verbindung selbst wird in der Variable $con gespeichert, welche mehrmals im Code zu finden ist!

Die SQL-Anweisung selbst wird in einer Variable mit dem Namen $stmt gespeichert, welches für Statement steht. Anstatt die Werte direkt an die Datenbank zu übergeben und damit eine Datenextraktion oder Manipulation zu riskieren, haben wir vorerst nur mit Fragezeichen (= ?) gearbeitet, welche unsere Platzhalter darstellen.

Im nächsten Schritt binden wir mit bind_param die vom User festgelegten Werte an die Platzhalter. Dies kann ein einzelner Wert, aber auch – wie in unserem Fall – eine ganze Reihe von Werten sein. Man spezifiziert zuerst den Typen der Werte und bindet diese im Anschluss an die Variablen. Dies haben wir in Zeile 15 erfolgreich getan.

$stmt -> bind_param('si', $name, $id)

Hier kann man zwischen vier verschiedenen Typen wählen. In 99 % der Fälle ist man mit einem String (s) oder einem Integer (i) bestens bedient. Daneben gibt es noch den Double (d) und den BLOB (b). Ein String funktioniert in fast jedem einzelnen Fall.

Im letzten Schritt führen wir mit execute() die Prepared Statements in der Datenbank aus und checken mit affected_rows, ob dies auch erfolgreich war.

Debugging

Einen kleinen Abschnitt möchte ich noch dem Debugging widmen! Für den Fall der Fälle, dass dieser Code nicht so funktioniert, wie es eigentlich geplant ist, sollte man sich immer mit dem Debugging beschäftigen.

Dank mysql_stmt_error findet man in wenigen Sekunden das Problem in seinem Code. In der Bedingung oben im Code überprüfen wir erst, ob das Statement korrekt ausgeführt werden konnte. Im Anschluss begeben wir uns auf Fehlersuche. Hier noch einmal extra aufgeführt.

{
	echo "Update erfolgreich!";
} else {
	echo $con  -> error;
	echo $stmt -> error;
}

Hierbei überprüfen wir zuerst die Probleme, welche auf die Datenbank bezogen sind. Sollte es dort keine Ergebnisse geben und der Fehler im Statement selbst liegen, wird auch dies überprüft.

Fazit

Der Einsatz von Prepared Statements ist meiner Meinung nach in jeder Umgebung Pflicht, in der ein User mit einer Datenbank interagieren und somit potentiell auch manipulieren kann.

Die Vorteile der nützlichen Platzhalter überwiegen in jedem Fall dem Nachteil, dass man sich das Wissen erst aneignen muss. Hierbei handelt es sich um einen äußerst wirkungsvollen Schutz gegen SQL-Injections.

Ich bedanke mich wie immer ganz herzlich für deine Aufmerksamkeit. Bis zum nächsten Mal – Maurice.

Möchtest du immer aktuell bleiben und automatisch über neue Beiträge informiert werden? Dann melde dich doch für meinen kostenfreien Newsletter an!