PHP_SELF ist böse! Potentielles Cross Site Scripting (XSS)!

Was haben wir gelernt?

EvilJa, uns wurde gelehrt, dass man wenn immer möglich nicht Dateinamen direkt, sondern eine Variable angeben soll, die für den Dateinamen steht. Warum konnte mir zwar noch niemand so genau sagen, aber ich nehme an, dass es darum geht, dass der Dateinamen oder der Pfad ändern könnte. So habe ich ziemlich oft ganz brav geschrieben:

<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method ="post">
...
</form>

Und (zum Glück) bin ich nicht der Einzige. Eine kleine Suche auf Googles Codesearch hat gezeigt, dass mindestens Mambo, PHPMyFAQ, Einige WordPressthemes und viele mehr dasselbe Problem haben. Ich bin bei Weitem auch nicht der Erste, der über Probleme mit dieser Technik erfahren und darüber geschrieben hat.

Problem? mit $_SERVER['PHP_SELF']?

Lasst mich ausholen: Der Apache Webserver hat eine Option AcceptPathInfo, welche standardmässig auf On ist. Mit dieser Option mappt der Apache beliebig lange Pfade auf Dateien, sofern diese irgendwie Bestandteil des Pfades sind. Der Rest wird in Umgebungsvariablen mitgegeben.

Gimme Code

Nehmen wir an, es gäbe eine Datei /subdir/mypath.php welche so aussieht:

<?php
echo "<pre>";
echo "REQUEST_URI: ".$_SERVER['REQUEST_URI']."\n";
echo "PHP_SELF:    ".$_SERVER['PHP_SELF']."\n";
echo "SCRIPT_NAME: ".$_SERVER['SCRIPT_NAME']."\n";
echo "</pre>";
?>

Bei einem Aufruf von: http://localhost/subdir/mypath.php/additional/stuff/nonsense.php?para=4 mappt Apache netterweise alles auf unsere Datei http://localhost/subdir/mypath.php und verstaut den Rest im $_SERVER Array. Die Ausgabe ist:

REQUEST_URI: /subdir/mypath.php/additional/stuff/nonsense.php?para=4
PHP_SELF:    /subdir/mypath.php/additional/stuff/nonsense.php
SCRIPT_NAME: /subdir/mypath.php

PHP_SELF übernimmt also den ganzen Krempel und würde ihn bei unserem Form auch so darstellen. Angenommen, wir haben ein PHP-Script mit der URL http://localhost/contact/myform.php mit folgendem Inhalt:
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method ="post">

Ruft man dieses nun mit folgender URL auf (die aufwändiger als notwendig konstruiert und des besseren Verständnisses wegen nicht URL-encodiert ist):
http://localhost/contact/myform.php/"></form>Hier ein Javascript: <script>alert('gotcha');</script><form action="/contact/myform.php
erhält man:
<form action="/"></form>Hier ein Javascript: <script>alert('gotcha');</script><form action="/contact/myform.php" method ="post">

Also vollkommen gültiges HTML (sogar das Form funktioniert) mit fremdbestimmbaren Seiteninhalt. Das ist ja wohl hässlich…

Theorie! Gib mir Praxis!

Ich weiss nicht, wie lange folgende Links funktionieren, beziehungsweise diese Sites anfällig für diese Art von XSS sind:

Digital Postcard (Kein XSS, aber mein Text 🙂 ):
Postcard XSS

Multimediatreff: (Mittlerweile behoben)
Multimediatreff

Jobs. ch (Mittlerweile behoben):
Jobs.ch

Was nutzt das dem bösen Hacker?

Text in Fremdpages einbauen, Phishing, Indentitätenklau und noch einiges Weiteres. Ein Folgeartikel wird mindestens eine Anwendung zeigen.

Was tun?

Ganz einfach: Das oft verschmähte $_SERVER['SCRIPT_NAME'] verwenden!!!

BTW: Viele Variablen in $_SERVER sind anfällig, aber alles muss ich ja auch nicht verplappern, oder?

