Back

Ein Leitfaden für Einsteiger zu SQL Injection (Und wie man sie verhindert)

Ein Leitfaden für Einsteiger zu SQL Injection (Und wie man sie verhindert)

Sie entwickeln eine Suchfunktion für Ihre App. Benutzer geben einen Produktnamen ein, Ihr Backend fragt die Datenbank ab und Ergebnisse werden angezeigt. Einfach genug – bis jemand '; DROP TABLE products;-- in das Suchfeld eingibt.

Das ist SQL Injection, und sie bleibt eine der gefährlichsten Schwachstellen in der Webentwicklung. Die OWASP Top 10 führen „Injection” als kritisches Sicherheitsrisiko auf, und SQL Injection steht im Zentrum dieser Kategorie. Wenn Sie Code schreiben, der eine Datenbank berührt – auch nur gelegentlich – müssen Sie verstehen, wie dieser Angriff funktioniert und wie man ihn verhindert.

Wichtigste Erkenntnisse

  • SQL Injection tritt auf, wenn Angreifer Datenbankabfragen manipulieren, indem sie schädlichen Code über Benutzereingaben einschleusen
  • Parametrisierte Abfragen (Prepared Statements) sind die primäre Verteidigung und trennen SQL-Code von Daten
  • ORMs sind nicht automatisch sicher, wenn Raw-Query-Methoden verwendet werden
  • Verwenden Sie Allowlists für dynamische Bezeichner wie Tabellen- und Spaltennamen, die nicht parametrisiert werden können
  • Wenden Sie Defense in Depth an: Konten mit minimalen Berechtigungen, zentralisierte Datenzugriffsschichten und Sicherheitstests in CI-Pipelines

Was ist SQL Injection?

SQL Injection tritt auf, wenn ein Angreifer die Datenbankabfragen Ihrer Anwendung manipuliert, indem er schädlichen Code über Benutzereingaben einschleust. Anstatt die Eingabe als Daten zu behandeln, führt die Datenbank sie als Befehle aus.

Betrachten Sie ein Login-Formular. Ihr Backend könnte eine Abfrage wie diese konstruieren:

SELECT * FROM users WHERE email = 'user@example.com' AND password = 'secret123'

Wenn Sie diese Abfrage durch Verkettung von Strings mit Benutzereingaben erstellen, kann ein Angreifer ' OR '1'='1 als Passwort eingeben. Die resultierende Abfrage wird zu:

SELECT * FROM users WHERE email = 'user@example.com' AND password = '' OR '1'='1'

Da '1'='1' immer wahr ist, gibt die Abfrage alle Benutzer zurück und umgeht die Authentifizierung vollständig.

Warum SQL Injection immer noch wichtig ist

Sie könnten annehmen, dass moderne Frameworks dieses Problem gelöst haben. Das haben sie nicht – zumindest nicht automatisch.

SQL-Injection-Schwachstellen treten auf in:

  • API-Endpunkten, die JSON mit Filterparametern akzeptieren
  • Suchformularen mit mehreren Abfrageoptionen
  • Admin-Dashboards mit dynamischer Sortierung oder Filterung
  • Report-Buildern mit benutzerdefinierten Kriterien

Jeder Ort, an dem Benutzereingaben eine Datenbankabfrage beeinflussen, ist eine potenzielle Angriffsfläche. Die Konsequenzen reichen von Datendiebstahl bis zur vollständigen Zerstörung der Datenbank.

Wie unsichere Abfragekonstruktion entsteht

Die Grundursache ist immer dieselbe: Benutzereingaben werden als vertrauenswürdiger SQL-Code behandelt und nicht als Daten.

String-Verkettung ist der Hauptverursacher:

// Verwundbar - niemals so machen
const query = `SELECT * FROM products WHERE name = '${userInput}'`;

Template-Literale und String-Formatierung erzeugen dasselbe Problem:

# Verwundbar - niemals so machen
query = f"SELECT * FROM products WHERE category = '{category}'"

Selbst ORMs sind nicht automatisch sicher, wenn Sie Raw-Query-Methoden verwenden:

// Verwundbar - Raw Queries umgehen ORM-Schutzmaßnahmen
db.query(`SELECT * FROM users WHERE id = ${req.params.id}`);

Verhinderung von SQL Injection mit parametrisierten Abfragen

Die primäre Verteidigung gegen SQL Injection ist die Verwendung von parametrisierten Abfragen (auch Prepared Statements genannt). Diese trennen SQL-Code von Daten und stellen sicher, dass Benutzereingaben niemals als Befehle ausgeführt werden.

Hier ist der sichere Ansatz in JavaScript:

// Sicher - parametrisierte Abfrage
const query = 'SELECT * FROM products WHERE name = ?';
db.query(query, [userInput]);

