Was ist SQL Injection und wie verhindere ich sie in C#?
SQL-Injection ist eine Technik zur Code-Injektion, die es Angreifern ermöglicht, über Benutzereingaben bösartigen SQL-Code an Ihren Datenbankserver zu senden. In seinem Video "What Is SQL Injection And How Do I Prevent It in C#?" demonstriert Tim Corey genau, wie SQL-Injection-Schwachstellen in echtem Code auftreten, zeigt mehrere erfolgreiche Beispiele für SQL-Injection-Angriffe (einschließlich unionbasierter und destruktiver Angriffe) und führt praktische Techniken zur Verhinderung von SQL-Injection durch, die Sie in C# anwenden können. Dieser Artikel folgt Tims Lösungsweg, damit Sie genau die Probleme und Lösungen sehen können, die er zeigt.
Demo-App und warum sie wichtig ist
Tim beginnt mit einer kleinen WPF-Demo-Anwendung, die mit einer lokalen InjectableDB (Tabellen People und Secrets) verbunden ist. Das Webformular-ähnliche Suchfeld der App nimmt Benutzereingaben (einen Nachnamen) entgegen und erstellt eine SQL-Abfrage, um ID, Vorname und Nachname zurückzugeben. Es "funktioniert" - geben Sie Corey ein und Sie erhalten Tim Corey - aber Tim betont den Kernpunkt: "Nur weil eine Anwendung funktioniert, bedeutet das nicht, dass sie sicher ist." Eine funktionierende Webanwendung kann immer noch SQL-Injection-Schwachstellen aufweisen, wenn Benutzereingaben über String-Verkettung oder dynamisches SQL direkt in SQL-Anweisungen eingefügt werden.
Der unsichere Code - Stringverkettung und dynamisches SQL
Tim zeigt genau das unsichere Muster, das viele Entwickler verwenden:
var sql = $"SELECT * FROM People WHERE LastName = '{searchText}'";
var results = connection.Query<Person>(sql);
var sql = $"SELECT * FROM People WHERE LastName = '{searchText}'";
var results = connection.Query<Person>(sql);
Diese ursprüngliche Abfrage verwendet String-Verkettung, um eine SQL-Anweisung zu erstellen. Tim warnt: Wenn Sie Code sehen, der Benutzereingaben direkt in SQL-Abfragen einfügt, hören Sie auf - dies ist eine SQL-Injektionsschwachstelle. Angreifer können bösartige Eingaben machen, die die Struktur Ihrer SQL-Befehle ändern oder sogar zusätzliche bösartige SQL-Anweisungen ausführen.
Wie ein Angreifer es ausnutzt - UNION und DROP
Um zu zeigen, wie ein SQL-Injektionsangriff funktioniert, reproduziert Tim Abfragen in SQL Server und bastelt dann Injektionen mit UNION ALL und SQL-Kommentaren (--), um nachgestellte Zeichen zu verbergen. Beispiele für bösartige Nutzdaten, die Tim demonstriert:
-
Union-basierte SQL-Injektion zum Lesen anderer Tabellen:
UNION ALL SELECT ID, Username AS FirstName, Password AS LastName FROM Secrets;Dadurch werden die Ergebnisse von Secrets in die ursprüngliche SELECT-Ergebnismenge gemischt, wodurch sensible Daten wie Benutzernamen und Kennwörter offengelegt werden.
-
Destruktive Injektion zum Löschen von Tabellen:
DROP TABLE DemoTable;Hier wird eine zweite SQL-Anweisung (DROP TABLE) ausgeführt, indem die erste Anweisung mit einem Semikolon beendet und dann der destruktive Befehl hinzugefügt wird. Tim zeigt, dass die Tabelle verschwindet - die Datenbank wurde durch bösartiges SQL verändert.
Tims Argument: Angreifer müssen Tabellen- oder Spaltennamen nicht im Voraus kennen - sie können Tabellen- oder Spaltennamen von Datenbankservern aufzählen oder einfach blinde oder zeitbasierte Techniken ausprobieren, um das Verhalten zu entdecken.
Fix 1 - Parametrisierte Abfragen
Tims erster und wichtigster Schutz besteht darin, keine SQL-Strings mit Benutzerdaten zu erstellen. Ersetzen Sie dynamisches SQL durch parametrisierte Abfragen:
string sql = "SELECT * FROM People WHERE LastName = @LastName";
var results = connection.Query<Person>(sql, new { LastName = searchText });
string sql = "SELECT * FROM People WHERE LastName = @LastName";
var results = connection.Query<Person>(sql, new { LastName = searchText });
Tim erklärt, dass Parametrisierung (Verwendung im Stil von vorbereiteten Anweisungen) bedeutet, dass die Datenbank die vom Benutzer eingegebenen Daten strikt als Daten behandelt - bösartiges SQL wird nur zu einem String-Wert und kann die SQL-Struktur nicht ändern. Dies verhindert viele gängige SQL-Injection-Angriffe, einschließlich unionbasierter Nutzdaten und angehängter ; DROP TABLE-Befehle.
Er empfiehlt außerdem, die Parametrisierung mit einer minimalen Eingabevalidierung zu kombinieren: Bereinigen oder blockieren Sie Zeichen, die in einem Nachnamen unwahrscheinlich sind (z. B. Semikolons oder ---Kommentarmarker), während Sie legitime Zeichen wie Apostrophe zulassen (O'Reilly). Parametrisierte Abfragen und Eingabebereinigung bieten einen erheblichen Schutz gegen SQL-Injection-Angriffe.
Fix 2 - Gespeicherte Prozeduren
Als Nächstes zeigt Tim zwei gespeicherte Prozeduren: eine unsichere gespeicherte Prozedur, die SQL innerhalb der Prozedur verkettet und dann ausführt, und eine sichere gespeicherte Prozedur, die Parameter direkt verwendet.
-
Unsichere gespeicherte Prozedur erstellt einen @sql-String aus dem Parameter und führt ihn aus - immer noch anfällig für Injektionen.
- Safe Stored Procedure führt SELECT ... WHERE LastName = @LastName und wird mit dem Parameter - safe - ausgeführt.
Tim stellt klar: Stored Procedures sind keine automatische Heilung, wenn man in ihnen noch dynamisches SQL erstellt. Bei richtiger Verwendung (kein dynamisches SQL) helfen gespeicherte Prozeduren bei der Zentralisierung von SQL-Anweisungen und erleichtern so die Parametrisierung und Überprüfung von Abfragen. Gespeicherte Prozeduren können auch dazu beitragen, die Vermeidung von SQL-Injektionen in Ihrer Anwendung zu vereinfachen.
Vertrauen Sie keinen Daten - nicht einmal Datenbankdaten
Ein wichtiger, oft übersehener Punkt, auf den Tim hinweist: Sie dürfen Daten, die Sie aus Ihrer eigenen SQL-Datenbank abrufen, nicht blind vertrauen. Angreifer platzieren manchmal bösartige Nutzdaten in Spalten (eine "tickende Zeitbombe"), die später von einem anderen Prozess zu dynamischem SQL verkettet werden. Tim besteht darauf, immer Parameter zu verwenden und Daten bei jedem Schritt zu bereinigen - egal, ob sie aus einem Webformular, einem Datei-Upload oder einer eigenen Datenbank stammen -, damit böswillige Eingaben später nicht als Einfallstor dienen können.
Bonustipp - Least Privilege und Begrenzung der Datenbankrechte
Neben Codekorrekturen empfiehlt Tim eine defensive Konfiguration: Begrenzen Sie die Datenbankprivilegien für Ihre Anwendungskonten. In seiner Demo verwendet die Verbindung ein Administratorkonto über integrierte Sicherheit - gefährlich. Verwenden Sie stattdessen das Prinzip des geringsten Privilegs:
-
Erstellen Sie ein Datenbankkonto für die App mit nur den erforderlichen Rechten.
-
Wenn Sie Stored Procedures verwenden, erteilen Sie diesem Konto nur die EXECUTE-Berechtigung für bestimmte Stored Procedures und nichts anderes.
- Geben Sie Anwendungskonten keine umfassenden Administratorrechte, die DROP TABLE, das Auflisten aller Tabellen oder das Lesen anderer Datenbanken erlauben.
Dadurch werden die Auswirkungen eines erfolgreichen SQL-Injektionsangriffs verringert - selbst wenn eine Injektion möglich ist, kann der Angreifer nicht mehr tun, als dem Konto erlaubt ist.
Tim weist auch darauf hin, dass Entity Framework dies erschwert: EF erfordert oft erhöhte Berechtigungen (Migrationen, Schemaänderungen). Wenn Sie EF in der Produktion verwenden, planen Sie die Berechtigungen und den Einsatz sorgfältig.
Recap - stoppen, parametrisieren, bereinigen, begrenzen
Tim schließt sein Video mit einer klaren Checkliste zur Vermeidung von SQL-Injection in C#-Anwendungen ab:
-
Erstellen Sie keine SQL-Anweisungen mit String-Verkettung oder dynamischem SQL, das Benutzereingaben enthält.
-
Verwenden Sie parametrisierte Abfragen/vorbereitete Anweisungsmuster, damit Benutzerdaten immer als Daten behandelt werden.
-
Bereinigen Sie Eingaben, wo es angebracht ist (Blocksemikolons, SQL-Kommentare, unerwartete Zeichen).
-
Bevorzugen Sie sichere gespeicherte Prozeduren (ohne dynamisches SQL darin) für die Zentralisierung der Abfragelogik.
-
Least Privilege auf Datenbankkonten anwenden - einschränken, was der Datenbankbenutzer der App tun kann.
- Überprüfen Sie Code (insbesondere solche, die SQL dynamisch erstellen) und testen Sie ihn auf SQL-Injection-Fehler.
Tims letzte Warnung: Ein schlampiger Umgang mit Benutzereingaben, dynamischem SQL und übermäßigen Datenbankprivilegien kann zu schwerwiegenden Verstößen führen - zum Abfluss sensibler Daten, zur Zerstörung von Tabellen oder zu einer lang anhaltenden, unentdeckten Exfiltration. Behandeln Sie die Verhinderung von SQL-Injektionen als grundlegende Sicherheitsanforderung und nicht als optionalen Feinschliff.
