Kodokon kodokon.com

Sicherheit: SQL-Injection, Rechte, Prepared Statements

Reproduziere eine SQL-Injection, neutralisiere sie mit Prepared Statements und wende das Prinzip der geringsten Rechte an.

11 Min. · 3 Fragen

Diese Lektion in Kodokon öffnen

Eine SQL-Injection entsteht in dem Moment, in dem nicht vertrauenswürdige Daten in den Text einer Abfrage eingefügt werden. Der Angreifer liefert einen Wert, der die Zeichenkette schließt und die Logik entführt. Reproduzieren wir den Angriff auf eine users-Tabelle: Die Eingabe ada' OR '1'='1 direkt zu verketten, verwandelt einen präzisen Filter in eine stets wahre Bedingung.

SQL
CREATE TABLE users (
  id INTEGER PRIMARY KEY,
  name TEXT NOT NULL,
  is_admin INTEGER NOT NULL DEFAULT 0
);

INSERT INTO users (id, name, is_admin)
VALUES (1, 'ada', 0), (2, 'root', 1);

-- Dangerous concatenation (NEVER do this)
-- input = ada' OR '1'='1
SELECT * FROM users
WHERE name = 'ada' OR '1'='1';
Die eingeschleuste Abfrage gibt ALLE Zeilen zurück.

Die Payload funktioniert, weil das Anführungszeichen das Literal 'ada' vorzeitig schließt, dann fügt OR '1'='1' eine allgemeingültige Bedingung hinzu. Eine Variante hängt -- an, um den Rest der ursprünglichen Abfrage auszukommentieren. Die Lehre: Solange die Daten und der Code sich dieselbe Zeichenkette teilen, ist kein manuelles Escaping wirklich zuverlässig.

SQL
-- Positional placeholder
SELECT * FROM users WHERE name = ?;

-- Named placeholder
SELECT * FROM users WHERE name = :name;
Zwei Formen gebundener Parameter in SQLite.

Das Gegenmittel ist das Prepared Statement: Der Treiber kompiliert zuerst die Struktur der Abfrage und bindet dann die Werte separat. Die gebundenen Daten werden nie als SQL geparst, nur verglichen. Als Bonus ist die kompilierte Struktur wiederverwendbar: Mehrere Werte nacheinander zu binden, vermeidet, den Plan bei jedem Aufruf neu zu kompilieren.

SQL
-- The bound value stays data, not SQL
SELECT * FROM users
WHERE name = :name;
-- :name = ada' OR '1'='1
-- -> 0 rows: no user
--    is literally named that
Dieselbe Payload, jetzt harmlos.

Wissenscheck

Stelle sicher, dass du die wichtigsten Punkte dieser Lektion behalten hast.

  1. Warum verhindert ein Prepared Statement mit Parametern eine SQL-Injection?
    • Weil es die übertragenen Werte verschlüsselt
    • Weil der gebundene Wert als Daten behandelt und nie als SQL geparst wird
    • Weil es die Anführungszeichen in der Zeichenkette automatisch escaped
    • Weil es die Länge der Benutzereingabe begrenzt
  2. Die Payload ' OR '1'='1 gelingt bei der Injection, weil sie...
    • den Speicher des Datenbankservers auslastet
    • das String-Literal schließt und dann eine stets wahre Bedingung hinzufügt
    • die von der Abfrage anvisierte Tabelle löscht
    • die Rechte des aktuellen Benutzers ändert
  3. Kannst du einen Tabellennamen über einen ?-Parameter binden?
    • Ja, Platzhalter akzeptieren jede Art von Bezeichner
    • Nein: Platzhalter binden nur Werte; ein Bezeichner muss gegen eine Whitelist validiert werden
    • Ja, aber nur mit der benannten Form :name
    • Ja, sofern die Tabelle bereits existiert