🛡️SQL-Injection und Prepared Statements

Werden Benutzereingaben direkt in SQL-Text eingesetzt, kann ein Angreifer die Abfrage umschreiben. Die Demo läuft vollständig im Browser gegen eine erfundene Tabelle – die Passwörter stehen nur zur Veranschaulichung im Klartext.

Richtige Daten: genau eine Zeile – in beiden Varianten.

❌ Unsicher: Text zusammenkleben

sql = "SELECT … WHERE benutzer = '" + benutzer + "' AND passwort = '" + passwort + "';"
SELECT id, benutzer, rolle FROM konten WHERE benutzer = 'anna' AND passwort = 'beispiel-passwort-anna';

Rot: was aus der Eingabe stammt. Die Datenbank kann nicht unterscheiden, was Daten und was Befehl ist.

✅ Sicher: Prepared Statement

stmt = db.prepare("SELECT id, benutzer, rolle FROM konten WHERE benutzer = ? AND passwort = ?;")
stmt.bind([benutzer, passwort])
SELECT id, benutzer, rolle FROM konten WHERE benutzer = ? AND passwort = ?;
? ← "anna"
? ← "beispiel-passwort-anna"

Der SQL-Text steht fest, bevor die Werte ankommen. Die Werte werden nur noch als Daten verglichen – ein Anführungszeichen ist dann einfach ein Zeichen.

🧰Prepared Statements in verschiedenen Sprachen

Platzhalter heißen je nach Treiber ?, :name oder $1 – das Prinzip ist immer gleich.

Python (sqlite3)

cur.execute(
    "SELECT id FROM konten WHERE benutzer = ? AND passwort_hash = ?",
    (benutzer, passwort_hash),
)

Java (JDBC)

PreparedStatement ps = con.prepareStatement(
    "SELECT id FROM konten WHERE benutzer = ? AND passwort_hash = ?");
ps.setString(1, benutzer);
ps.setString(2, passwortHash);
ResultSet rs = ps.executeQuery();

PHP (PDO)

$st = $pdo->prepare(
    'SELECT id FROM konten WHERE benutzer = :b AND passwort_hash = :h');
$st->execute(['b' => $benutzer, 'h' => $hash]);

Node.js (pg)

await client.query(
  "SELECT id FROM konten WHERE benutzer = $1 AND passwort_hash = $2",
  [benutzer, passwortHash],
);

📌Weitere Schutzschichten

⚠️ Platzhalter gibt es nur für Werte
Tabellen- und Spaltennamen (z. B. eine wählbare Sortierspalte) lassen sich nicht binden. Dann gegen eine feste Liste erlaubter Namen prüfen (Allowlist).
💡 Minimale Rechte
Die Anwendung verbindet sich mit einem Benutzer, der nur darf, was sie braucht (GRANT SELECT, INSERT … auf bestimmte Tabellen) – kein DROP, kein Zugriff auf andere Schemata.
✅ Passwörter nie speichern
Gespeichert wird ein langsamer, gesalzener Hash (Argon2id, bcrypt, scrypt). Der Vergleich passiert in der Anwendung, nicht per SQL-Gleichheit auf dem Klartext.