PRODSouveräne europäische BaaS-PlattformÖffnen Sie das Dashboard →

Native KI · 9 Min. Lesezeit

Sichern Sie NL2SQL gegen LLM-SQL-Injection

Affane Daylami · Fondateur · 16. April 2026

Zurück zum Blog

Ein NL2SQL-Endpunkt wandelt eine Frage in natürlicher Sprache in eine SQL-Abfrage um, dann wird diese Abfrage für Ihre Datenbank ausgeführt. Das Risiko liegt also nicht in der klassischen SQL-Injection, einem schlecht maskierten String in einem Formular: Es ist ein Sprachmodell, das allein entscheidet, welches SQL geschrieben wird. Die höfliche Aufforderung an das Modell, in der Systemeingabeaufforderung nur SELECT zu generieren, verhindert strukturell nichts, es handelt sich um eine Anweisung, nicht um eine Zugriffskontrolle. Die einzige Methode, die funktioniert, besteht darin, das anschließend generierte SQL mit einem Parser zu validieren, der seinen syntaktischen Baum erstellt.

Dieser englische Text wurde automatisch aus dem französischen Original generiert und wurde noch nicht überprüft.
Diese Seite wurde automatisch übersetzt. Maßgeblich ist die englische Version.

In diesem Leitfaden wird die Methode detailliert beschrieben, die tatsächlich funktioniert: strukturelle Beschränkung auf SELECT, geschlossene Whitelist von Funktionen, obligatorische Zeilenbegrenzung und Sperrung des abgefragten Schemas. Jeder Schritt basiert auf dem Validator, der tatsächlich in der NL2SQL-Engine von Aurabase implementiert ist, einer Funktion der nativen KI , die in das Backendintegriert ist, und nicht auf einem nachträglich zusammengestellten Dienst eines Drittanbieters. Wenn das Thema für Sie neu ist, legt unser Überblick über NL2SQL den Grundstein und das Schritt-für-Schritt-Tutorial zeigt, wie Sie den vollständigen Endpunkt erstellen.

Das Wesentliche

  • Prompt Engineering („generiert nur SELECT“) ist keine Sicherheitsüberprüfung: Ein Modell kann halluzinieren, sich von einer mehrdeutigen Frage leiten lassen oder die Anweisungen einfach ignorieren.
  • Die durchgeführte Validierung ist strukturell: Ein Parser erstellt den syntaktischen Baum (AST) der Anfrage und lehnt standardmäßig alles ab, was nicht explizit autorisiert ist.
  • Vier konkrete Schichten begrenzen das Risiko: striktes SELECT (weder Unterabfrage, noch CTE, noch UNION), geschlossene Whitelist mit zehn Funktionen, LIMIT obligatorisch und begrenzt, blockierter Zugriff auf den Systemkatalog und Nicht-Mandanten-Schemata.
  • Das abgefragte Schema muss vom Server stammen, niemals aus einem Feld in der Client-Anfrage: Andernfalls hindert nichts einen Aufrufer daran, sein eigenes Schema bereitzustellen, um die Validierung zu umgehen.
  • Bei Aurabase wird dieser Validator (Rust-Kiste sqlparser) mit kontradiktorischen Fällen getestet, die im Code dokumentiert sind: verbotene Funktionen, die in FILTER, in einem Intra-Aggregat ORDER BYoder in OFFSETversteckt sind.
#
Das eigentliche Problem

Warum blockiert eine Anweisung in der Systemaufforderung nichts?

Eine Systemaufforderung mit der Meldung „Generiert nur SELECT-Abfragen“ ist eine Präferenz und kein Hindernis. Das Modell respektiert es meistens, weil es darauf trainiert wurde, den Anweisungen zu folgen, und nicht, weil eine technische Einschränkung es physisch daran hindert, etwas anderes zu schreiben. Zwei Fehlerklassen führen dazu, dass dieses Vertrauen in die Produktion unzureichend ist.

