Donnerstag, 19. März 2015

Sicherer Zugriff auf MySQL Datenbanken mit PHP

In einem vorigen Post habe ich erklärt, wie man mittels PHP auf eine MySQL Datenbank zugreift.
Dort habe ich eine einfache bzw. veraltete Variante zur Demonstration gewählt, nämlich die Klasse mysql. Mittlerweile sollte man die Klasse mysqli wählen, das i steht für improved.
Insbesondere möchte ich in diesem Post eine neue, wichtige Funktion von mysqli vorstellen, nämlich die Möglichkeit sogenannte Prepared Statements zu verwenden.
Die Methode aus dem vorigen Post zur Kommunikation mit der SQL Datenbank lässt nämlich eine gravierenden Attacke zu: Eine SQL-Injection ist möglich. Dieses bezeichnet das Einschleußen von Code auf dem Server. Warum? Nun, schauen wir uns die Kommunikation mit der Datenbank an: Wir haben die POST Parameter im Skript verwendet, um zum Beispiel den eingegebenen Benutzernamen abzufragen und in die Datenbank zu schreiben. Ein Angreifer könnte nun aber zum Beispiel in den Benutzernamen ein Semikolon, Anführungszeichen etc. einfügen, so die Abfrage schließen und im Benutzernamen weiter eine neue Abfrage / Anweisung stellen, welche dann auch ausgeführt würde, da für die Datenbank einfach 2 Abfragen ankommen!
Um dies zu verhindern, bzw. zu versuchen zu verhindern (100% Sicherheit kann nie garantiert werden), könnte man zum Beispiel versuchen, per Hand in den ausgeführten SQL Abfragen Sonderzeichen wie ; zu escapen und so die Attacke zu verhindern. Ich werde hier aber eine höhere Abstraktionsebene wählen und eine existierende Methode der Klasse mysqli verwenden, welche dies für uns erledigt. Das Stichwort sind Prepared Statements, die quasi eine Abfrage vorbereiten und die "sauberen" Parameter an diese binden. PreparedStatements bieten sehr guten Schutz gegen SQL-Injections.
Das Grundprinzip eines Prepared Statements ist das folgende:
Zuerst erstellen wir uns eine Verbindungsvariable:

$conn = new mysqli(server, username, password, database);

Dann benutzen wir diese zur Abfrage:

$stmt = $conn->prepare("query ... ?");
$stmt->bind_param("s...", params ...);
$stmt->execute();

In der 1. Zeile bereiten wir die Anfrage vor, in der Anfrage ersetzen wir dabei die Parameter durch ein ?. In der 2. Zeile binden wir die Parameter an diese Platzhalter, die erste Zeichenkette gibt den Typ an. s steht für String, i für Integer und d für Double. Die nächsten Argumente sind dann die Parameter. Schließlich führen wir die Anweisung aus.

Um eine Abfrage zu stellen und die Antwort auszulesen, führen wir auch zuerst die obigen 3 Zeilen aus. Die Antwort können wir dann mit folgender Struktur abgreifen:

$stmt->bind_result(params);
while($row = $stmt->fetch())
   {
echo params .... ;
$stmt->bind_result($sender, $message);
   }

Ich möchte nun das Skript des vorigen Posts zu MySQL mit mysqli umschreiben, ich denke, das Prinzip sollte dann verständlich sein:

<?php
$FirstName = $_POST["FirstName"];
$LastName = $_POST["LastName"];

$conn = new mysqli("db4free.net", "csharptricks", "12345678", "csharptricks");

$stmt = $conn->prepare("INSERT INTO Customers (FirstName, LastName) VALUES (?, ?);");
$stmt->bind_param("ss", $FirstName, $LastName);
$stmt->execute();
  
$stmt = $conn->prepare("SELECT FirstName, LastName FROM Customers;");
$stmt->execute();
$stmt->bind_result($output1, $output2);
while($row = $stmt->fetch())
   {
          echo "$output1<br />";
          echo "$output2<br />";
          $stmt->bind_result($output1, $output2);
   }
?>

Keine Kommentare:

Kommentar veröffentlichen