38 Gedanken zu “PHP_SELF ist böse! Potentielles Cross Site Scripting (XSS)!

  1. Naja eigentlich sollte man Server Vars auch nur im Hintergrund verwenden.
    Zb mit denen eine var machen die wiederum als basis für die eigentlich zweckmäßige var ist.
    auch könnte man diese vars dann vor der interpretation durch iene function jagen die nur gewünschten inhalt zulässt oder zumindest einige prüfungen macht
    so könnte man den ganzen mist bereits im vorherein abfangen

  2. @Bofh
    Jup, auf jeden Fall sollte man den Variablen von Aussen kein Bisschen trauen. Es gibt viele verschiedene Filtermöglichkeiten und man muss aufpassen, dass man keine Fehler macht und dann trotzdem wieder XSS-Lücken provoziert…

  3. Pingback: Problem mit altem Code auf PHP5 - php.de

  4. Pingback: $php_self und Variablen in der URL - Seite 2 - php.de

  5. Pingback: Anonymous

  6. Pingback: wieso ist PHP_SELF unsicher? - php.de

  7. Pingback: Templaterie Blog

  8. Danke Chummer,
    Da hat gerade der Platz 1 auf der SERP ganz gut zum akuten Code gepasst…
    Man freut sich, zur rechten Zeit das rechte bisschen Info zu bekommen und verbastelt es gerne alternativ zur Angriffsfläche.

    Die Menschheit wird verrückt… 🙂

  9. @skaldrom

    Hallo, ich habe deine Angaben gerade getestet, und habe dafür
    <form action="“ method=“post“>
    geschrieben, aber wenn ich:
    http://localhost/web/kontakt.php/„>Hier wurde Javascript eingeschmuggelt!alert(‚Du bist erledigt!‘);<form action="/web/kontakt.php
    in meiner adressleiste eingebe, dann sehe ich keie Änderung oder Verbesserung, ich erhalte dann trotzdem den Text auf der seite und auch eine js-alertbox 🙁

    Was soll jetzt an $_SERVER['SCRIPT_NAME'] besser sein?
    Ich hab das nicht ganz verstanden, sorry. Daniel

    Upps ich sehe gerade das der code entschärft wir…als ich habe php_self durch script_name ersetzt…und sehe keine änderung, das js-script wird trotzdem verarbeitet:-(

  10. Ich habe Dir gemailt wegen dem PHP-Code und werde den hier ergänzen. Grundsätzlich hängt SCRIPT_NAME all das zusätzliche gewurstel nicht an…

  11. So ein Quatsch. Der Request-Pfad kommt halt von außen, und alles was von außen kommt muss in einem Kontext, in dem bestimmte Zeichen eine Funktion haben, escaped werden. In diesem Fall ist der Kontext ein HTML-Quellcode und darin haben bekanntermaßen <, >, & und " eine Funktion.

    Wie wär’s mal mit

    <form action="<?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?>" method ="post">

    ???

    Das ist absolut grundlegendes Handwerkszeug.

  12. Hm?
    – Was ist besser: Infos von Aussen, escaped: PHP_SELF
    – Infos, die gar nicht vom bösen Internet berührt werden: SCRIPT_NAME

    Deine Lösung geht so auch, ich empfinde sie aber als suboptimal ohne Zusatznutzen.

    Ausserdem: Der Ton macht die Musik.

  13. Und wo genau steckt hier die Unsicherheit? Ja, du als „böser Hacker“ kannst dir selbst einen Javascript-Code ausgeben, oder dir selbst deine Cookies auslesen, oder für dich selbst die Anzeige der Seite manipulieren. Aber wie willst du andere User dazu bringen diesen „Exploit“ zu verwenden, bzw damit an Daten anderer User kommen? Ich sehe dazu keine Möglichkeit ehrlichgesagt.

  14. Nun, hier einige Möglichkeiten:

    • Link mailen (Browser zeigt ja die vertrauenswürdige Hostadresse in der URL)
    • Link auf eigener Seite Posten (Browser zeigt ja die vertrauenswürdige Hostadresse in der URL)
    • Link auf eigener Seite Posten und hoffen, dass eine Suchmaschine ihn so indiziert
    • Link so irgendwo posten

    Wenn die Leute dann draufklicken kann ich ihre Cookies auslesen… Oder habe ich Dein Anliegen falsch verstanden?

  15. Pingback: PHP-Self und du mich auch | Medienbeckerei Blog

  16. Alles was nach einem „Daten-Trenner“ aussieht (sprich “ ‚ <– diese verwende ich), wird bei mir als erstes in den entsprechenden ASCII-Code umgewandelt.

    function CLEAR_USER_VARS($var){
        $var=eregi_replace("'",'&#039;',$var);
        $var=eregi_replace('"','&#034;',$var);
        $var=strip_tags($var);

        return $var;
    }
    if(isset($_POST)){foreach($_POST as $pk => $pw){$_POST[$pk]=CLEAR_USER_VARS($_POST[$pk]);}
    if(isset($_GET)){foreach($_GET as $gk => $gw){$_GET[$gk]=CLEAR_USER_VARS($_GET[$gk]);}
  17. Aber das putzt PHP_SELF nicht, oder?

    Doch, genau das tut es und jede andere Schwachstelle auch SQL-Injection z.B. . Wenn man Trennzeichen vorher entfernt, können keine Anweisungen unterbrochen werden. Außerdem braucht man bei Formularen das Action-Attribut nicht reich aus.

  18. Ist nur ein Detail, aber Du putzt $_SERVER nitte, damit auch nicht PHP_SELF. Wenn Du das aber tust und die Codierung so tragbar ist für die App, dann bist Du mit Deinem Code sicher gut unterwegs.

  19. Pingback: Umgehen von PHPIDS « Web-Sicherheit

  20. das ist ja intressant was hier steht. Btreibe ein Joomla
    und die seite wird mit eval base {yxyjjjrejjej) reintext
    serverumleitung manipuliert >> verseucht. Nun hat jemand ne Ahnung wie das geht? Dank für Antwort

  21. Nun, evtl werden die über eine Schwachstelle reingekommen sein und das eval im Nachhinein eingefügt haben. Wenn eine ältere Joomlaversion eingesetzt wird gibt es genügend Exploits…

  22. Wow, danke für diesen schönen Artikel! Hab auch immer $_SERVER[‚PHP_SELF‘] verwendet, doch als ich es mal gegoogelt habe bin ich sofort hierauf gestoßen.

    Also danke nochmal für den Tipp 🙂

  23. Pingback: Was haltet ihr von PHP Self??? - XHTMLforum

  24. Das in diesem Artikel beschriebene Problem ist zwar teilweise richtig, aber die angebotene Lösung schlecht, denn sie schneidet auch jegliche Parameter weg, die eventuell doch gewünscht sind. Anstatt irgendwas zu filtern, rauszuschneiden oder andere Servervariablen zu benutzen, sollte man in diesem Fall einfach gar nichts ausgeben. Eine Form mit action=““ sendet die Formulardaten an sich selbst (inklusive aller Parameter in der URI) und es gibt keine Sicherheitslücke, weil nichts von außen augegeben wird.

  25. Das ist etwas resolut formuliert. HTML5 erlaubt keine leeren Actions. Bei älteren Versionen ist es zwar usus, aber nicht Standardkonform.

    Wie meinst Du das mit den Parametern? Hast Du GET-Parameter (die mit ? &) in der Action-URL? Auch das ist definitiv nicht nach Standard.

    Ich habe die besten Erfahrungen mit Standardkonformität gemacht, aber das ist natürlich nicht universell.

  26. Das ist so nicht korrekt dargestellt. Die Apache-Direktive AcceptPathInfo ist keineswegs standardmäßig auf „On“ gestellt, im Gegenteil, auf „On“ steht sie in den seltensten Fällen. Die Voreinstellung ist „default“. Das bedeutet, dass der jeweilige Handler bestimmt, ob die PATH_INFO-Zugriffe zugelassen werden oder nicht. Der Core-Handler für gewöhnliche Dateien weist sie standardmäßig zurück. Nur Handler für spezielle Skripte, beispielsweise für cgi oder isapi akzeptieren sie.

  27. Ich muss meinem Kommentar noch etwas hinzufügen: Viele Webhoster, vor allem Massen-Webhoster, lassen CGI-PHP laufen. In dem Falle ist diese XSS-Methode wieder möglich. Falls man selbst einen Webserver betreibt, oder zumindest Einfluss darauf hat, dass PHP als Modul des Apache läuft, besteht die Gefahr nicht; jedenfalls nicht in der Standardeinstellung.

    Da man aber grundsätzlich auch mal von einem Server auf einen anderen umziehen kann oder ähnliches ist es grundsätzlich immer richtig auf PHP_SELF zu verzichten.

  28. Hm?
    – Was ist besser: Infos von Aussen, escaped: PHP_SELF
    – Infos, die gar nicht vom bösen Internet berührt werden: SCRIPT_NAME

    Deine Lösung geht so auch, ich empfinde sie aber als suboptimal ohne Zusatznutzen.

    Weil du dich mit Sicherheitskonzepten und den Protokollen scheinbar nicht auskennst. Ausnahmslos alle externen Informationen müssen immer validiert werden. Es spielt dabei überhaupt keine Rolle woher diese Informationen kommen oder wo sie gespeichert werden. Du scheinst den Zweck dieser beiden Variablen auch nicht wirklich zu kennen. Stattdessen setzt du auf die eine Variable von der du keine Angriffsmöglichkeiten kennst und hoffst einfach, dass das auch so bleibt. Du vermittelst den Leuten ein falsches Denken was die Sicherheit im Internet angeht. Es ist grundsätzlich richtig fremde Daten korrekt zu filtern anstatt die entsprechende Technik einfach zu meiden. Nichts ist sicher und PHP_SELF ist nur eine von vielen Variablen. Die anderen schmeißt du ja auch nicht raus sondern filterst sie.

    Hast Du GET-Parameter (die mit ? &) in der Action-URL? Auch das ist definitiv nicht nach Standard.
    Das ist völlig irrelevant (in mehrfacher Hinsicht). Dann tippt man die URL eben direkt rein. Auch hier zeigt sich, dass du das HTTP-Protokoll nicht wirklich verstehst. Natürlich ist es (ein) Standard Parameter in die URI zu schreiben. Du machst allerdings den Fehler von HTML als Quelle auszugehen (wie die meisten Leute) und vergisst, dass HTTP und PHP überhaupt nichts mit HTML zu tun haben, was die Frage nach Standard / Nicht-Standard überflüssig macht.

    Ich hoffe für dich, dass du kein Informatiker bist…

  29. Vielen Dank für Deine Erleuchtung Du mächtiger anonymer Informatiker. Ich hoffe, Deutsch ist nicht Deine Muttersprache, denn bei Leseverständnis und Artikulation gibt es bei Dir noch Potential zu verwirklichen.

    Ich hätte ja gerne etwas zum Inhalt entgegnet, aber Du stehst nicht mit Deinem Namen dazu und das zeigt mir um was es geht: Allgemeinplätze eines Anfängerkurses hinrotzen und abhauen. *gähn*.

  30. Unter einem Pseudonym haben meine Worte keine Bedeutung für dich, Herr „Skaldrom“? Zum Argumentieren benötigst du meinen Namen nicht (ich bin übrigens der einzige hier unter dem Pseudonym „Gast“ – sollte eigentlich nicht so schwer sein mich von anderen zu unterscheiden) und Deutsch ist tatsächlich nicht meine Muttersprache (falls du andere Menschen danach beurteilst). Aber es ist interessant welche Ausreden sich Leute ausdenken wenn sie keine Argumente mehr haben. Es ist tatsächlich so – das Erste was man lernt ist: Traue keinen Benutzereingaben / Daten immer validieren. Hält man sich daran, gibt es auch kein Problem mit PHP_SELF. PHP_SELF ist auch nicht das Problem, denn es tut genau das was es soll, sondern der Programmierer der ohne nachzudenken alle Daten ungefiltert passieren lässt. Dein Ansatz im Artikel greift an falscher Stelle. Trauriger ist es, dass du scheinbar selbst erkennst dass es Anfängerwissen ist, es aber an keiner Stelle erwähnst und stattdessen denn Leuten den falschen Weg zeigst.
    Und ja, ich bin Informatiker…

    Eine Gegenfrage: Was machst du bei globalen Variablen wie _POST, die viel häufiger bei Angriffen eingesetzt werden? Nutzt du sie ebenfalls nicht oder filterst du sie – und wenn du sie filterst, warum machst du das dann bei PHP_SELF anders? Ich wäre an deiner Begründung wirklich interessiert, denn mir erschließt sich deine Vorgehensweise bis jetzt leider nicht.

  31. Mein Flammenwerfer ist ziemlich eingerostet, aber für das hier reicht er noch.

    Ich beurteile die Leute vor allem danach wie sie sich benehmen, und Du hast Dich hier unfreundlich und beleidigend eingeführt.

    Es gibt feine Unterschiede zwischen „total anonym“ und „mit Pseudonym“. Hättest Du eine Mailadresse hinterlassen, hätten wir das nichtöffentlich (mit Pseudonym wenn Du möchtest) ausdiskutieren können. Aber da kann man sich nicht so schön aufplustern, gell.

    Nun noch ganz kurz zur Sache, obwohl ich nicht glaube dass Du wirklich an einer Fachdiskussion interessiert bist: Klar muss man die Eingaben filtern, aber um das geht es hier nicht zentral. Das Thema war, dass es Beispiele gibt, bei denen PHP_SELF für die Form-Action verwendet wird und dass das nicht gut ist. Es wird auch eine funktionierende und sichere Lösung gezeigt. Es tut mir Leid, wenn Du Dich hier übergangen gefühlt hast.

    Mehr möchte ich dazu nicht mehr sagen und ich werde diese öffentliche Diskussion hier abbrechen. Wenn Du konstruktiv diskutieren willst, hinterlass doch bitte eine Mailadresse.

  32. Ich bin ehrlich gesagt gerade ein bisschen verwirrt, erstens weil ich nicht wusste das es veränderbar ist und zweitens ich keine „Sicherheitslücke“ entdecken kann.
    Schliesslich wirkt sich das ganze, wie es dargestellt wird (im Action-Attribut der Form), nur für den „Hacker“ selber aus. Solange man es nur so in Form einer Form 🙂 verwendet schadet es doch niemandem?
    Das ganze ist natürlich dann anders, wenn du z. Bsp. die Pfadangabe ($_SERVER[‚PHP_SELF‘]) in der Datenbank speicherst und nachher irgendwo z. Bsp. auf einer Profilseite ausgibst. Aber spätestens wenn du sie in die Datenbank einträgst sollte man es escapen, was ich auch standardmässig bei JEDEM Wert (ausser Literalen), der eingetragen wird mache.

  33. Der Hacker könnte aber einen Link konstruieren um den Effekt zu erzeugen. Dieser Link kann auf einer Website abgelegt oder direkt gemailt werden. Sobald ein Opfer auf diesen Link klickt, wirkt sich der Effekt nicht mehr nur auf den Hacker aus.

    Effekte sind vielfältig:
    – Ich kann den Inhalt der Website ändern. Das Opfer sieht dann beispielsweise gefälschte Neuigkeiten, gefälschte Aktienkurse, gefälschte Preise, … Selbst mit HTTPS gibts hier keine Warnung.
    – Der Link geht auf die tatsächliche Seite, der Host-Teil der URL stimmt perfekt. Mittels eingebautem Javascript kann ich aber das Opfer auf meine Seite umleiten.
    – Ev. kann ich mittels eingebautem, eigenem Javascript Formulareingaben des Opfers abfangen.

    XSS sind immer böse. ganz egal ob es dazu einen konstruierten Link braucht oder nicht, und PHP_SELF ausnutzen ist eigentlich einfach eine Form des XSS.

    Konnte ich meine Gedanken etwas klarer machen?

  34. Stimmt, so gesehen könnte er Phising betreiben. Danke für deine Erklärung, werde das in Zukunft berücksichtigen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.