Das erste ergibt sich aus der Frage selbst. Ein Benutzer, der böse Absichten hat oder einfach nur kreativ in seiner Formulierung ist, kann die Frage so richten, dass das Modell in Richtung SQL verschoben wird, das er nicht hätte schreiben sollen: ein Join mit einer sensiblen Tabelle, ein Filter, der die erwartete Logik umgeht, ein Systemfunktionsaufruf. Das Modell unterscheidet nicht zwischen einer legitimen Frage und einer Frage, die darauf abzielt, sie zu manipulieren.

Das zweite erfordert keine Bosheit. Ein Modell kann einen Tabellennamen halluzinieren, den in der Eingabeaufforderung angeforderten LIMIT vergessen oder einen SELECT * ohne Einschränkungen für eine große Tabelle generieren. Das Ergebnis ist in beiden Fällen dasselbe: potenziell teures oder aufdringliches SQL, das den Eingabeaufforderungsfilter passiert hat und kurz vor der Ausführung gegen eine echte Datenbank steht.

Das Bild, das hilft

Die Systemaufforderung bleibt nützlich, da sie das Modell in den meisten Fällen zum richtigen Ergebnis führt. Aber ein „Zutritt verboten“-Schild hält niemanden davon ab, der nicht lesen kann oder beschließt, es zu ignorieren. Sie brauchen eine geschlossene Tür hinten, nicht nur eine Platte vorne.

#
Schritt 1

Analysieren Sie die generierte SQL in einen Syntaxbaum, niemals in eine Rohzeichenfolge

Die erste Verteidigungslinie besteht darin, das vom Modell erzeugte SQL mit einem echten Parser für den Zieldialekt zu analysieren und dann die resultierende Struktur und nicht den Rohtext zu validieren. Eine Suche nach verbotenen Wörtern in der Zeichenfolge („DROP“, „DELETE“, „;“) wird trivial umgangen: andere Groß-/Kleinschreibung, in der Mitte eines Schlüsselworts eingefügter Kommentar, eingegebene Anführungszeichen. Ein Syntaxbaum beschreibt eindeutig, was die Abfrage tatsächlich tut.

Aurabase implementiert diesen Schritt mit der Rust-Kiste sqlparser und ihrem Dialekt PostgreSqlDialect. Noch vor dem Parsen weist ein erster lexikalischer Filter zwei Konstruktionen zurück, die im Baum nur schwer richtig zu begründen sind: Dollar-Anführungszeichen ($$...$$), die beliebige Inhalte in einer Zeichenfolge verbergen können, und mehrzeilige Kommentare (/* */), die das tatsächliche Ende einer Anweisung verbergen können.

nl2sql/validator.rsrust
let dialect = PostgreSqlDialect {};
let mut statements = Parser::parse_sql(&dialect, sql)
    .map_err(|e| AuraError::BadRequest(format!("Erreur de parsing SQL: {}", e)))?;

if statements.len() > 1 {
    return Err(AuraError::BadRequest("Multi-statements non autorisés".to_string()));
}

Allein diese Ablehnung von Multi-Anweisungen blockiert die bekannteste Form der SQL-Injection durch Stapeln: SELECT * FROM users; DROP TABLE users;--. Der Parser gibt nur eine umsetzbare Anweisung zurück, die zweite wird einfach nie erreicht, egal wie sie in der ursprünglichen Frage formuliert ist.

#
Schritt 2

Beschränken Sie sich strukturell auf ein einfaches SELECT

Sobald der Baum erhalten ist, besteht die umfassendste Validierung darin, nur einen Typ von Stammknoten, eine Abfrage (Statement::Query), zu akzeptieren und alles andere abzulehnen: INSERT, UPDATE, DELETE, DROP, CREATE, ALTER. Es handelt sich nicht mehr um eine sofortige Anweisung, sondern um eine Bedingung für den Typ des analysierten Objekts, die durch keine geschickte Formulierung der Frage umgangen werden kann.

Selbst innerhalb eines SELECT bleiben mehrere Konstrukte gefährlich und verdienen ihre eigene ausdrückliche Ablehnung:

