Wissen › SQL-Injection
SQL-Injection
Angreifer-Eingaben verändern die SQL-Abfrage — bis hin zum Auslesen aller Daten (z. B. be_users).
SQL-Injection entsteht, wenn Nutzereingaben als Teil eines SQL-Strings interpretiert werden statt als reine Daten. Angreifer verändern so die Abfrage, lesen fremde Tabellen aus (etwa die Backend-Logins in be_users) und können — je nach Konfiguration — auch schreiben.
Wie der Angriff funktioniert
- Eingabe wird per String-Konkatenation in die Abfrage eingesetzt: der Angreifer schließt mit ' das Literal und hängt eigene SQL an.
- Mit UNION SELECT werden fremde Tabellen angehängt (gleiche Spaltenzahl vorausgesetzt), mit Kommentaren (-- ) wird der Rest der Abfrage verworfen.
- Ob auch geschrieben werden kann, hängt von Treiber (Stacked Queries), Query-API und DB-Rechten ab — nicht von der Injection selbst.
Beispiel
Der Unterschied ist, ob die Eingabe als Daten gebunden oder in den String konkateniert wird:
// verwundbar: Eingabe steckt im SQL-String
$sql = "... WHERE bodytext LIKE '%" . $q . "%'";
// sicher: Parameter-Bindung über den QueryBuilder
$qb->where(
$qb->expr()->like('bodytext',
$qb->createNamedParameter('%' . $qb->escapeLikeWildcards($q) . '%'))
);
Mögliche Auswirkungen
- Auslesen beliebiger Tabellen — inkl. be_users (Benutzername + Passwort-Hash).
- Je nach Setup Schreibzugriffe (INSERT/UPDATE/DELETE) oder Datei-Export.
- Hashes sind offline knackbar; schwache Admin-Passwörter führen zur Backend-Übernahme.
Worauf du in TYPO3 achten musst
TYPO3 bietet mit Doctrine DBAL (QueryBuilder) und Extbase durchgehend parametrisierbare APIs. SQL-Injection in TYPO3-Projekten entsteht praktisch immer durch rohe String-Konkatenation in eigenem Code.
QueryBuilder + createNamedParameter()
Nutzereingaben IMMER über createNamedParameter() binden, nie in den String konkatenieren. Für LIKE zusätzlich escapeLikeWildcards() verwenden.
$qb = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('tt_content');
$qb->select('*')->from('tt_content')
->where($qb->expr()->eq('uid',
$qb->createNamedParameter($uid, \PDO::PARAM_INT))); Bezeichner nicht aus Nutzereingabe
Spalten-/Tabellennamen lassen sich nicht als Parameter binden. Kommen sie aus Eingaben, strikt gegen eine Allowlist prüfen und mit quoteIdentifier() behandeln — niemals direkt einsetzen.
Extbase ist standardmäßig sicher
Repository-Queries über die QueryInterface-API (equals, in, like …) sind parametrisiert. Gefährlich wird erst ->statement() mit zusammengebautem SQL.
Rohe Statements nur mit Bindung
Wenn executeQuery()/executeStatement() auf dem Connection-Objekt nötig ist, Platzhalter + Parameter-Array verwenden — nie Werte in den SQL-String schreiben.
Least Privilege für den DB-User
Der Datenbank-Benutzer der Installation braucht kein FILE-Privileg und keine Rechte außerhalb des TYPO3-Schemas. Das begrenzt den Schaden einer verbliebenen Lücke (z. B. kein INTO OUTFILE).
Schutzmaßnahmen
- Ausschließlich parametrisierte Abfragen (QueryBuilder/Extbase, createNamedParameter).
- Bezeichner aus Eingaben nur per Allowlist; LIKE-Wildcards escapen.
- DB-User mit minimalen Rechten; starke, einzigartige Admin-Passwörter.
Interaktiv üben
In der Lernplattform kannst du diese Schwachstellenklasse an einer echten, isolierten TYPO3-Instanz nachvollziehen: