Rainer: SUchen und ersetzen

Hallo,

beim suchen hole ich aus der Datenbank (MySQL) einen Text welcher incl. HTML-Tags gespeichert ist.
Nun möchte ich im Ergebnis die Sucheingabe farblich markieren.

Geht so - dachte ich:
$z[1] = str_replace("".$_GET["s"]."","<span class=\"searchValue\">".$_GET["s"]."</span>",$z[1]);

Macht es auch nur wenn ich jetzt zum Bsp. nach "Navigation" suche und im Text/Ergebnis git es ein <img src="Navigation.jpg" /> geht es eben SO nicht mehr.
Ich müsste also alles zwischen < und > ausschliessen.

Wie könnte das gehen?

Gruß Rainer

  1. Erledigt...

    falls es jemanden interessiert oder wer was besseres weiss -

    preg_match_all("/\<img(.*)\/>/iUs", $z[1], $matches);  
    			$bx=count($matches[0]);  
    			if($bx > 0){  
    				$b1 = explode("<img",$z[1]);  
    				$z[1] = "$b1[0]";  
    				$z[1] = str_replace("".$_GET["s"]."","<span class=\"searchValue\">".$_GET["s"]."</span>",$z[1]);  
    				for($g = 1; $g <= $bx; $g ++){  
    					$b2 = explode("/>",$b1[$g]);  
    					$z[1] .= "<img $b2[0] />";  
    					$z[1] .= str_replace("".$_GET["s"]."","<span class=\"searchValue\">".$_GET["s"]."</span>",$b2[1]);  
    				}  
    			}  
    			echo"$z[1]";
    
    1. Hallo,

      Erledigt...

      naja, das Grundübel nicht: Es rächt sich meistens irgendwann, wenn man Daten nicht in Reinform in der DB ablegt, in diesem Fall als reinen Text. Kompletten HTML-Code in die DB zu stopfen, ist in den allermeisten Fällen ein Designfehler.

      Aber noch was ...

      $z[1] = "$b1[0]";
      echo"$z[1]";

      wieso fasst du deine Strings nochmal in Anführungszeichen ein und lässt PHP das wieder auseinanderdröseln?

      $z[1] = str_replace("".$_GET["s"]."", ...

      Auch hier sträuben sich die Nackenhaare: Wozu hängst du an den String, den du verarbeitest, vorne und hinten nochmal einen Leerstring an, was den Ausdruck im Endeffekt unverändert lässt?

      $z[1] .= str_replace("".$_GET["s"]."", ...

      Derselbe Unfug nochmal.

      So long,
       Martin

      --
      F: Wer waren die ersten modernen Politiker?
      A: Die Heiligen drei Könige. Sie legten die Arbeit nieder, zogen teure Klamotten an und gingen auf Reisen.
      Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
      1. Tach!

        $z[1] = str\_replace("".$\_GET["s"]."", ...  
        

        Wozu hängst du an den String, den du verarbeitest, vorne und hinten nochmal einen Leerstring an, was den Ausdruck im Endeffekt unverändert lässt?

        Manche notieren das so (also zumindest einen Leerstring am Anfang), weil damit als Nebenwirkung ein Typecast zu String einhergeht. Solche Nebenwirkungen auszunutzen, besonders wenn die dafür notwendige Syntax auf den ersten Blick überflüssig aussieht, kann man nicht gerade als verständliches Programmieren bezeichnen. Ob das in dem Fall bezweckt ist, weiß ich natürlich nicht. Es ist nur eine mögliche Erklärung für solch eine Notation. Allerdings stehen in $_GET und $_POST immer String-Werte *). Ein Typecast zu String bringt keine Änderung. Damit können die Anführungszeichen komplett entfallen und es muss auch kein expliziter Typecast notiert werden.

        *) Die Ausnahme mit den Arrays und dass man selbst andere Datentypen in $_GET/$_POST ablegen kann, lass ich der Einfachheit halber mal unerwähnt.

        dedlfix.

        1. @@dedlfix:

          nuqneH

          … lass ich der Einfachheit halber mal unerwähnt.

          Paradox. Wie willst du das jetzt noch machen? ;-)

          Qapla'

          --
          „Talente finden Lösungen, Genies entdecken Probleme.“ (Hans Krailsheimer)
        2. Hallo,

          $z[1] = str\_replace("".$\_GET["s"]."", ...  
          

          Wozu hängst du an den String, den du verarbeitest, vorne und hinten nochmal einen Leerstring an, was den Ausdruck im Endeffekt unverändert lässt?
          Manche notieren das so (also zumindest einen Leerstring am Anfang), weil damit als Nebenwirkung ein Typecast zu String einhergeht.

          ja, darüber habe ich auch kurz nachgedacht, weil ich solche Kniffe selbst gelegentlich einsetze. Aber die Werte in $_GET sind "von Natur aus" schon Strings, also hielt ich das als Erklärung für unpassend.

          Solche Nebenwirkungen auszunutzen, besonders wenn die dafür notwendige Syntax auf den ersten Blick überflüssig aussieht, kann man nicht gerade als verständliches Programmieren bezeichnen.

          Das stimmt, aber manche dieser Kniffe, mit denen man einen impliziten Typecast erzwingt, sind inzwischen so verbreitet, dass man sie beinahe als allgemein bekannt voraussetzen kann - etwa die Multiplikation mit 1, um eine Interpretation als Number zu erzwingen.

          Allerdings stehen in $_GET und $_POST immer String-Werte *). Ein Typecast zu String bringt keine Änderung. Damit können die Anführungszeichen komplett entfallen und es muss auch kein expliziter Typecast notiert werden.

          Sach ich doch. ;-)

          *) Die Ausnahme mit den Arrays und dass man selbst andere Datentypen in $_GET/$_POST ablegen kann, lass ich der Einfachheit halber mal unerwähnt.

          Daran hätte ich jetzt nicht einmal gedacht. Danke fürs Nicht-Erwähnen.

          Schönes Wochenende,
           Martin

          --
          Datenbanken speichern keine User.
          Das liegt daran, daß Datenbanken mit der Lebensmittelversorgung für gespeicherte biologische Lebensformen derzeit noch Probleme haben.
            (Christoph Schnauß)
          Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
          1. Tach!

            Das stimmt, aber manche dieser Kniffe, mit denen man einen impliziten Typecast erzwingt, sind inzwischen so verbreitet, dass man sie beinahe als allgemein bekannt voraussetzen kann - etwa die Multiplikation mit 1, um eine Interpretation als Number zu erzwingen.

            Und dann gibt es noch Sprachen wie Javascript, die haben keinen Typecast. Um da einen zu erzwingen macht man dann solche Dinge wie !!variable. Also die Negation der Negation ergibt garantiert einen booleschen Wert.

            dedlfix.

            1. Hi,

              Und dann gibt es noch Sprachen wie Javascript, die haben keinen Typecast. Um da einen zu erzwingen macht man dann solche Dinge wie !!variable. Also die Negation der Negation ergibt garantiert einen booleschen Wert.

              kann ich nicht nicht bestätigen. ;-)

              Ciao,
               Martin

              --
              Triple Negative: Nobody ain't gonna blame you for nothing.
              Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
            2. Meine Herren!

              Und dann gibt es noch Sprachen wie Javascript, die haben keinen Typecast. Um da einen zu erzwingen macht man dann solche Dinge wie !!variable. Also die Negation der Negation ergibt garantiert einen booleschen Wert.

              Es gibt (Konstruktor-)Funktionen für primitive Datentypen, also Boolean(), Number() und String(). Die Funktionen nehmen ein Argument beliebigen Typs entgegen, der Rückgabewert hat dann einen entsprechend angepassten Typen. Die Umwandlung erfolgt, insofern ich mich recht erinnere, nach den selben Regeln, die auch bei impliziten Typumwandlung (z.B. durch !!variable) angewendet werden. Statt !!variable, können wir folglich auch Boolean(variable) schreiben. Für die primitiven Typen null und undefined existieren solche Funktionen allerdings nicht, die beiden Brüder verlangen ständig nach einer Extrawurst.

              --
              “All right, then, I'll go to hell.” – Huck Finn
              1. Tach!

                Es gibt (Konstruktor-)Funktionen für primitive Datentypen,

                Stimmt, hab ich doch neulich auch Number() verwendet, weil die parseXXX() in dem Fall nicht anwendbar waren. Allerdings, so erinnere ich mich, sagt auch schon Selfhtml, dass mit den Konstuktorfunktionen nicht mehr ein primitiver Wert sondern ein Objekt erstellt wird und man die Konstruktorfunktionenn für die primitiven Typen zugunsten einer direkt-Notation meiden soll, also nicht ohne Grund zum Beispiel var foo = Boolean(true); sondern var foo = true; nehmen soll.

                dedlfix.

                1. @@dedlfix:

                  nuqneH

                  weil die parseXXX() in dem Fall nicht anwendbar waren.

                  Es war eine Webseite für Kinder?

                  Qapla'

                  --
                  „Talente finden Lösungen, Genies entdecken Probleme.“ (Hans Krailsheimer)
                2. Meine Herren!

                  Es gibt (Konstruktor-)Funktionen für primitive Datentypen,

                  Stimmt, hab ich doch neulich auch Number() verwendet, weil die parseXXX() in dem Fall nicht anwendbar waren.

                  Allerdings, so erinnere ich mich, sagt auch schon Selfhtml, dass mit den Konstuktorfunktionen nicht mehr ein primitiver Wert sondern ein Objekt erstellt

                  Die Funktionen erstellen nur Objekte, wenn sie mit new aufgerufen werden. Das kann zu wirklich absurdem Verhalten führen:

                  if ( new Boolean ( false ) ) {  
                     // Yep, dieser Zweig wird ausgeführt  
                  }  
                    
                  if ( new Boolan ( false ) == true ) {  
                     // Nope, dieser Zweig nicht  
                  }
                  

                  Ohne new besteht diese Gefahr nicht:

                  if ( Boolean( false ) ) {  
                     // Dieser Zweig wird auch nicht ausgeführt.  
                  }
                  

                  Übrigens: Primitive Datentypen, die ab EcmaScript 6 eingeführt werden (zunächst Symbol), kann man nicht mehr mit new instanzieren. Aus Gründen der Abwärtskompatibilität bleibt die Möglichkeit für die bestehenden Primitiven aber bestehen.

                  Allerdings, so erinnere ich mich, sagt auch schon Selfhtml, dass [...] man die Konstruktorfunktionenn für die primitiven Typen zugunsten einer direkt-Notation meiden soll, also nicht ohne Grund zum Beispiel var foo = Boolean(true); sondern var foo = true; nehmen soll.

                  Da stimme ich nur zur Hälfte zu. Wenn man Werte hartcodieren möchte, dann bevorzugt immer in Literal-Schreibweise. Wenn es aber um die Umwandlung von Typen geht, dann bevorzuge ich zu Gunsten der Lesbarkeit explizite Typumwandlung mit Boolean(variable), Number(variable) etc. (ohne new!) vor impliziter Typumwandlung durch !!variable oder 1 * variable.

                  --
                  “All right, then, I'll go to hell.” – Huck Finn
                  1. Tach!

                    Die Funktionen erstellen nur Objekte, wenn sie mit new aufgerufen werden.

                    Ah, den Unterschied kannte ich noch nicht. Nun weiß ich auch, warum die Konsole bei Boolean(true) nur ein einfaches true ausgespuckt hat und kein Objekt. Dann ist alles klar, als Typekonverter ohne new aufrufen geht problemlos und die new-Variante braucht man ansonsten nicht weiter.

                    dedlfix.

    2. @@Rainer:

      nuqneH

      Erledigt...

      Damit meinst du, dein Server ist erledigt. Kann gut sein, denn hiermit reißt du Sicherheitslöcher noch und nöcher:

      echo"$z[1]";

      Du darfst niemals Nutzereingaben ungefiltert in HTML ausgeben!

      Immer mit htmlspecialchars() entschärfen!

      echo htmlspecialchars($z[1]);

      <span class=\"searchValue\">

      Zum Markieren von Suchergebnissen ist das mark-Element da.

      Qapla'

      --
      „Talente finden Lösungen, Genies entdecken Probleme.“ (Hans Krailsheimer)
      1. Damit meinst du, dein Server ist erledigt. Kann gut sein, denn hiermit reißt du Sicherheitslöcher noch und nöcher:

        Nein, sicher nicht. $z[1] ist das Ergebnis der Abfrage aus der Datenbank (fetch_row)
        $_GET["s"] kommt NICHT aus einer Nutzereingabe.

        Den Rest des Threads kann ich mir schenken. Wenn einer eine bessere Lösung gewusst hätte wäre es sinnvoll gewesen diese hier zu posten, aber so...

        Rainer

        1. Hi,

          $_GET["s"] kommt NICHT aus einer Nutzereingabe.

          Aha. Du schreibst also im PHP-Script Werte ins $_GET?

          Denn wenn's vom Browser kommt, kann's vom User manipuliert sein.

          cu,
          Andreas

          --
          Warum nennt sich Andreas hier MudGuard?
          O o ostern ...
          Fachfragen per Mail sind frech, werden ignoriert. Das Forum existiert.
          1. Aha. Du schreibst also im PHP-Script Werte ins $_GET?

            Denn wenn's vom Browser kommt, kann's vom User manipuliert sein.

            Richtig, lässt sich nicht anders machen.

            $_POST["s"] kommt vom User und wird geprüft.
            Wenn ok - durchgereicht, wenn nicht - Abbruch

            Ist schlecht gemacht, aber ich darf es nicht ändern und muss nun versuchen den String aus $_POST["s"] in der Ausgabe aus der DB ($z[1])  farblich zu markieren. Was ich gebaut habe funktioniert insoweit - nur das eben preg_replace leider Case Sensitiv arbeitet. Also brauch ich eine andere Funktion. Nur welche?

            Gruß Rainer

            1. @@Rainer:

              nuqneH

              nur das eben preg_replace leider Case Sensitiv arbeitet.

              Ach ja?

              Die Beschreibung von preg_replace hast du dir aber nicht angesehen? „Es stehen auch einige PCRE-Modifikatoren zur Verfügung …“

              Also brauch ich eine andere Funktion. Nur welche?

              Dass reguläre Ausdrücke zum Parsen von HTML generell untauglich sind, hat lichtheini schon gesagt.

              Qapla'

              --
              „Talente finden Lösungen, Genies entdecken Probleme.“ (Hans Krailsheimer)
              1. Ach ja?

                Die Beschreibung von preg_replace hast du dir aber nicht angesehen? „Es stehen auch einige PCRE-Modifikatoren zur Verfügung …“

                Danke, das hatte ich wirklich glatt übersehen.

                Gruß Rainer

        2. Hallo Rainer,

          Damit meinst du, dein Server ist erledigt. Kann gut sein, denn hiermit reißt du Sicherheitslöcher noch und nöcher:
          Nein, sicher nicht. $z[1] ist das Ergebnis der Abfrage aus der Datenbank (fetch_row)

          das ist schon klar, aber gerade dadurch, dass du HTML-Fragmente anstatt puren Text in deiner DB hast, machst du es hier noch komplizierter. Wie stellst du sicher, dass da nichts drin ist, was die HTML-Ausgabe beim Browser stört?

          $_GET["s"] kommt NICHT aus einer Nutzereingabe.

          Woher denn sonst?? $_GET und $_POST kommen immer vom Nutzer und sind daher immer als potentiell gefährlich, zumindest aber "verseucht" zu betrachten.

          Den Rest des Threads kann ich mir schenken.

          Nein. Es ist typisch, dass Anfänger gutgemeinte Hinweise ignorieren oder für unangebracht halten, weil sie die Tragweite nicht überblicken können. Du tätest trotzdem gut daran, sie anzunehmen.

          Wenn einer eine bessere Lösung gewusst hätte wäre es sinnvoll gewesen diese hier zu posten, aber so...

          Die Lösung besteht im ersten Schritt darin, dein Datenmodell zu überarbeiten.

          Ciao,
           Martin

          --
          Die Natur ist gnädig: Wer viel verspricht, dem schenkt sie zum Ausgleich ein schlechtes Gedächtnis.
          Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
        3. Hallo Rainer,

          man kann das Wissensniveau des Fragestelers nicht immer gut einschätzen, manchmal traut man ihm mehr zu als er kann und manchmal hält man ihn fälschlicherweise für einen Anfänger. In jedem Fall kommen dann für den Fragenden unverständliche Antworten.

          Folgende zwei Dinge wollen dir die Antwortenden sagen:
          Der $_GET-Array wird stumpf mit den Daten gefüllt, die das PHP-Skript bekommt. Ein Angreifer (und davon gibt es genügend), kann den Parameter "s" setzen, ohne auch nur einen Blick in dein HTML-Formular geworfen zu haben. Und das mit Dingen, die man sich als Anfänger (zu denen ich auch gehöre) nicht mal Ansatzweise vorstellen kann. Deswegen sind POST und GET grundsätzlich "böse".

          Zweitens: Regex über beliebeigen HTML-Code geht fast immer schief. Es gibt genügend Konstellationen, denen man nur mit sehr unübersichtlichen Suchmuster Herr wird.
          <img src="/bin/image.php?q=34$&amp;f=img" title="Ein Beispiel für ein <HTML>-Tag">. Dafür haben Browser nicht umsonst einen Parser und keine Regex.
          Wenn dein Code aus der Datenbank in gewisserweise genormt ist, dann kann man vielleicht für diesen speziellen Fall eine Regex basteln. Also zum Beispiel nur betimmte Elemente erlaubt, eine eindeutige DOM-Struktur, alle "Sonderzeichen" entfernt, etc... Wenn es sich um ungefilterten Code für ein Blogpost oder ähnliches handelt, dann lautet die sicherste und zukunftsorientierte Antwort: Passe dein Datenmodell an. Dabei sind die Kollegen hier gerne behilflich.

          Den Rest des Threads kann ich mir schenken. Wenn einer eine bessere Lösung gewusst hätte wäre es sinnvoll gewesen diese hier zu posten, aber so...

          Dazu hat Der Martin schon etwas geschrieben.

          Grüße, der lichtheini

    3. Ja, es wurde ja bereits auf das Wagnis hingewiesen. Eine kompaktere Variante:

      preg_replace() verwenden und mittels sog. Special Backtracking Control Verbs <img...> skippen:

      (?i)<img[^>]*>(*SKIP)(*F)|\bNavigation\b
      (test auf regex101.com)

      Würde auf jeden Fall mögliche Sonderzeichen aus $_GET["s"] entfernen, z.b.:

        
      $input = '<img src="Navigation.jpg" />Navigation<img src="Navigation.jpg" />';  
        
      $_GET["s"] = "Navigation";  
        
      if(isset($_GET["s"]) && ($_GET["s"]!==""))  
      {  
        // sonderzeichen entfernen / wenn input = utf-8: u modifier anhängen ...~u  
        $search = trim(preg_replace('~[^-\'\w]+~', " ", $_GET["s"]));  
        // [^-\'\w] negierte Zeichenklasse; \w = shorthand f. word character [A-Za-z0-9_]  
        // alle Zeichen gegen Leerzeichen tauschen ausser -\'\w  
        
        if(strlen($search) > 0) {  
          $rex = '~(?i)</?\w[^>]*>(*SKIP)(*F)|\b'.$search.'\b~'; // \b = word boundary  
          echo preg_replace($rex, '<strong>\0</strong>', $input);  
        }  
      }  
      
      

      test auf eval.in, RegEx Kurzreferenz