Und in Python:

# Sicher - parametrisierte Abfrage
cursor.execute("SELECT * FROM products WHERE name = %s", (user_input,))

Die Datenbank behandelt den Parameter als literalen Wert, nicht als SQL-Code. Selbst wenn jemand '; DROP TABLE products;-- eingibt, sucht die Datenbank nach einem Produkt mit genau diesem String als Namen.

Was parametrisierte Abfragen nicht abdecken

Bezeichner (Tabellennamen, Spaltennamen) können nicht parametrisiert werden. Wenn Sie dynamische Sortierung benötigen:

// Verwenden Sie eine Allowlist für Spaltennamen
const allowedColumns = ['name', 'price', 'created_at'];
const sortColumn = allowedColumns.includes(userInput) ? userInput : 'name';
const query = `SELECT * FROM products ORDER BY ${sortColumn}`;

Stored Procedures sind nur sicher, wenn sie intern kein dynamisches SQL verwenden. Eine Stored Procedure, die Strings verkettet, ist genauso verwundbar.

Warum Escaping nicht ausreicht

Manuelles String-Escaping und Sanitization sind allein nicht ausreichend. Sie sind fehleranfällig, datenbankspezifisch und leicht falsch zu implementieren. Parametrisierte Abfragen handhaben das Escaping automatisch und korrekt.

Defense in Depth

Parametrisierte Abfragen sind Ihre primäre Verteidigung, aber zusätzliche Schichten helfen:

  • Datenbankkonten mit minimalen Berechtigungen: Der Datenbankbenutzer Ihrer App sollte in der Produktion keine administrativen Berechtigungen wie DROP, ALTER oder CREATE USER haben
  • Zentralisierte Datenzugriffsschicht: Leiten Sie alle Abfragen durch ein einzelnes Modul, das Parametrisierung erzwingt
  • Code-Review und Testing: Integrieren Sie SQL-Injection-Prüfungen in Ihren Review-Prozess und Ihre CI-Pipeline

Moderne Leitlinien von OWASP ASVS und CISAs „Secure by Design”-Initiative betonen, Sicherheit in Ihren Entwicklungsprozess einzubauen, anstatt sie nachträglich hinzuzufügen.

Fazit

SQL Injection bleibt gefährlich, weil sie leicht einzuführen und verheerend ist, wenn sie ausgenutzt wird. Die Lösung ist unkompliziert: Verwenden Sie parametrisierte Abfragen für alle Datenbankoperationen, validieren Sie Bezeichner mit Allowlists und vertrauen Sie niemals Benutzereingaben.

Machen Sie parametrisierte Abfragen zu Ihrem Standard. Ihr zukünftiges Ich – und Ihre Benutzer – werden es Ihnen danken.

Häufig gestellte Fragen

Ja. NoSQL Injection ist eine verwandte Schwachstelle, die Datenbanken wie MongoDB betrifft. Angreifer können Query-Operatoren über Benutzereingaben manipulieren. Die Verteidigung ist ähnlich: Verwenden Sie die integrierten Query-Methoden des Datenbanktreibers, anstatt Abfragen aus Strings zu konstruieren, und validieren Sie alle Benutzereingaben, bevor Sie sie in Abfragen verwenden.

ORMs bieten Schutz, wenn Sie ihre Standard-Query-Methoden verwenden. Die meisten ORMs bieten jedoch Raw-Query-Funktionen, die diese Schutzmaßnahmen umgehen. Wenn Sie Raw-SQL-Methoden oder String-Interpolation innerhalb von ORM-Abfragen verwenden, sind Sie immer noch verwundbar. Verwenden Sie immer parametrisierte Methoden, auch innerhalb von ORM-Code.

Verwenden Sie automatisierte Tools wie SQLMap oder OWASP ZAP, um nach Schwachstellen zu scannen. Fügen Sie sicherheitsorientierte Unit-Tests hinzu, die Injection-Payloads versuchen. Führen Sie Code-Reviews durch, die speziell nach String-Verkettung in Abfragen suchen. Erwägen Sie Penetrationstests für kritische Anwendungen vor dem Produktionseinsatz.

Nein. Eingabevalidierung hilft, die Angriffsfläche zu reduzieren, sollte aber niemals Ihre einzige Verteidigung sein. Clevere Angreifer können oft Validierungsregeln umgehen. Parametrisierte Abfragen sind die primäre Verteidigung, weil sie grundlegend Code von Daten trennen. Verwenden Sie Validierung als zusätzliche Schicht, nicht als Ersatz für ordnungsgemäße Abfragekonstruktion.

Gain control over your UX

See how users are using your site as if you were sitting next to them, learn and iterate faster with OpenReplay. — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay