Archiv der Kategorie ‘Webtechnik‘

 
 

Froxlor Bug: maximale Länge von ServerAlias (Apache)

SysCP bzw. dessen Nachfolger Froxlor kümmert sich hier auf diesem Server um das Erstellen diverser Konfigurationsdateien sowie um die Verwaltung der virtuellen User für E-Mail- und FTP-Zugänge.

Schon vor einiger Zeit habe ich festgestellt, dass bei Verwendung vieler Alias-Domains die erzeugte Apache-Konfiguration fehlerhaft sein kann. Dann nämlich, wenn die ServerAlias-Direktive mehr als 8000 Zeichen hat. Scheinbar hat noch keiner zuvor so viele Alias-Domains einer Hauptdomain hinzugefügt. 8000 Zeichen hört sich auch viel an, doch wenn pro Domain jeweils noch die www-Subdomain hinzukommt, reichen bereits ca. 150 Domains um dieses Limit zu erreichen.

Der Apache vHost-Container unterstützt mehrere ServerAlias-Direktiven, so dass bevor die 8000 Zeichen erreicht werden einfach ein neuer ServerAlias-Eintrag erzeugt werden sollte. Die Datei cron_tasks.inc.http.10.apache.php in scripts/jobs ist für das Erstellen der Apache-Konfigurationsdateien verantwortlich und mit wenigen Zeilen auf dieses Verhalten anpassbar (Patch).

Da ich nicht nach jedem Update daran denken möchte, diese Änderung einzuspielen, habe ich bei Froxlor mal ein Ticket erstellt und diesen Patch angehängt. Vielleicht schafft er es ja in die 0.9.27 :)

PHP und Y2K38: Werden wir alle sterben?

Der 19. Januar 2038 3:14:07 Uhr ist ein besonderer Zeitpunkt. Dann sind nämlich genau 2147483647 Sekunden seit dem 1. Januar 1970 vergangen. PHPs date()-Funktion arbeitet genau auf dieser Grundlage – auch bekannt als der UNIX-Timestamp.

Der Unix-Timestamp ist in PHP sehr populär. Funktionen wie date() oder strtotime() arbeiten mit UNIX Timestamps. Das ist auf den ersten Blick auch unheimlich praktisch, weil sich damit relativ platzsparend Daten (Plural von Datum) speichern lassen. Außerdem lässt es sich mit einem Timestamp leicht rechnen.

Ein PHP signed Integer auf 32bit-Systemen kann Werte zwischen -2147483648 und 2147483647 annehmen. Am 19. Januar 2038 3:14:08 läuft der Int also über und es ist plötzlich der 13. Dezember 1901 20:45:52 Uhr (Freitag der 13.!).

Folgendes Beispiel zeigt das Verhalten auf 32bit-Systemen. Mit einem 64bit-System und 64bit PHP kann PHPs unsinged Int Werte bis 9223372036854775807 annehmen. Dort stellt sich die Y2K38-Frage also garnicht. Hier stellt sich dann die Jahr 292471210689-Frage – aber bis dahin sind 64bit Systeme genau so ausgestorben, wie es 2038 die 32bit-Systeme sein werden.

var_export(strtotime("20 jan 2038")); // false :(

PHP CLI Fortschrittsbalken

Hin und wieder führt man PHP-Skripte direkt auf der Kommandozeile (CLI) aus. Das hat den Vorteil, dass man nicht den Umweg über den Webserver gehen muss, wenn man ihn garnicht braucht. Außerdem lässt sich das Skript leicht mit STRG+C abbrechen, den Webserver hingegen müsste man bei einer Endlosschleife neu starten.

Der aktuelle Fortschritt lässt sich auch ganz einfach mit echo ausgeben – kein (ob_)flush notwendig.

Wenn man mit echo einen Carriage Return ("\r") ausgibt, wird der Cursor auf den Anfang der Zeile zurückgestellt und man kann eine bereits ausgegebene Zeile überschreiben. Perfekt also für einen Fortschrittsbalken!

$total = 10;
$bar_length = 20;
$spinner = '-\\|/';
for ($i = 1; $i <= $total; $i++) {
    usleep(1000000);

    $spin = $spinner[$i%strlen($spinner)];    
    $cur = sprintf('%'.strlen($total).'.d', $i);
    $percent = $i/$total*100;

    $progress_len = floor($bar_length * $percent / 100);
    $progress = str_repeat('=', $progress_len);
    if ($progress_len < $bar_length) {
        $progress .= '>';
        $progress .= str_repeat('-', $bar_length-$progress_len-1);
    }

    echo " $cur/$total $spin [$progress] $percent%\r";
}

PHP-Security! Heute: path traversal

Path traversal bzw. directory traversal ist eine Methode, um aus vorgesehenen Verzeichnissen auszubrechen. In Bezug auf PHP findet diese Sicherheitslücke  Anwendung, da Dateien oftmals anhand des Querystrings eingebunden werden. Beispiel:

http://example.org/index.php?site=impressum.php
<?php
    include './includes/sites/' . $_GET['site'];
?>
Das ist natürlich fatal. Mittels ../ in $_GET['site'] kann man das vorgegebene Verzeichnis verlassen und je nach Rechten auch sicherheitsrelevante Dateien ausgeben lassen. Denn: wird eine nicht-PHP-Datei mittels include eingebunden wird deren Inhalt 1:1 an den Browser gesendet.

Oftmals wird das Ausbrechen aus einem Verzeichnis dadurch versucht zu verhindern, indem ‘../’ aus $_GET['site'] entfernt wird.

$save = str_replace('../', '', $_GET['site']);
Auch ganz schlecht: Aus einem ....// würde ../, da str_replace bereits ersetzte Teile nicht nochmals ersetzt. Außerdem kommen durch URL-Encoding und unterschiedliche Directory-Separator (Linux/Windows) noch weitere Zeichen in Frage, die es erlauben aus einem Verzeichnis auszubrechen.

Sehr verbreitet ist auch die Dateiendung vorzugeben:

include './includes/sites/' . $_GET['site'] . '.php';
Je nach Betriebssystem ist hier mit einem NULL-Byte (Url-Encoded: %00) die vorzeitige Terminierung des Strings möglich:
http://example.org/index.php?site=../../../../../../../etc/passwd%00
Hier muss man übrigens nicht die exakte Anzahl der nach oben zu gehenden Verzeichnisse wissen – ist man im Root-Verzeichnis angelangt, können beliebige ../ ohne Auswirkungen folgen.

Mit aktiviertem allow_url_fopen gibt es noch weitere Zeichen zu prüfen – das ist dann aber kein path traversal mehr und somit nicht bestandteil dieses Blog-Beitrages.

Was tun?

Statt str_replace sollte man besser preg_replace mit einem Pattern wie #\.+[/\]+# verwenden. Noch besser ist es natürlich von vorne herein eine Whitelist zu führen – also ein Array mit erlaubten Dateinamen und dann prüfen, ob $_GET['site'] in diesem Array vorkommt. Eine weitere Methode ist, den kanonischen, absoluten Pfad mittels realpath() zu erzeugen und dann den Beginn dieses Pfades mit dem Document-Root (bzw. dem erlaubten Verzeichnis) abzugleichen.

Variablen tauschen ohne Hilfsvariable?

Pah!

list($a, $b) = array($b, $a);