michael: inhalt aus DB enthält php-code

Hallo,

ich habe ein winziges CMS geschrieben, welches zur Lagerung der Inhalte ein MySQL-DB nutzt. Dabei habe ich 2 Probleme:

1.
mysql_query("INSERT INTO artikel (titel, inhalt, erstellt) VALUES('$titel','$inhalt','$erstellt') ") or die(mysql_error());

Wenn ich in $inhalt nun einfache Anführungszeichen habe ('), gibt es einen Syntax-Error. Wie kann ich das umgehen, damit ich beide Arten von Anführungszeichen nutzen kann, die dann in der DB gespeichert werden?

2.
Der Inhalt wird aktuell durch ein einfaches echo wiedergegeben. Nun möchte ich aber (wie im Titel geschrieben) auch PHP-Code in den Artikeln unterbringen, um bspw. eine zuvor definierte Funktion aufzurufen. Wie mache ich das?
Also dass alle Inhalte aus der DB so eingebunden werden, als wären sie in einer Datei mit include() aufgerufen worden.

Hat das irgendwas mit Buffern zu tun?

Ein Eintrag in der DB könnte als Inhalt bspw. folgendes haben:

------------

<div id="content">  
<?php  
$foo = 'bar';  
echo $foo;  
?>  
</div>

------------

Weiterführendes Problem:
Momentan habe ich alles in Dateien abgespeichert. Einzelne Templates etc. werden über include() aufgerufen. Wenn nun der Code in der DB steht, wie kann ich dann dass include() ersetzen, um das Template ebenfalls aus der DB abzurufen?

Also ich rufe die URI host.de/pfad/news/13 auf.
--> Der Datensatz 'News' wird aufgerufen. Dieser enthält den Code, wie News zu behandeln sind.
----> 'News' ruft in der News-DB die entsprechenden Inhalte zur ID 13 ab.
----> 'News' ruft das Template für die Newsanzeige aus der DB ab
------> Das Template enthält das DOM und entsprechende Variablen für den Inhalt

Bsp. Template:
-----------
<div class="news"><div class="titel"><?php echo $titel; ?></div></div>
-----------

Vielen Dank für eure Mühe
michael

  1. hallo Michael,

    Code in der DB zu speichern ist (ausser zu Dokumentationszwecken wie beispielsweise SELFHTML) kein guter Stil. Du bewegst dich in die völlig falsche Richtung. Bitte überdenke dein Konzept nochmals gründlich.

    Gruß
    Brillo

  2. Nachtrag:

    in der DB könntest du möglicherweise Platzhalter setzen. Parse diesen Text später nach den Platzhaltern und ersetze diese dann entsprechend.

  3. Hi!

    mysql_query("INSERT INTO artikel (titel, inhalt, erstellt) VALUES('$titel','$inhalt','$erstellt') ") or die(mysql_error());
    Wenn ich in $inhalt nun einfache Anführungszeichen habe ('), gibt es einen Syntax-Error. Wie kann ich das umgehen, damit ich beide Arten von Anführungszeichen nutzen kann, die dann in der DB gespeichert werden?

    Kontextwechsel beachten. Du hast sonst nicht nur ein Syntax-Problem sondern auch eine SQL-Injection-Lücke.

    Der Inhalt wird aktuell durch ein einfaches echo wiedergegeben. Nun möchte ich aber (wie im Titel geschrieben) auch PHP-Code in den Artikeln unterbringen, um bspw. eine zuvor definierte Funktion aufzurufen. Wie mache ich das?

    Um Code zu interpretieren und auszuführen, der in einem String vorliegt, ist eval() vorgesehen. Du hast dann aber keine Kontrolle, was da alles an Code ausgeführt werden kann. Du wirst kaum einen eigenen Syntaxparser aufsetzen, der versucht, unerwünschten Code zu erkennen. Im Allgemeinen verzichtet man lieber auf die Anwendung von eval() auf Texte unsicheren Ursprungs. DBMS-Inhalte gehören dazu, sie könnten kompromittiert sein.

    Ich plädiere lieber für eine begrenzte Zahl an Platzhaltern, nach denen du scannst und entsprechende vordefinierte Funktionen ausführst. Damit bist du natürlich weniger flexibel, aber deutlich sicherer.

    Weiterführendes Problem:
    Momentan habe ich alles in Dateien abgespeichert. Einzelne Templates etc. werden über include() aufgerufen. Wenn nun der Code in der DB steht, wie kann ich dann dass include() ersetzen, um das Template ebenfalls aus der DB abzurufen?

    Versuch darauf zu verzichten. Code im DBMS ist immer ein Problem, weil man ihn dort schlecht kontrollieren kann.

    Lo!

  4. Wenn ich in $inhalt nun einfache Anführungszeichen habe ('), gibt es einen Syntax-Error. Wie kann ich das umgehen, damit ich beide Arten von Anführungszeichen nutzen kann, die dann in der DB gespeichert werden?

    1. Schau dir mal die Funktion mysql_real_escape_string() an. Die sollte man immer benutzen wenn man Daten in die Datenbank eingeben möchte die von den Benutzern stammen.

    Der Inhalt wird aktuell durch ein einfaches echo wiedergegeben. Nun möchte ich aber (wie im Titel geschrieben) auch PHP-Code in den Artikeln unterbringen, um bspw. eine zuvor definierte Funktion aufzurufen. Wie mache ich das?

    2. Wie bereits vorher schon von anderer Seite gesagt wurde solltest du lieber Platzhalter im Text verwenden. Ähnlich wie bei Smilies oder BBCode würde an den Stellen dann bestimmte Funktionen aufgerufen. Halte ich aber nicht für ein gutes System, Inhalt sollte von der Logik der Seite getrennt sein.

    Momentan habe ich alles in Dateien abgespeichert. Einzelne Templates etc. werden über include() aufgerufen. Wenn nun der Code in der DB steht, wie kann ich dann dass include() ersetzen, um das Template ebenfalls aus der DB abzurufen?

    3. Generell solltest du Templates nicht in der Datenbank abspeichern. Wenn ein Design/Template erst einmal steht kann man sagen das die Daten statisch sind und schon alleine deswegen nicht in eine DB gehören. Ich persönlich schreibe meine Templates komplett in HTML und füge an Stellen an denen etwas dynamische eingefügt werden soll eine Variable mit echo ein.

    <div id="content">  
    <h1><?php echo $title; ?></h1>  
    <p><?php echo $content; ?></p>  
    </div>
    

    Die Variablen müssen vorher natürlich initialisiert werden. Ich mache das über eine kleine einfache Template-Klasse.

    Vielleicht solltest du dich auch mal über Template-Systeme wie Smarty informieren.

    Also ich rufe die URI host.de/pfad/news/13 auf.
    --> Der Datensatz 'News' wird aufgerufen. Dieser enthält den Code, wie News zu behandeln sind.
    ----> 'News' ruft in der News-DB die entsprechenden Inhalte zur ID 13 ab.
    ----> 'News' ruft das Template für die Newsanzeige aus der DB ab
    ------> Das Template enthält das DOM und entsprechende Variablen für den Inhalt

    Irgendwie machst du dir die Sache ziemlich kompliziert. Meine News-Datenbank enthält pro Eintrag nur ID, Titel, Text weite Infos sind eher unwichtig hier. Wenn man nun also die News lädt:
    SELECT * FROM news
    So erhält man ein schönes Array das mit foreach() einfach durchgegangen und ausgegeben werden kann. Wenn du jedes Mal für jeden Eintrag noch abspeicherst wie er behandelt werden soll, obwohl die meisten Einträge gleich zu behandeln wären, machst du es dir ziemlich schwer, wenn du etwas ändern willst. Soll heißen speichere nur Text und keinen Code.

    Gruß Andreas

    1. Hi!

      1. Schau dir mal die Funktion mysql_real_escape_string() an. Die sollte man immer benutzen wenn man Daten in die Datenbank eingeben möchte die von den Benutzern stammen.

      So generell gesagt ist das nicht richtig, denn sie ist nur für Strings vorgesehen. Bei Zahlen, die nicht in Anführungszeichen in ein SQL-Statement geschrieben werden, ist diese Funktion nicht nur überflüssig, sie wiegt den Anwender auch in falscher Sicherheit. Siehe Zahlen im (My)SQL-Statement.

      Lo!

      1. So generell gesagt ist das nicht richtig, denn sie ist nur für Strings vorgesehen. Bei Zahlen, die nicht in Anführungszeichen in ein SQL-Statement geschrieben werden, ist diese Funktion nicht nur überflüssig, sie wiegt den Anwender auch in falscher Sicherheit. Siehe Zahlen im (My)SQL-Statement.

        Ich setze meine Anführungszeichen eh selbst in der Abfrage. Werte die Zahlen sein müssen werden vorher darauf überprüft. Die bekommen dann keine Anführungszeichen was aber bei MySQL eh uninteressant ist, wenn ein Feld ein Integer erwartet wird es per Typecast umgewandelt.

        Hier mal die Funktion die bei meiner Datenbank-Klasse zum escapen da ist. Nicht unbedingt perfekt, aber naja:

        public function escape($var)  
        {  
        	if ( is_int($var) || is_bool($var) ) { return (int)$var; }  
        	if ( is_float($var) ) { return (float)$var; }  
          
        	if( get_magic_quotes_gpc() )  
        	{  
        		$var = stripslashes($var);  
        	}  
          
        	return mysql_real_escape_string($var, $this->db_connect_id);  
        }
        
        1. Hi!

          So generell gesagt ist das nicht richtig, denn sie ist nur für Strings vorgesehen. Bei Zahlen, die nicht in Anführungszeichen in ein SQL-Statement geschrieben werden, ist diese Funktion nicht nur überflüssig, sie wiegt den Anwender auch in falscher Sicherheit. Siehe Zahlen im (My)SQL-Statement.
          Ich setze meine Anführungszeichen eh selbst in der Abfrage. Werte die Zahlen sein müssen werden vorher darauf überprüft. Die bekommen dann keine Anführungszeichen was aber bei MySQL eh uninteressant ist, wenn ein Feld ein Integer erwartet wird es per Typecast umgewandelt.

          Jo, genauso, wie ich es im verlinkten Absatz beschrieben habe. Du gibst mit deiner Antwort zu, dass du für Zahlenwerte Ausnahmen machst. Das ist richtig und gut so. Ich sprach diesen Punkt aber an, weil gelegentlich hier Postings auftauchen, die diese Zahlen-Ausnahme so nicht gesehen haben, und ich zunächst vermutet hatte, dass dir dieses Thema nicht bewusst war.

          Hier mal die Funktion die bei meiner Datenbank-Klasse zum escapen da ist. Nicht unbedingt perfekt, aber naja:

          Find ich auch. Schon nicht schlecht, aber wenn du gestattest:

          public function escape($var)

          {
          if ( is_int($var) || is_bool($var) ) { return (int)$var; }
          if ( is_float($var) ) { return (float)$var; }

          if( get_magic_quotes_gpc() )
          {
          $var = stripslashes($var);
          }

          return mysql_real_escape_string($var, $this->db_connect_id);
          }

            
          Du scheinst davon auszugehen, dass du nur Werte behandeln musst, die über $\_GET, $\_POST und $\_COOKIE (GPC) reingekommen sind. Du behandelst nämlich den übergebenen Wert gegen Magic Quotes, die aber eben nur bei GPC-Variablen gesetzt sind. Du musst aber alle Werte berücksichtigen, die in ein SQL-Statement einfließen sollen, nicht nur GPC-Werte. Das mag in deinem Script, in dem du diese Funktion verwendest, vielleicht nicht der Fall sein, aber das macht diese Funktion weniger universell verwendbar. Die Magic-Quotes-Behandlung sollte bei einem nach EVA-Prinzip strukturierten Programmablauf im E(ingabe)-Teil angesiedelt sein. Du brauchst die MQ-Behandlung ja generell, nicht nur für Werte, die über SQL weitergereicht werden und von deiner Funktion behandelt werden sollen, sondern unter anderem auch für Werte, die nach HTML ausgegeben werden sollen (zum Beispiel beim Affenformular).  
            
          Du prüfst am Anfang auf int, bool und float. (Der Typecast nach float ist nicht nötig, denn is\_float() stellt ja schon sicher, dass $var von diesem Typ ist.) Du prüft also schon, ob du (Boolean und) Zahlentypen (nicht jedoch in Strings steckende Zahlenwerte) übergeben bekommst. Allerdings musst du beim Verwenden der Funktion ebenfalls aufpassen, ob du einen Zahlentyp(!) einfügst oder einen String, denn danach richtet sich ob du Anführungszeichen setzen musst oder nicht. Wenn du hingegen stets Anführungszeichen setzt, brauchst du diese Funktion nicht, denn dann erledigt mysql\_real\_escape\_string() allein schon alles (abgesehen von der MQ-Behandlung, aber die ist ja sowieso am Scriptanfang besser aufgehoben). Damit deine Funktion einen wirklichen Mehrwert hat, fehlt ihr zum Beispiel noch, dass sie automatisch Anführungszeichen hinzufügt, je nach Notwendigkeit. (Dann braucht sie aber auch noch einen anderen Namen, denn sie escapet dann nicht mehr nur sondern quotet auch.)  
            
          Was auch nicht hervorgeht, ist, wie du Zahlenwerte in GPC-Variablen behandelst, denn in GPC stehen generell Strings (wenn man sie nicht händisch typecastet) (und ja, Arrays können auch drinstehen, aber darin sind ebenfalls wieder nur Strings (und Arrays)</rekursion>). Diese GPC-Zahlenstrings müsstest du mit intval() oder floatval() oder Typecasts zwangsweise in ebendiese Typen bringen, wenn du deine Funktion gewinnbringend verwenden willst.  
            
          Nochmal zusammenfassend meine Vorschläge:  
          - Magic Quotes-Behandlung entfernen und an den Scriptanfang verlagern.[\*]  
          - Automatische typabhängige Quotierung einbauen.  
          - Zahlenbehandlungskonzept eventuell noch einmal überdenken und verfeinern.  
          - Und wenn die Funktion NULL-Werte beachten würde, wäre sie noch ein bisschen perfekter.  
            
            
          [\*] Am besten MQ ganz deaktivieren, denn die fallen mit PHP6 sowieso weg.  
            
            
          Lo!
          
          1. Danke erst mal für den Input. Vor allem für das Wort Affenformular, wusste nicht das man das so nennt. :)

            • Magic Quotes-Behandlung entfernen und an den Scriptanfang verlagern.[*]
              [*] Am besten MQ ganz deaktivieren, denn die fallen mit PHP6 sowieso weg.

            Die sind aus, hab grad noch mal nachgeguckt. Das ich das ganze nur auf GPC Variablen überprüfe liegt wohl daran dass ich bisher kaum etwas anderes benutzt habe und eh nur lokal meine Spielereien veranstalte. Über sehr einfache Scripte komm ich bei diesem Hobby selten mal hinaus, auch wenn ich gerne möchte.

            • Automatische typabhängige Quotierung einbauen.

            Naja, wie gesagt, normalerweise füge ich die Quotes selbst schon in die SQL-Abfrage ein ala:
            $sql ='... user_name_login = \'' . $DATA->escape($name_login) . '\'';
            Vorher war die Funktion auch einfach nur ein Wrapper für mysql_... um sie nicht jedes mal komplett ausschreiben zu müssen.

            • Zahlenbehandlungskonzept eventuell noch einmal überdenken und verfeinern.

            Ich finde gerade kein Beispiel, aber meist benutze ich die Funktion gar nicht bei Zahlen sondern caste sie direkt. Diese escape Funktion sollte eigentlich erst einmal eine richtige werden. Das ganze gehört halt zu einer Datenbank-Klasse die ich immer wieder verwende. Das mit dem unnötigen cast ist allerdings richtig.

            • Und wenn die Funktion NULL-Werte beachten würde, wäre sie noch ein bisschen perfekter.

            Da wüsste ich jetzt gar nichts ausser wieder NULL zurückzugeben. Habe ich so auch noch nicht gehabt.

            Danke nochmals, ich werde mal sehen ob ich die Funktion noch mal umschreibe und doch vielleicht über diese die Quotes machen lasse. Muss nur mal ausprobieren wie genau sch das ganze dann bei Single- und Double-Quotes verhält. Meine Scripte sind eh eine ewige Baustelle.

            Gruß Andreas

            1. Hi!

              • Und wenn die Funktion NULL-Werte beachten würde, wäre sie noch ein bisschen perfekter.
                Da wüsste ich jetzt gar nichts ausser wieder NULL zurückzugeben. Habe ich so auch noch nicht gehabt.

              Im Statement muss das Wort NULL stehen, nackig ohne Anführungszeichen. Also aus dem PHP-Typ null muss der String NULL werden.

              Danke nochmals, ich werde mal sehen ob ich die Funktion noch mal umschreibe und doch vielleicht über diese die Quotes machen lasse. Muss nur mal ausprobieren wie genau sch das ganze dann bei Single- und Double-Quotes verhält. Meine Scripte sind eh eine ewige Baustelle.

              Da gibt es im Prinzip keine Probleme. Stell einfach ' oder " um den Rückgabewert von mysql_real_escape_string(). Im PHP-Code sieht das dann so aus:

              $sql = 'SELECT foo FROM bar WHERE qux = ' . $data->quote($qux) . ' ORDER BY foo';
              oder auch:
              $sql = sprintf('SELECT foo FROM bar WHERE qux = %s ORDER BY foo', $data->quote($qux));

              Mit sprintf() bekommt man eine Menge Anführungszeichen und Stringverknüpfungen weg, was der Lesbarkeit zu Gute kommt.

              Lo!

              1. Hab die Funktion einfach mal etwas umgestellt. Ich denke mal, wenn ich MQ mal außen vor lasse bzw. deaktiviert ist dass es so eigentlich richtig sein müsste.

                public function escape($var)  
                {  
                	if ( is_int($var) || is_bool($var) ) { return (int)$var; }  
                	if ( is_float($var) ) { return $var; }  
                  
                	if ( $var == NULL ) { return 'NULL'; }  
                  
                	return '\'' . mysql_real_escape_string($var, $this->db_connect_id) . '\'';  
                }
                

                Mit sprintf() muss ich mal gucken ob mir das gefällt aber bei c find ich das ganz komfortabel.

                Danke und Gruß Andreas