Julius: Fragwürdiger Schutz einer SQL-Abfrage?

Moin!

Prepared Statements funktionieren naturgemäß nur mit Daten innerhalb von SQL-Anweisungen. Sollen allerdings die SQL-Anweisungen selbst dynamisch auf Basis von übergebenen URL-Parametern zusammengestellt werden, muss man sich anderer Mittel bedienen.

Soll beispielsweise bei folgender Abfrage eine Sortierung vorgenommen werden...

SELECT * FROM users

... bediene ich mich einer Liste von erlaubten Spalten und Sortierreihenfolgen, gegen die geprüft wird und aus denen anshcließend der Teil hinter dem ORDER BY zusammengebaut wird. Bastelt jemand an den URL-Parametern herum, kann nichts passieren.

Nun bin ich auf den Ansatz gestoßen, das mit einem Regex – etwa [a-zA-Z_0-9.]+\s+[adescADESC]+ – abzufangen. Das sieht auf den ersten Blick sicher aus, gefällt mir aber vom Bauchgefühl her nicht, weil es einem möglichen Angreifer viel Spielraum lässt.

Bedenken bereitet mir vor allem, dass ein Angreifer invalides SQL erzeugen kann, auch wenn mir ad hoc nicht einfällt, was ein Angreifer mit etwas wie SELECT * FROM users ORDER BY spaltenname AAA anrichten kann.

An konkreten, ausnutzbaren Szenarien mit gültiger Syntax fallen mir zwei Orakel ein:

  • Ein Angreifer kann herausfinden, welche Spalten die Tabelle in der Datenbank hat. Sortierung nach einer nicht existenten Spalte führt zu einem Fehler bei der Abfrage, Ergebnisse kriegt man im Falle einer nicht existenten Spalte folglich nicht zu Gesicht.
  • Ein Angreifer kann durch Sortieren Werte eingrenzen oder komplett erraten. Bleiben wir beim Beispiel mit der users-Tabelle und von dieser wird nur eine Auswahl an Spalten öffentlich angezeigt. Wenn ich als Angreifer frei die Sortierung bestimmen und mir einen Account anlegen kann, so kann ich durch das Sortieren herausfinden, welche E-Mail-Adresse ein anderer Nutzer hinterlegt hat, indem ich so lange meine E-Mail-Adresse (oder etwas Vergleichbares) anpasse und die Reihenfolge der Sortierung mit dem zu erratenden E-Mail-Adresse beobachte, bis ich den Wert erraten oder die Sortierung sich nicht mehr ändert.

Außerdem kann ein Angreifer vielleicht die Datenbank stressen, indem er schwer sortierbare Spalten sortiert – das ist purer ein Schuss ins Blaue, ich könnte mir nur vorstellen, dass es so etwas geben könnte, aber vielleicht regelt das schlicht der Index...

Gruß
Julius

