Fork me on GitHub

Web Development

Ein Lehrbuch für das Informatik oder Medien-Informatik Studium.

Hier das einfachste Programm, das ein neues Werk speichert:

Php Code Einfügen von Daten in die Datenbank - mit Sicherheitsproblem!

$t = $_POST['title'];
$dbh->query("INSERT INTO projects (title) VALUES ('$t')");
// Beispielcode mit Sicherheitslücke - NICHT verwenden!

Aber was passiert wenn ein Werk den Titel „That’s it“ haben soll? Dann wird folgendes SQL-Statement ausgeführt:

INSERT INTO projects (title) VALUES ('That's it')

Das kann nicht funktionieren: das einfache Anführungszeichen beendet den String und es bleibt s it übrig. Die Fehlermeldung von Postgres lautet:

ERROR:  syntax error at or near "s"

Die Falsche Lösung

Für dieses Problem gab es in PHP bis Version 5.3.0 eine einfache und falsche Lösung:

Urspünglich veränderte PHP automatisch alle Daten die über GET, POST und Cookies hereinkommen: Vor alle Anführungszeichen wird ein Backslash eingefügt. Aus „That’s it“ wird also automatisch „That\’s it“, das SQL-Statement funktioniert jetzt für MySQL:

INSERT INTO werk (titel) VALUES ('That\'s it')

Diese Automatik funktioniert aber leider nur für MySQL, nicht aber für Postgres. Da müsste es heissen:

INSERT INTO werk (titel) VALUES ('That''s it')

Wenn Sie eine PHP-Version größer als 5.4.0 verwenden, brauchen Sie sich nicht mehr darum zu kümmern. Bei älteren Versionen sollten Sie die magic_quotes abschalten:

php_flag magic_quotes_gpc off

Die Richtige Lösung

Wenn die magic quotes abgeschalten sind, kann man das Problem besser lösen: mit prepared statements.

Php Code Einfügen von Daten in die Datenbank mit prepared statements


// Variante 1: mySQL + postgreSQL (id-Wert weglassen)
$sth = $dbh->prepare(
  "INSERT INTO users
    (firstname, surname, email, profile_visible)
      VALUES
    (?, ?, ?, ?)");

// Variante 2: nur postgreSQL (mit DEFAULT für den autoincrement id-Wert)
$sth = $dbh->prepare(
  "INSERT INTO users
    (id,  firstname,surname,email,profile_visible)
      VALUES
    (DEFAULT, ?, ?, ?, ?)");

// Variante 3: nur MySQL (mit NULL für den autoincrement id-Wert)
$sth = $dbh->prepare(
  "INSERT INTO users
    (id,  firstname,surname,email,profile_visible)
      VALUES
    (NULL, ?, ?, ?, ?)");

$sth->execute(
  array(
    $_POST['firstname'],
    $_POST['surname'],
    $_POST['email'],
    $_POST['profile_visible']
  )
);

// noch ohne Fehlerbehandlung

Fehlerbehandlung

Beim Einfügen in die Users-Tabelle kann es leicht zu Problemen kommen: Die Tabelle verlangt für manche Spalten eine Eingabe.

So weit wollen Sie es nicht kommen lassen: Sie sollten die Eingaben aus dem Webformular schon vor dem INSERT prüfen und dann ausführliche, vollständige, deutsche Fehlermeldungen ausgeben.

Fehlermeldung der Datenbank vs. selbst gestaltete Fehlermeldung

Falls das Einfügen der Daten funktioniert hat und in der Tabelle ein autoincrement-Feld als Primärschlüssel vorhanden ist, kann man den Wert des Schlüssels im neuen Datensatz mit lastInsertId* auslesen und weiter verwenden.

Php Code Primärschlüssel des neuen Datensatzes auslesen

$id = $dbh->lastInsertId();
header("Location: person.php?id=$id");

Postgres bietet noch eine Zweite Möglichkeit mit RETURNING:

Php Code Primärschlüssel des neuen Datensatzes zurückgeben

$sth = $dbh->prepare("INSERT INTO users (firstname, surname) VALUES (?,?) RETURNING id");
$sth->execute(['Aliya', 'DaSilva']);
$result = $sth->fetch();

echo "I inserted $result->id";

Auch hier ist eine Weiterleitung direkt nach dem POST-Request sinnvoll: Nach dem Einfügen des Datensatzes wird direkt auf die Anzeige des neuen Datensatzes weitergeleitet. Falls man danach auf „Reload“ drückt, wird der Datensatz zwar neu angezeigt, aber so verhindert, dass er ein zweites Mal eingefügt wird.