Esteba: Probleme bei Suchfunktion wegen Sicherheitsmaßnahmen

Hallo allerseits.

Ich habe ein Problem bei der Umsetzung einer Suchfunktion. In einer MySQL-Datenbank habe ich eine Tabelle, in der Spiele aufgelistet sind. Diese Einträge können von Benutzern in einem Formular hinzugefügt werden. In der Übersicht kann man in einem Textfeld einen Suchstring eingeben, der dann bei Leerzeichen mit explode() in Teilstrings unterteilt wird und jeder Eintrag, in dem einer dieser Teilstrings vorkommt, wird angezeigt.

So sieht das ganze dann aus (das Array $suchbegriffe enthält die Teilstrings):

$sql = "SELECT id FROM spiele\_db WHERE ";
if(!empty($suchstring)) {
  if(count($suchbegriffe) > 1) {
    $sql .= "(";
  }
  $count = 0;
  foreach($suchbegriffe as $begriff) {
    $count++;
    $sql .= "(name LIKE '%".mysql_real_escape_string($begriff)."%')";
    if($count < count($suchbegriffe)) {
      $sql .= " OR ";
    }
  }
  if(count($suchbegriffe) > 1) {
    $sql .= ")";
  }
}

Das Problem stellt jetzt das addslashes() dar, das ich beim Formular zum Hinzufügen von Einträgen anwende, um Cross-Site-Scripting zu verhindern (bzw. mit mysql_real_escape_string wäre es genau so). Dadurch wird zum Beispiel "Baldur's Gate" in der Datenbank als "Baldur's Gate" gespeichert. Mit "Baldur" oder "'s" als Sucheingabe wird der Eintrag gefunden, mit "Baldur's" jedoch nicht, obwohl ja bei den Teilstrings mit mysql_real_escape_string() eigentlich auch Slashes hinzugefügt werden. Der SQL Query wäre damit:
"SELECT id FROM spiele\_db WHERE (name LIKE '%Baldur's%')";

