Mozz: und PHP <- geht das überhaupt?

Hi!

Bisher habe ich das meiste dynamische auf meiner Seite mit PHP gemacht. Nun habe ich folgendes Problem:

Ein Perl-Script erzeugt eine Ausgabe zu der ich aber - je nachdem ob es eine bestimmte Variable gibt  - eine Fußzeile hinzufügen möchte (Variable wird per get übertragen).

Bisher habe ich das so gemacht:

<?php if (!$dt) include("../ein-script-fetzen.php"); ?>

Nun meine Fragen (bin Perl-Newbie):

  • Wird der Perl-Output geparst, so dass ich da PHP-Zeugs reinschreiben kann? Vermutlich nicht, oder?!

  • Stehen per get übergebene Variablen auch in Perl automatisch zur Verfügung?

  • Wenn das nicht mit PHP geht, wie mach ich so etwas in Perl?

Danke Mozz

  1. Hi,

    • Wird der Perl-Output geparst, so dass ich da PHP-Zeugs reinschreiben kann? Vermutlich nicht, oder?!

    nein; der Server parst ein Dokument maximal ein Mal. Du hast die Wahl zwischen PHP, SSI, CGI, JSP oder was immer konfiguriert ist.

    • Stehen per get übergebene Variablen auch in Perl automatisch zur Verfügung?

    Es existieren in HTTP keine Variablen. Dass PHP daraus welche macht, ist ein Sicherheitsrisiko. Das Perl-Modul CGI.pm ermöglicht Dir jedoch einen leichten Zugang zu allen übergebenen Parametern; siehe Doku unter

    perldoc CGI

    • Wenn das nicht mit PHP geht, wie mach ich so etwas in Perl?

    Perl kann problemlos Dateien einbinden, jedoch kann es keinen PHP-Code ausführen. Dazu wäre ein HTTP-Request nötig, was performancetechnischer Unsinn ist, oder im Falle der CGI-Installation von PHP ein Systemaufruf des PHP-Prozessors, was ich auch nur bedingt empfehlen möchte: Der Footer ist höchstwahrscheinlich statisch, und sollte daher ohnehin, auch für PHP, in statischer Text-Form vorliegen.

    Cheatah

  2. Aloha!

    Ein Perl-Script erzeugt eine Ausgabe zu der ich aber - je nachdem ob es eine bestimmte Variable gibt  - eine Fußzeile hinzufügen möchte (Variable wird per get übertragen).

    Du hast vielfältige Möglichkeiten:

    1. Include per URL:
    include("http://www.example.com/cgi-bin/perlscript.pl?parameterstring-urlencoded");

    Diese Möglichkeit ist die schlechteste von allen. Dein PHP-Script muß einen kompletten HTTP-Request absetzen und das Ergebnis empfangen. Das dauert. Dafür kannst du das Perl-Script PHP-Code ausgeben lassen, der dann ausgeführt wird.

    Allerdings Vorsicht: Erstens passe höllisch auf, wenn die Include-Adresse variabel gelassen wird (wenn von extern ein Angreifer eine anderere URL mit einem bösen PHP-Script einschleusen kann, hast du verloren!), und zweitens funktioniert das nur, wenn die fopen-url-wrappers-Option aktiv ist (und die kann, um genau diesen Angriff auszuschließen, deaktiviert sein).

    2. Aufrufen eines externen Systemkommandos:
    Dazu kennt PHP die Kommandos passthru(), popen() und den Backtick-Operator. exec() und system() sind eher ungeeignet, um Massenoutput durchzureichen.

    2a: Im Prinzip könntest du auf diese Weise an einer bestimmten Stelle den Output eines Perl-Skriptes direkt durchschleusen:
    passthru("/pfad/zum/perlskript.pl");
    Vorher und hinterher kann PHP seine HTML-Teile ausgeben - das Perl-Skript darf dann natürlich keine komplette Seite mehr ausgeben.
    http://www.php.net/manual/en/function.passthru.php

    2b: Du kannst auch die Ausgabe des Perl-Skriptes in PHP zunächst in eine Variable speichern und dann nochmals parsen, um Variablen zu ersetzen.
    http://www.php.net/manual/en/language.operators.execution.php
    http://www.php.net/manual/en/function.popen.php

    3. Natürlich kannst du die Sache auch von Perl aus angehen. Lösung 1 funktioniert mit Perl im Prinzip genauso, nur eben perl-artig (und IMO etwas aufwendiger) mit dem Modul LWP::Simple.

    4. Du kannst PHP-Skripte auch lokal als Skript ausführen, wenn der Interpreter passend eingerichtet ist. Dann gelten im Prinzip alle Möglichkeiten von Lösung 2.

    - Sven Rautenberg

    1. Nochmal Aloha!

      1. Aufrufen eines externen Systemkommandos:
        Dazu kennt PHP die Kommandos passthru(), popen() und den Backtick-Operator. exec() und system() sind eher ungeeignet, um Massenoutput durchzureichen.

      Auch hier gilt: Extreme Vorsicht mit User-Input. Bei dieser Variante kann der User es u.U. schaffen, beliebige Kommandos (evtl. zusätzlich) auf dem Server auszuführen. "rm -rf *" reicht evtl. schon, um deine Präsenz zu löschen.

      - Sven Rautenberg

      1. Nochmal Aloha!

        1. Aufrufen eines externen Systemkommandos:
          Dazu kennt PHP die Kommandos passthru(), popen() und den Backtick-Operator. exec() und system() sind eher ungeeignet, um Massenoutput durchzureichen.

        Auch hier gilt: Extreme Vorsicht mit User-Input. Bei dieser Variante kann der User es u.U. schaffen, beliebige Kommandos (evtl. zusätzlich) auf dem Server auszuführen. "rm -rf *" reicht evtl. schon, um deine Präsenz zu löschen.

        Hi Sven!

        Ich selbst habe (leider) keine shell-Zugang und nach
        <?php passthru("rm test.php"); ?>
        war die Datei test.php immer noch da, rm -rf * hab' ich lieber nicht probiert ;-), sollte dann aber auch nicht gehen.

        Ich habe als Usereingaben "nur" die Parameter var1, var2, usw.
        <?php passthru("../cgi-bin/script.cgi?var1=xx&var2=yy"); echo "\n"; ?>

        D.h. ../cgi-bin/script.cgi? kann userseitig nicht verändert werden nur die Parameter danach. Bisher dachte ich mir das so: Der User gibt in ein Formular ein Suchwort ein und legt Optionen fest (z.B. Case Sensitive). Dies geschieht bisher noch über das Perl-Script, würde ich dann aber über PHP realisieren. Danach würde ich das Perl-Script mit

        <?php passthru("../cgi-bin/script.cgi?var1=xx&var2=yy");

        füttern. Allerdings habe ich gerade beim Testen entdeckt, dass

        <?php passthru("../cgi-bin/script.cgi?terms=test"); ?>
        nicht funktioniert :-(

        wohl aber
        <?php passthru("../cgi-bin/script.cgi"); ?>

        also ohne die Parameter geht es, mit nicht, gibt es hier Probleme mit der Variablenübergabe an Perl? Verstehe es nicht, dass

        ../cgi-bin/script.cgi?terms=test über passthru() nicht geht, aber der direkte Aufruf über
        http://www.test.de/cgi-bin/script.cgi?terms=test ohne Probleme möglich ist. Cheatah hat ja etwas vom Perl-Modul CGI.pm geschrieben, dass zur Parameterübernahme nötig ist, kann es sein, dass es bei lokalem Zugriff (also nicht über http) zu Problemen kommt?

        Nun, jedenfalls würde ich das so realisieren, wenn ich den Fehler noch finde. Gäbe es bei dieser Lösung für den User eine Möglichkeit bei geschickter Wahl der "Suchbegriffe" den an passthru übergebenen String so zu verändern, dass neben dem CGi-Script ein weitere Befehl abgesetzt werden kann? Das würde schon mal voraussetzen, dass passthru() auch mehrere Kommandos ausführen kann, ist das so?

        Nur mal als gedankliches Beispiel:

        Normale Usereingabe: Suche nach >SelfHTML Forum<
        erzeugte URL:
        <?php passthru("../cgi-bin/script.cgi?terms=%22SelfHTML+Forum%22"); ?>

        "Feindliche" Usereingabe: Suche nach >", "rm -rf *"<
        erzeugte URL:
        <?php passthru("../cgi-bin/script.cgi", "rm -rf *"); ?>

        Wie gesagt, ich weiß nicht, ob es mit passthru() möglich ist mehrere Befehle auszuführen [link:http://www.php.net/manual/en/function.passthru.ph spricht auch nur von einem Befehl, wenn ein Rückgabewert möglich ist, geht das vermutlich auch nur mit einem Befehl.

        Noch eine grundsätzliche Frage: Würde bei solchen Dingen generell auch ein urlencode reichen, damit der Sting innerhalb von "" ("string") nicht "durchbrochen" wird oder sind da weitere Überprüfungen notwendig?

        Mozz

        1. Normale Usereingabe: Suche nach >SelfHTML Forum<
          erzeugte URL:
          <?php passthru("../cgi-bin/script.cgi?terms=%22SelfHTML+Forum%22"); ?>

          "Feindliche" Usereingabe: Suche nach >", "rm -rf *"<
          erzeugte URL:
          <?php passthru("../cgi-bin/script.cgi", "rm -rf *"); ?>

          Das war gerade nicht konsistent, was ich da hingeschrieben habe. Im ersten Bsp. wurde der Such-String mit urlencode "behandelt" im zweiten nicht.

          Mozz

          1. Aloha!

            Betrachte dies als Antwort auf deine drei Postings. :)

            Was passthru() angeht: Damit veranlaßt du das Perl-Script, seine Aufgabe zu erledigen und schleust alles, was Perl so schreibt, direkt zum Browser durch.

            Wenn das Perl-Skript gewöhnlich direkt als CGI-Skript arbeitet, dann muß es natürlich einen gültigen HTTP-Header ausgeben. Das bedeutet für dich: Wenn du mit Passthru arbeiten willst, mußt du das Perl-Skript entsprechend so ändern, daß es keinen HTTP-Header ausgibt - und auch nichts von den sonst für eine Seite üblichen Bestandteilen, die du mit PHP schon gesendet haben könntest, also <html><head>[...]</head><body>[...]</body></html>. Eigentlich willst du nur das Suchergebnis haben, und sonst nichts.

            Das bedeutet, am Perl-Skript ein wenig herumzubasteln. Wenn du das nicht kannst, bleibt dir nur der Weg, den Inhalt der Perl-Ausgabe zwischenzuspeichern und nur das auszugeben, was du wirklich haben willst. Das kann relativ einfach werden, wenn das Perl-Skript eine markante Zeichenfolge ausgibt, die Start und Ende des interessanten Bereichs markiert. Beispielsweise <body> und </body>. Alles dazwischen könntest du dann mit PHP ausgeben.

            Was den Aufruf angeht:
            Dank der fopen-wrappers kannst du URLs includen. Damit wird dann der Webserver erneut angesprochen und sorgt in einem eigenen Prozeß dafür, daß das Perl-Skript die URL-Parameter erhält.

            Wenn du das Skript aber direkt mit passthru() ansprichst, dann mußt du eine andere Methode zur Parameterübermittlung wählen: Kommandozeilenparameter oder Pipes. Je nach deiner Wahl müßtest du deshalb dein Perl-Skript umarbeiten, damit es diese neue Art der Kommunikation versteht.

            Dann kannst du mit passthru('../cgi-bin/perlskript.pl -search="Suchbegriff" -flag1 -flag2 -parameter="sonstirgendwas"'); arbeiten.

            Du siehst: Entweder sprichst du das Perl-Skript direkt an und mußt ein paar Änderungen am Perl-Skript vornehmen, oder du sprichst das Perl-Skript über die URL an und mußt ein paar Änderungen an der Ergebnisbehandlung vornehmen. Ändern mußt du in jedem Fall, weil das Perl-Skript derzeit eine komplette HTML-Seite ausgibt, in die du aber noch etwas hineinschreiben willst.

            - Sven Rautenberg

            1. Aloha!

              Betrachte dies als Antwort auf deine drei Postings. :)

              Sorry, aber die Einfälle kommen halt manchmal schubweise, aber besser so als gar nicht :-)

              Dank der fopen-wrappers kannst du URLs includen. Damit wird dann der Webserver erneut angesprochen und sorgt in einem eigenen Prozeß dafür, daß das Perl-Skript die URL-Parameter erhält.

              Wenn du das Skript aber direkt mit passthru() ansprichst, dann mußt du eine andere Methode zur Parameterübermittlung wählen: Kommandozeilenparameter oder Pipes. Je nach deiner Wahl müßtest du deshalb dein Perl-Skript umarbeiten, damit es diese neue Art der Kommunikation versteht.

              Dann kannst du mit passthru('../cgi-bin/perlskript.pl -search="Suchbegriff" -flag1 -flag2 -parameter="sonstirgendwas"'); arbeiten.

              Ok, das habe ich jetzt kapiert :-)

              Kannst Du noch was zu passthru() und "feindlichen/bösen" Usereingabe sagen, wäre so ein Szenario (zweiter/veränderter Befehl) möglich, wenn der Scriptpath fix ist, aber die Parameter nicht, oder ist so etwas nur denkbar, wenn auch der Pfad dynamisch erzeugt wird. Ist urlencode() da eine Abhilfe oder nur "Weihwasser".

              Mozz

              1. Yo!

                Kannst Du noch was zu passthru() und "feindlichen/bösen" Usereingabe sagen, wäre so ein Szenario (zweiter/veränderter Befehl) möglich, wenn der Scriptpath fix ist, aber die Parameter nicht, oder ist so etwas nur denkbar, wenn auch der Pfad dynamisch erzeugt wird. Ist urlencode() da eine Abhilfe oder nur "Weihwasser".

                Gefährlich sind bei Usereingaben die Zeichen, die vom Zielsystem nicht als einfaches Zeichen angesehen werden, sondern Sonderfunktion haben.

                Bei SQL ist z.B. das einfache Anführungszeichen gefährlich, da damit die Strings beendet werden, die man an die Datenbank übergibt. Würde es ein Angreifer schaffen, beim Zusammenbau der SQL-Abfrage ein einfaches Anführungszeichen einzuschleusen, würde das den String beenden, und der Rest der Usereingabe wäre beliebiger SQL-Code, der z.B. die Datenbank löscht:

                $sqlquery="SELECT * FROM tabelle WHERE spalte LIKE '%$suchstring%'";

                Wenn $suchstring folgenden Inhalt hat, dann ist das böse:
                $suchstring="quark'; DELETE FROM tabelle WHERE spalte LIKE '";

                Zusammengesetzt:
                $sqlquery="SELECT * FROM tabelle WHERE spalte LIKE '%quark'; DELETE FROM tabelle WHERE spalte LIKE '%'";

                Wäre böse und birgt einiges an Potentiel. Lösung: Die möglicherweise im String enthaltenen Sonderzeichen müssen ihrer besonderen Wirksamkeit enthoben werden - durch escapen dieser Zeichen. addslashes() erledigt das bei Anführungszeichen für Datenbankabfragen. Und escapeshellcmd() erledigt das für Strings, die an die Kommandozeile gehen.

                http://www.php.net/manual/en/function.escapeshellcmd.php

                Durch Escapen ist sichergestellt, daß das System, welches mit den Daten arbeitet, nicht umgangen werden kann. Du gehst durch deine Programmierung davon aus, daß ein String übergeben wird. Ein String ist nicht böse. Durch Escapen geht das Zielsystem auch nur von einem String aus, weil es keine Befehlsartigen Kommandos enthalten kann.

                Natürlich hast du beim Perl-Skript wiederum die Verantwortung, Weitergaben von Strings an Subsysteme (z.B. die Datenbank oder eine Shell) zu escapen.

                Generell: Sicherheit bei Usereingaben ist dann gewährleistet, wenn die Usereingaben das bleiben, was sie ursprünglich waren: Daten. Es ist zu verhindern, daß sie zu Befehlen und Programmen mutieren.

                - Sven Rautenberg

    2. Aloha!

      :-) Ole!!

      Hi Sven!

      Erst mal muss ich ein bisschen schleimen: Du bist einfach der Beste!! Auf die Idee das Perl-Script in PHP einzubinden bin ich gerade gar nicht gekommen, sondern bin nur von Perl ausgeganen. Warum steht man manchmal so auf dem Schlauch, kann mir das jemand mal sagen??!

      Du hast vielfältige Möglichkeiten:

      1. Include per URL:
        include("http://www.example.com/cgi-bin/perlscript.pl?parameterstring-urlencoded");

      Diese Möglichkeit ist die schlechteste von allen. Dein PHP-Script muß einen kompletten HTTP-Request absetzen und das Ergebnis empfangen. Das dauert. Dafür kannst du das Perl-Script PHP-Code ausgeben lassen, der dann ausgeführt wird.

      Sachen gibt's, ein Perl-Script, das PHP-Code erzeugt, da muss man erst mal drauf kommen... ;-) Ist aber (bei mir) nicht nötig. Ich habe ein Suchmaschinenanfrage an ein Perl-Script, das HTML-Code erzeugt, der nur ausgegeben werden muss.

      Allerdings Vorsicht: Erstens passe höllisch auf, wenn die Include-Adresse variabel gelassen wird (wenn von extern ein Angreifer eine anderere URL mit einem bösen PHP-Script einschleusen kann, hast du verloren!), und zweitens funktioniert das nur, wenn die fopen-url-wrappers-Option aktiv ist (und die kann, um genau diesen Angriff auszuschließen, deaktiviert sein).

      Die Include-Adresse selbst wäre statisch: http://www.example.com/cgi-bin/perlscript.pl?var=suchanfrage&var2=weitere_Optionen&... nur die Parameter nicht. Das Perl-Scrip ist auch auf dem gleichen Rechner (wenn das für die fopen-url-wrappers-Option von Belang wäre). Habe es auch gerade mal getestet und es funktioniert auch prima!! Geht aber wirklich nur mit http://www.example.com/cgi-bin/... (http am Anfang, da mit der alleinigen Datei das Perl-Script ../cgi-bin/usw. nicht abgearbeitet wird, sondern nur der Code ausgegeben wird. Btw. PHP kennt das http-Stammverzeichnist / nicht, da es ja auf Dateiebene werkelt, oder?

      1. Aufrufen eines externen Systemkommandos:
        Dazu kennt PHP die Kommandos passthru(), popen() und den Backtick-Operator. exec() und system() sind eher ungeeignet, um Massenoutput durchzureichen.

      2a: Im Prinzip könntest du auf diese Weise an einer bestimmten Stelle den Output eines Perl-Skriptes direkt durchschleusen:
      passthru("/pfad/zum/perlskript.pl");
      Vorher und hinterher kann PHP seine HTML-Teile ausgeben - das Perl-Skript darf dann natürlich keine komplette Seite mehr ausgeben.

      Auch das geht, bei mir! Aber noch zwei Fragen ;-)

      • Ist passthru() Sicherheitstechnisch besser einzuschätzen als popen()
      • Mit passthru() wird vor der eigentlichen Ausgabe des (Perl-)Scripts noch "Content-Type: text/html" ausgegeben, kann man das irgendwie unterdrücken? Die ganze passthru()-Funktion würde mir wenig nützen, wenn ich es dann nochmal in eine Variable packen müßte und den ollen Text rausfiltern muss.

      http://www.php.net/manual/en/function.passthru.php

      Übrigens Danke für die Links!!

      2b: Du kannst auch die Ausgabe des Perl-Skriptes in PHP zunächst in eine Variable speichern und dann nochmals parsen, um Variablen zu ersetzen.
      http://www.php.net/manual/en/language.operators.execution.php
      http://www.php.net/manual/en/function.popen.php

      Danke für den Hinweis, ist aber bei mir (noch) nicht nötig (mir fällt aber bestimmt noch etwas ein, bei dem Perl PHP-Code erzeugt ;-)

      Hätte dieses Vorgehen - auch wenn keine (PHP-)Variablen da sind - sicherheitstechnisch Vorteile?

      1. Natürlich kannst du die Sache auch von Perl aus angehen. Lösung 1 funktioniert mit Perl im Prinzip genauso, nur eben perl-artig (und IMO etwas aufwendiger) mit dem Modul LWP::Simple.

      Das hat Cheatah schon angedeutet, mir wäre aber diese Sache hier bedeutend lieber, da ich dann den existierenden footer und header so einbauen kann wie er ist, weiter kann ich dann die Variaben einfach an das PHP-Script übergeben und sie auch so im footer verwenden und muss nicht das ganze zusätzlich in einer Perl-Version unterbringen.

      1. Du kannst PHP-Skripte auch lokal als Skript ausführen, wenn der Interpreter passend eingerichtet ist. Dann gelten im Prinzip alle Möglichkeiten von Lösung 2.

      Ähm, Du meinst wenn ich über ein Perl-Script als "Schnittstelle" arbeite?

      Nun, jetzt kann ich mich nicht so eindeutig entscheiden, was ich nehmen soll: Ein einfaches include() oder passthru()? Was an passthru() ärgerlich ist ist die Ausgabe von "Content-Type: text/html". Gibt es - bei statischer URL - sicherheitstechnische Vorteile für passthru? Performancetechnisch ist sicher auch passthru() zu bevorzugen, da kein http-Request nötig ist, allerdings ist das Perl-Scrip auf dem gleichen Server, die Wege also recht kurz.

      Mozz

      P.S.: Noch mal ganz herzlichen Dank für die ausführliche  Antwort und Hilfe!!