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?

Zertifikat erstellen und http-Anfragen auf https umleiten

Um zu einem SSL- Zertifikat zu kommen, kann unter Debian folgendes Kommando verwendet werden:

apache2-ssl-certificate

Dieses könnte man nun signieren lassen oder wenn man nur die Verschlüsselungseigenschaften braucht einfach so belassen. Wenn Apache das Modul mod_ssl geladen hat, kann man nun noch die Datei ans richtige Ort bewegen (Siehe SSLCertificateFile Parameter weiter unten).

Port 443 ist der Port auf dem der Apache auf https Verbindungen hört. Dass er dort ein Ohr platziert muss /vserver/webv2/etc/apache2/ports.conf angepasst werden;

Listen 80
Listen 443

Man hat ein schönes Zertifikat mit ganz vielen Bitlein und nun sollen die Benutzer dies auch gefälligst nutzen. Auf gut Deutsch: man zwingt alle http-Anfragen auf https (http://sicher.oncode.info soll zu https://sicher.oncode.info werden). mod_rewrite für Apache kann das gut, sehr gut sogar und erst noch automatisch. Dafür muss Apache folgendermassen konfiguriert werden:

Datei /vserver/webv2/etc/apache2/sites-available/default

NameVirtualHost *:443
NameVirtualHost *:80

<virtualhost *:80>
        RewriteEngine On
        RewriteCond %{HTTPS} !=on
        RewriteRule ^/(.*) https://%{SERVER_NAME}%{REQUEST_URI} [R]
</virtualhost>

<virtualhost *:443>
        ServerName sicher.oncode.info
        ServerAdmin apache.admin@onc0de.info
        SSLEngine On
        SSLCertificateFile /etc/apache2/ssl/apache.pem
[...]
</virtualhost>

Ein nettes Tutorial gibt es auch auf Tim Bormans Blog.