Bau abgelehntWarum ist es gefährlich?
CTE / MITKann zusätzliche unbeabsichtigte Logik vor dem endgültigen SELECT verketten.
Unterabfragen, UNION / INTERSECT / EXCEPTErweitert die Oberfläche dessen, was eine einzelne Frage in einer einzelnen Abfrage bewirken kann.
AUSWÄHLEN...INTOErstellt eine Tabelle: Schreiben als Lesen getarnt.
ZUR AKTUALISIERUNG / ZUM TEILENInstallation von Sperren, Gefahr von Konflikten mit dem Produktionsverkehr.
Tabellenfunktionen (generate_series, pg_read_file...)Systemzugriff oder Denial-of-Service über bei Bedarf generierte Leitungen.

Ein aus dem Repository entnommener Testfall verdeutlicht den letzten Punkt konkret: SELECT * INTO backup FROM users wird abgelehnt, obwohl es weder sichtbares Schreibschlüsselwort noch verdächtige Funktion enthält. Die Form des Antrags reicht aus, um ihn auszuschließen.

#
Schritt 3

Funktion Whitelist, nicht Blacklist

Eine schwarze Liste verbotener Funktionen (pg_sleep, pg_read_file, dblink...) erfordert, jede gefährliche Funktion einzeln zu antizipieren, während Postgres mehrere Hundert davon offenlegt. Eine Whitelist kehrt die Beweislast um: Nur zehn Funktionen sind autorisiert, count, sum, avg, min, max, lower, upper, coalesce, date_trunc, now. Alles andere ist standardmäßig nicht zulässig, einschließlich einer legitimen Funktion, an deren Hinzufügung noch niemand gedacht hat.

Ein einziger Validierungsdurchlauf reicht nicht immer aus. Bei einer strukturellen Durchquerung des Baums werden die Einstiegspunkte nacheinander aufgelistet (Projektion, WHERE, JOIN, GROUP BY...), und einen vergisst man leicht: Eine verbotene Funktion kann in einer FILTER (WHERE pg_sleep(10) IS NOT NULL)-Klausel, in einem aggregatinternen ORDER BY (sum(id ORDER BY pg_sleep(10))), in WITHIN GROUP, DISTINCT ONoder OFFSETversteckt werden.

nl2sql/tests.rsrust
#[test]
fn test_rejette_fonction_interdite_dans_filter() {
    assert!(validate(
        "SELECT count(*) FILTER (WHERE pg_sleep(10) IS NOT NULL) FROM users",
        None, None
    ).is_err());
}

Der Aurabase-Validator fügt daher einen zweiten umfassenden Durchgang hinzu, der alle Ausdrücke im Baum durchläuft, wo immer sie sich befinden, unabhängig vom Strukturpfad. Es handelt sich um eine angenommene Tiefenverteidigung: Wenn der erste Durchgang einen Fall verfehlt, holt der zweite den Fall auf.

#
Schritt 4

Binden Sie die zurückgegebenen Zeilen: LIMIT obligatorisch und begrenzt

SELECT * bleibt autorisiert, es ist nützlich für das Data Mining. Das Risiko ist nicht der Star, sondern das Fehlen einer Obergrenze für eine von einem Modell geschriebene Abfrage: Eine schlecht formulierte Frage kann eine ganze Tabelle zurückbringen, mit den damit verbundenen Speicherkosten und Antwortzeiten.

Aurabase wendet eine einfache und transparente Regel an. Wenn die generierte SQL kein LIMIThat, fügt der Server eine hinzu (standardmäßig 100 Zeilen, Wert wird dem Modell in der Systemeingabeaufforderung mitgeteilt). Wenn die SQL einen LIMIT über eine feste Obergrenze (standardmäßig 1000 Zeilen) hinaus anfordert, wird die Abfrage explizit abgelehnt und nicht stillschweigend reduziert. Beide Werte sind serverseitig konfigurierbar (AI_NL2SQL_DEFAULT_LIMIT, AI_NL2SQL_MAX_LIMIT), und der Server verweigert sogar den Start, wenn der Fehler die Obergrenze überschreitet.

réponse /v1/ai/{project_id}/nl2sql (extrait)json
{
  "sql": "SELECT count(*) FROM orders WHERE status = 'paid' LIMIT 100",
  "limit": 100,
  "limit_injected": true
}
Infos