Wie bringe ich die Sicherheitsvorkehrung und die Suche mit LIKE unter einen Hut?

  1. Hello,

    Das Problem stellt jetzt das addslashes() dar, das ich beim Formular zum Hinzufügen von Einträgen anwende, um Cross-Site-Scripting zu verhindern (bzw. mit mysql_real_escape_string wäre es genau so). Dadurch wird zum Beispiel "Baldur's Gate" in der Datenbank als "Baldur's Gate" gespeichert.

    Und genau das ist falsch.
    In der Datenbank sollten nur Roh-Daten gespeichert werden und dazu müssen diese auch so ankommen im Script.

    Vermutlich machen schon die magic quotes Schwierigkeiten. Wenn die eingeschaltet sind
    http://www.php.net/manual/en/function.get-magic-quotes-gpc.php
    dann müssen sie erst wieder entfernt werden.

    Erst anschließend kannst Du mysql_real_escape_string() auf die Daten anwenden, bevor sie an die Textschnittstelle der MySQL-Datenbank übergeben werden.

    Anschließend stehen in der DB nur noch Rohdaten

    Liebe Grüße aus Syburg bei Dortmund

    Tom vom Berg

    --
    Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de
    1. »»» In der Datenbank sollten nur Roh-Daten gespeichert werden und dazu müssen diese auch so ankommen im Script.

      Vermutlich machen schon die magic quotes Schwierigkeiten. Wenn die eingeschaltet sind
      http://www.php.net/manual/en/function.get-magic-quotes-gpc.php
      dann müssen sie erst wieder entfernt werden.

      Erst anschließend kannst Du mysql_real_escape_string() auf die Daten anwenden, bevor sie an die Textschnittstelle der MySQL-Datenbank übergeben werden.

      Anschließend stehen in der DB nur noch Rohdaten

      Vielen Dank, das hat mir jetzt wirklich sehr weitergeholfen. Dass ich mir die Slashes in der Datenbank auch sparen kann, wusste ich noch nicht, wahrscheinlich hat da bei meinen ersten Erfahrungen mit diesen Sicherheitsvorkehrungen auch magic quotes für Verwirrung gesorgt. Das Problem wäre jedenfalls gelöst.

      Eine Frage hätte ich aber noch. Wenn die Sucheingabe des Benutzers ein % oder ein _ enthält, werden in bestimmten Fällen mehr Ergebnisse gefunden als eigentlich sollten, weil diese in dem "LIKE"-Teil ja als Platzhalter für "eine beliebige Zeichenkette" bzw. "ein beliebiges einzelnes Zeichen" interpretiert werden. Wie verhindere ich das und ermögliche gleichzeitig, dass man Prozentzeichen und Underscores bei der Suche findet? Gibts da auch eine Möglichkeit, das irgendwie zu "escapen"?

      1. Hello,

        Vielen Dank, das hat mir jetzt wirklich sehr weitergeholfen.

        Bitte. Gerne geschehen. Es ist auch nicht immer sofort einsichtig, an welcher Stelle man schrauben soll. Sind die Daten aber erst "verkurbelt", kann man das selten wieder rückgängig machen.

        Dass ich mir die Slashes in der Datenbank auch sparen kann, wusste ich noch nicht, wahrscheinlich hat da bei meinen ersten Erfahrungen mit diesen Sicherheitsvorkehrungen auch magic quotes für Verwirrung gesorgt. Das Problem wäre jedenfalls gelöst.

        Eine Frage hätte ich aber noch. Wenn die Sucheingabe des Benutzers ein % oder ein _ enthält, werden in bestimmten Fällen mehr Ergebnisse gefunden als eigentlich sollten, weil diese in dem "LIKE"-Teil ja als Platzhalter für "eine beliebige Zeichenkette" bzw. "ein beliebiges einzelnes Zeichen" interpretiert werden. Wie verhindere ich das und ermögliche gleichzeitig, dass man Prozentzeichen und Underscores bei der Suche findet? Gibts da auch eine Möglichkeit, das irgendwie zu "escapen"?

        Das ist eigentlich recht gut beschrieben unter
        http://dev.mysql.com/doc/refman/5.0/en/string-comparison-functions.html

        Es sind aus Anwendersicht zwei vollkommen verschiedene Suchvorgänge, ob sie eine von Dir vorgefertigte Suche benutzen sollen, die keine Joker zulässt, oder ob Du ihnen die Jokerzeichen (%_) zur Verfügung stellen willst.

        Wenn Du nicht willst, dass der Benutzer selber die Ähnlichkeitssuche verwendet, obwohl Du aber im Hintergrund ein 'LIKE' einsetzt, dann musst Du die Bentuzereingaben escapen. Standardzeichen dafür wäre dann wieder der Backslash. Das kannst Du in Deiner Schnittstelle machen, indem Du jedes Vorkommen von '%' und '_' gegen '%' und '\_' in den Usereingaben austauschst. Vorher solltest Du aber schauen, ob bereits '' enthalten sind, denn manche User sind pfiffig.

        Wenn '' allerdings zum Datentrom gehört, dann musst Du ein anderes Escape-Zeichen wählen und MySQL das durch die Klausel "ESCAPE 'Z'" mitteilen, wobei 'Z' hier für das von Dir gewählte Escape-Zeichen steht.

        Liebe Grüße aus Syburg bei Dortmund

        Tom vom Berg

        --
        Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
        1. Wenn Du nicht willst, dass der Benutzer selber die Ähnlichkeitssuche verwendet, obwohl Du aber im Hintergrund ein 'LIKE' einsetzt, dann musst Du die Bentuzereingaben escapen. Standardzeichen dafür wäre dann wieder der Backslash. Das kannst Du in Deiner Schnittstelle machen, indem Du jedes Vorkommen von '%' und '_' gegen '%' und '\_' in den Usereingaben austauschst. Vorher solltest Du aber schauen, ob bereits '' enthalten sind, denn manche User sind pfiffig.

          Wenn '' allerdings zum Datentrom gehört, dann musst Du ein anderes Escape-Zeichen wählen und MySQL das durch die Klausel "ESCAPE 'Z'" mitteilen, wobei 'Z' hier für das von Dir gewählte Escape-Zeichen steht.

          Ich hab die beiden Zeichen jetzt mal mit str_replace ersetzen lassen. Funktioniert wunderbar, Einträge, die sie enthalten, werden nun auch gefunden. Backslashes sollen in den Einträgen aber auch vorkommen dürfen und ich hab schon selber gesehen, dass das Probleme macht, vor allem bei Kombinationen mit Joker-Zeichen, wie "%".

          Wie löst man sowas denn am elegantesten? Ist es legitim, ein Zeichen als Escape-Zeichen zu wählen, das in der Praxis mit hoher Wahrscheinlichkeit nicht vorkommen wird? Oder gibt es doch eine Möglichkeit, dass die Suche mit allen Zeichenketten gleich funktioniert?

          Danke nochmal für deine Hilfe,
          Esteba

          1. Hello,

            Wie löst man sowas denn am elegantesten? Ist es legitim, ein Zeichen als Escape-Zeichen zu wählen, das in der Praxis mit hoher Wahrscheinlichkeit nicht vorkommen wird? Oder gibt es doch eine Möglichkeit, dass die Suche mit allen Zeichenketten gleich funktioniert?

            Es ist schon legitim, per Maschine ein Escapezeichen zu wählen, dass man per Tastatur üblicherweise nicht eintippen könnte (es sei denn, man kennt die ALT-Sequenz und hat einen entsprechenden transparenten Treiber installiert).

            Unter Wikipedia http://de.wikipedia.org/wiki/Steuerzeichen findest Du eine Übersicht.

            0x1A = Ersetzen     wäre doch prädestiniert dafür.

            Liebe Grüße aus Syburg bei Dortmund

            Tom vom Berg

            --
            Nur selber lernen macht schlau
            http://bergpost.annerschbarrich.de
            1. Es ist schon legitim, per Maschine ein Escapezeichen zu wählen, dass man per Tastatur üblicherweise nicht eintippen könnte (es sei denn, man kennt die ALT-Sequenz und hat einen entsprechenden transparenten Treiber installiert).

              Unter Wikipedia http://de.wikipedia.org/wiki/Steuerzeichen findest Du eine Übersicht.

              0x1A = Ersetzen     wäre doch prädestiniert dafür.

              OK, ich hab es jetzt mal so umgesetzt:

              $ersatz = array(chr(26) => "_", "%" => chr(26)."%", "_" => chr(26)."_");
              foreach($suchbegriffe as $key => $begriff) {
                  $suchbegriffe[$key] = strtr($begriff, $ersatz);
              }

              Das Steuerzeichen "Substitute" (0x1A) ist jetzt also das Escape-Zeichen und sollte der unwahrscheinliche Fall eintreten, dass ein solches im Suchstring vorkommt, wird es einfach durch ein "_" ersetzt. Damit hat der Benutzer zwar theoretisch doch wieder einen Joker zur Verfügung, aber das soll mich nicht weiter stören. ;)

              Funktioniert alles wunderbar. Wenn an dem Code jetzt nichts mehr zu bemängeln sein sollte, sage ich nochmal herzlich danke für die Hilfe und erkläre das Problem damit als gelöst. :)

              Viele Grüße,
              Esteba

              1. Hello,

                Das Steuerzeichen "Substitute" (0x1A) ist jetzt also das Escape-Zeichen und sollte der unwahrscheinliche Fall eintreten, dass ein solches im Suchstring vorkommt, wird es einfach durch ein "_" ersetzt.

                Und wenn jedes "_" im unautorisierten Suchstring durch ein ESCAPE maskiert wird, dürfte doch kein Problem mehr bestehen. Du musst eben nur die Ersetzugnsreihenfolge einhalten. Alle (0x1A) werden zuerst durch ein "_" ersetzt und dann alle "_" und alle "%" durch ein (0x1A) maskiert.

                Danach sollte keine Manipulation der von Dir vorgegebenen Suche mehr möglich sein.

                Liebe Grüße aus Syburg bei Dortmund

                Tom vom Berg

                --
                Nur selber lernen macht schlau
                http://bergpost.annerschbarrich.de