akzeptierte Antworten

  1. Hallo Julius,

    als ersten Schritt könnte man die Regex zu

    /([a-z0-9_]+\.)?[a-z0-9_]+\s+(asc|desc)/i
    

    ändern, d.h.

    • case insensitive
    • explizite Festlegung eines Table-Aliasnamens als Column-Prefix
    • asc und desc als einzige Optionen

    Ich würde das aber nicht tun. Nicht wegen der möglichen SQL Errors. Aber die

    Liste von erlaubten Spalten und Sortierreihenfolgen

    ist besser. Bleib dabei. Denn

    Ein Angreifer kann herausfinden, welche Spalten die Tabelle in der Datenbank hat

    Diese Erkenntnis kann Basis für andere (hoffentlich gut abgefangene) SQL Injections sein.

    Es gibt noch einen weiteren Grund: Je nach Anforderung kann es nötig sein, nach einer Expression zu sortieren. Das kannst Du auf diese Weise nicht realisieren.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf,

      vielen Dank für deine Rückmeldung!

      Ich würde das aber nicht tun. Nicht wegen der möglichen SQL Errors. Aber die

      Liste von erlaubten Spalten und Sortierreihenfolgen

      ist besser. Bleib dabei.

      Das war sowieso mein Plan. Die Intention hinter meiner Frage war, die Argumente zu prüfen und zu schärfen, denn ich versuche gerade gewisse Leute davon zu überzeugen, dass sie eben dieses Vorgehen mit dem Regex nicht empfehlen sollen. Und mit meinem Bauchgefühl zu argumentieren, zieht nicht wirklich.

      Gruß
      Julius

  2. Tach!

    Soll beispielsweise bei folgender Abfrage eine Sortierung vorgenommen werden...

    SELECT * FROM users
    

    ... bediene ich mich einer Liste von erlaubten Spalten und Sortierreihenfolgen, gegen die geprüft wird und aus denen anshcließend der Teil hinter dem ORDER BY zusammengebaut wird.

    Alternativ kann man auch nach der Position in der SELECT-Klausel sortieren. Dann wird über den Client lediglich eine Zahl geschleift und nicht ein Spaltenname oder ein Alias.

    dedlfix.

    1. Hallo dedlfix,

      Soll beispielsweise bei folgender Abfrage eine Sortierung vorgenommen werden...

      SELECT * FROM users
      

      Alternativ kann man auch nach der Position in der SELECT-Klausel sortieren. Dann wird über den Client lediglich eine Zahl geschleift und nicht ein Spaltenname oder ein Alias.

      Das geht sogar bei SELECT *. Dann zählt die Reihenfolge der Spalten in der Datenbank. Die Zählung beginnt bei 1.

      Bis demnächst
      Matthias

      --
      Du kannst das Projekt SELFHTML unterstützen,
      indem du bei Amazon-Einkäufen Amazon smile (Was ist das?) nutzt.
    2. Hallo dedlfix,

      Soll beispielsweise bei folgender Abfrage eine Sortierung vorgenommen werden...

      SELECT * FROM users
      

      ... bediene ich mich einer Liste von erlaubten Spalten und Sortierreihenfolgen, gegen die geprüft wird und aus denen anshcließend der Teil hinter dem ORDER BY zusammengebaut wird.

      Alternativ kann man auch nach der Position in der SELECT-Klausel sortieren. Dann wird über den Client lediglich eine Zahl geschleift und nicht ein Spaltenname oder ein Alias.

      Interessant, das kannte ich noch nicht. Der Vorteil bei der Validierung wäre dann, dass man nur auf einen bestimmten Zahlenbereich prüfen müsste und niemand nach Spalten sortieren kann, die ich nicht im SELECT auswähle. Gut zu wissen, Danke!

      Gruß
      Julius

  3. Nun bin ich auf den Ansatz gestoßen, das mit einem Regex – etwa [a-zA-Z_0-9.]+\s+[adescADESC]+ – abzufangen.

    Das mag auf manchen „elegant“ wirken, ist es aber nicht.

    Grund:

    Für Dich (oder einen nachfolgenden Programmierer) wird bei einem Regex schwerer erkennbar, was denn erlaubt sein soll. Bei der vermeintlich „stumpfen“ Methode der Notierung erlaubter konkreter Werte ist das sofort und jederzeit sichtbar.

    Fazit:

    Bleibe bei

    ... bediene ich mich einer Liste von erlaubten Spalten und Sortierreihenfolgen, gegen die geprüft wird.

    denn das ist einfach einfacher.

    Auch der Umstand, dass ggf. nach berechneten Werten sortiert werden soll ändert daran nichts, denn diese kann man z.B. in eine View aufnehmen und diese View „befragen“ oder halt mit etwas wie SELECT (a+b)*c AS sortVal in der Abfrage notieren und sortVal als Sortierung erlauben...

    1. Hallo Raketensimplizissimus,

      Nun bin ich auf den Ansatz gestoßen, das mit einem Regex – etwa [a-zA-Z_0-9.]+\s+[adescADESC]+ – abzufangen.

      Das mag auf manchen „elegant“ wirken, ist es aber nicht.

      Grund:

      Für Dich (oder einen nachfolgenden Programmierer) wird bei einem Regex schwerer erkennbar, was denn erlaubt sein soll. Bei der vermeintlich „stumpfen“ Methode der Notierung erlaubter konkreter Werte ist das sofort und jederzeit sichtbar.

      Das ist tatsächlich auch ein gewichtiges Argument. Vor allem dürfte dann jemand das ganze erweitern, übersieht aufgrund der Komplexität und dann knallt es – sofern das das Bestellformular bei einer Silvesterfeuerwerksraketenmanufaktur ist, sogar ganz ordentlich.

      Bleibe bei

      ... bediene ich mich einer Liste von erlaubten Spalten und Sortierreihenfolgen, gegen die geprüft wird.

      Das war eh mein Plan. Ich muss andere überzeugen, die die Probleme nicht sehen 🙃.

      Gruß
      Julius