Eine Ablehnung statt stillschweigender Kürzungen hat ein direktes Interesse: Eine Obergrenze ohne Angabe von Gründen würde beim Anrufer die Illusion erwecken, dass seiner Bitte entsprochen wurde, während das Ergebnis ohne sein Wissen abgeschnitten worden wäre. limit_injected gibt immer an, ob der Wert vom Modell oder vom Server stammt.

#
Schritt 5

Sperren Sie den Zugriff auf das Schema: Systemkatalog und schemaübergreifend

Zwei unterschiedliche Lecks bedrohen eine NL2SQL-Engine, die mit einer echten Datenbank verbunden ist: Zugriff auf den Postgres-Systemkatalog und Zugriff auf ein Schema, das nicht dem Aufrufer gehört. Beide blockieren bei der Validierung, unabhängig von einer nachgeschalteten RLS-Richtlinie.

pg_catalog ist immer Teil von search_path, was bedeutet, dass ein unqualifizierter Name wie pg_authid oder pg_stat_activity direkt und ohne Präfix darauf zugreift. Der Aurabase-Validator blockiert alle Namen, die mit pg_beginnen, sowie information_schema und das interne Schema aura_console, unabhängig davon, ob sie qualifiziert sind oder nicht.

Bei einem zweikomponentigen Namen (schema.table) ist nur das Schema des aufrufenden Projekts zulässig, alle anderen Werte werden abgelehnt. Ein Name mit drei oder mehr Bestandteilen wird automatisch abgelehnt. Diese Grenze auf der Ebene der generierten Abfrage gilt zusätzlich zur Isolation auf Datenbankebene, die in unserem Artikel überMulti-Tenant-Isolationbeschrieben wird: Eine davon verhindert, dass die generierte SQL auf ein anderes Schema abzielt, die andere verhindert, dass die Verbindung selbst eine andere Datenbank erreicht. Keiner ersetzt den anderen.

#
Schritt 6

Lassen Sie niemals zu, dass der Client das abgefragte Schema neu definiert

Eine diskrete Falle wartet auf jede NL2SQL-API, die einen Parameter akzeptiert, der das Schema oder die in der Client-Abfrage zulässigen Tabellen beschreibt. Wenn derselbe Parameter verwendet wird, um die Eingabeaufforderung zu erstellen und die Ausgabe-SQL zu validieren, kann ein Aufrufer darüber lügen, was zulässig ist, und die Validierung validiert dann anhand dieser Lüge und nicht anhand der Realität der Datenbank.

Aurabase überprüft bei jedem Aufruf das tatsächliche Projektbasisschema mit einem kurzen 30-Sekunden-Cache für die Leistung und lehnt alle im Anforderungstext gesendeten Felder schema, allowed_schemaoder schema_context explizit ab (400-Fehler), anstatt sie zu akzeptieren und dann stillschweigend zu überschreiben. Der Unterschied ist wichtig: Ein Feld, das akzeptiert und dann ignoriert wird, vermittelt die Illusion einer Kontrolle, die nicht existiert; Ein abgelehntes Feld sagt es sofort.

#
Checkliste

Prüfen Sie Ihre eigene NL2SQL-Pipeline vor der Produktion

Unabhängig davon, ob Sie Aurabase verwenden oder Ihre eigene Pipeline auf Basis eines generischen LLM aufbauen, decken die folgenden Punkte ab, was am häufigsten übersehen wird.

Wenn Sie den Validator selbst schreiben

  • Analysieren Sie SQL mit einem echten Parser für Ihren genauen Dialekt, niemals mit Mustervergleich in einer Zeichenfolge.
  • Übernehmen Sie eine Standardablehnung: Jeder Knotentyp und jede Funktion, die nicht ausdrücklich autorisiert ist, muss abgelehnt werden, nicht nur die bereits identifizierten gefährlichen Fälle.
  • Akzeptieren Sie nur eine Anweisung pro Abfrage. Dies ist die einfachste Ablehnung von Stapelabfragen.
  • Testen Sie den Validator mit echten kontradiktorischen Fällen (Funktion verboten in FILTER, in einem intra-aggregierten ORDER BY, in OFFSET), nicht nur mit offensichtlichen Fällen.
  • Führen Sie trotz allem das validierte SQL mit einer Postgres-Rolle mit reduzierten Berechtigungen für das erwartete Schema aus: Der Validator begrenzt die Form der Abfrage, die Rolle begrenzt, was sie physisch erreichen kann, wenn Ihnen ein Fall entgangen ist.

Wenn Sie ein NL2SQL-Framework eines Drittanbieters evaluieren

  • Fragen Sie explizit, ob die Validierung strukturell (AST) oder nur eine Eingabeaufforderung ist: Die Antwort ändert alles.
  • Stellen Sie sicher, dass eine Leitungsobergrenze standardmäßig angewendet wird und nicht nur auf Ihre Kosten als Best Practice dokumentiert wird.
  • Prüfen Sie, ob das zur Validierung verwendete Schema vom API-Client bereitgestellt werden kann, wodurch genau der oben beschriebene Fehler erneut geöffnet würde.
  • Vergleichen Sie mehrere Tools nach diesem spezifischen Kriterium, bevor Sie sich entscheiden: Unser -Vergleich der NL2SQL-Tools beschreibt detailliert, was die im Jahr 2026 verfügbaren Ansätze auszeichnet.
#
Verteidigung in der Tiefe

Der Validator reduziert das Risiko, er ersetzt nicht RLS

Ein solider AST-Validator reduziert das Risiko an der Quelle: Das SQL, das Ihre Datenbank erreicht, hat bereits eine bekannte und begrenzte Form. Es ersetzt jedoch nicht die RLS-Richtlinien für Ihre sensiblen Tabellen, die entscheiden, welche Zeilen ein bestimmter Benutzer sehen darf. Die beiden Schichten beantworten unterschiedliche Fragen: Der Validator begrenzt die Form der generierten Abfrage, der RLS begrenzt die Daten, die er für einen bestimmten Benutzer zurückgeben kann. Halten Sie beide aktiv, auch wenn das eine mit dem anderen überflüssig erscheint.

NL2SQL deckt strukturierte Fragen zu Ihren Tabellen ab. Bei Fragen zu unstrukturierten Inhalten, Dokumenten, Notizen und Tickets folgt das native RAG von Aurabase einer vergleichbaren Sicherheitslogik, die in unserem RAG-Pipeline-Tutorial zu pgvectorausführlich beschrieben wird.

#
Häufig gestellte Fragen

FAQs

Ist Prompt Engineering für die Sicherung von NL2SQL völlig nutzlos?+
Nein, es bleibt in den meisten Fällen nützlich, um das Modell auf korrektes und relevantes SQL auszurichten. Aber es handelt sich nicht um eine Sicherheitsüberprüfung: Eine mehrdeutige oder manipulierte Frage kann sie umgehen, und eine prompte Anweisung blockiert nichts physisch. Ein Strukturvalidator bleibt nach der Generierung erforderlich, unabhängig von der Qualität der Eingabeaufforderung.
Sollten wir RLS trotzdem aktivieren, wenn das generierte SQL bereits validiert und auf SELECT beschränkt ist?+
Ja. Der Validator schränkt die Form der Anfrage ein (keine Unterabfrage, keine Nicht-Whitelist-Funktion, begrenztes LIMIT), nicht die Geschäftsrechte an den Daten. Das RLS bleibt die Ebene, die entscheidet, welche Zeilen ein bestimmter Benutzer sehen darf, selbst innerhalb eines vollkommen gültigen SELECT.
Schränkt eine geschlossene Whitelist von zehn Funktionen die möglichen Fragen nicht zu sehr ein?+
Ja, und es ist freiwillig. Es deckt die meisten Leseanalysen ab (Anzahl, Summe, Durchschnitt, Min., Max., Untere, Obere, Koaleszenz, date_trunc, jetzt) ​​und verweigert standardmäßig alles andere, einschließlich einer legitimen Funktion, an deren Hinzufügung noch niemand gedacht hat. Bei jeder Hinzufügung muss es sich um eine ausdrückliche Entscheidung und nicht um ein Versehen auf einer schwarzen Liste handeln.

BEREIT ZUM EINSATZ?

Ihr Backend in fünf Minuten.

Keine Kreditkarte erforderlich · 500 MB kostenlos · 50.000 MAU