mitmario.dev

Was der Server verrät

PHP Sandbox 3 Min Lesezeit 2 BeispieleLektion 5 von 7

Die letzten vier Lektionen waren Angriffe auf deinen Code: eine geklebte Abfrage, eine rohe Ausgabe, ein fehlendes Token, ein schwacher Hash. Diese hier ist anders. Sie handelt von Voreinstellungen und Dateien, die für sich harmlos sind und auf einem öffentlichen Server zum Problem werden. Die Regel dahinter passt in einen Satz: Öffentlich ist nur, was öffentlich sein muss. Alles andere liegt daneben.

Fehlermeldungen, noch einmal

Eine Fehlermeldung ist ein Bauplan
<?php

// Eine Fehlermeldung ist eine erstaunlich ergiebige Auskunft. Hier
// sieht man, wie viel eine einzige Meldung ueber eine Tabelle
// verraet, die man nie zu Gesicht bekommt.

$db = new PDO("sqlite::memory:", null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$db->exec("CREATE TABLE kunden (id INTEGER PRIMARY KEY, email TEXT, kreditkarte TEXT)");

foreach ([
    "SELECT * FROM kunde",
    "SELECT name FROM kunden",
    "INSERT INTO kunden (id) VALUES (1), (1)",
] as $sql) {
    echo "Abfrage: ", $sql, "\n";
    try {
        $db->exec($sql);
        echo "  (kein Fehler)\n\n";
    } catch (PDOException $e) {
        echo "  ", $e->getMessage(), "\n\n";
    }
}

echo "Jede Zeile nennt eine Tabelle, eine Spalte oder eine Regel.\n";
echo "Auf einer oeffentlichen Seite liest ein Angreifer daraus dein Schema.\n";

In Lektion 7.5 stand, dass eine Fehlermeldung auf einem öffentlichen Server nichts zu suchen hat. Das erste Beispiel zeigt, warum das so hart formuliert ist. Drei kaputte Abfragen, drei Meldungen, und jede verrät etwas: no such table: kunde sagt, wie die Tabelle nicht heißt, und legt damit nahe, wie sie heißt. no such column: name bestätigt, welche Spalten es gibt und welche nicht. Die UNIQUE-Meldung nennt Tabelle und Spalte in einem. Für dich sind das Hinweise zum Reparieren, für einen Angreifer ist es der Bauplan für das UNION SELECT aus Lektion 15.1.

Deshalb display_errors = Off auf einem öffentlichen Server, und log_errors = On, damit du die Meldung trotzdem liest, nur eben im Protokoll und nicht in der Auslage.

Die genaue Version, die du gratis mitlieferst

Was PHP von sich aus preisgibt
<?php

// Was PHP von sich aus verraet, ohne dass dein Code darum bittet.

echo "In jeder Antwort steht diese Kopfzeile, solange expose_php an ist:\n";
echo "  X-Powered-By: PHP/", phpversion(), "\n\n";

echo "Der aktuelle Wert der Einstellung dazu:\n";
echo "  expose_php = ", ini_get("expose_php") ? "On" : "Off", "\n\n";

echo "Warum das zaehlt: Die genaue Version sagt einem Angreifer, welche\n";
echo "bekannten Luecken er ueberhaupt probieren muss. Abschalten kostet\n";
echo "nichts: expose_php = Off in der php.ini, und die Zeile ist weg.\n\n";

echo "phpinfo() ist dasselbe in gross: eine ganze Seite mit Version,\n";
echo "Pfaden, Erweiterungen und Einstellungen. Praktisch zum Entwickeln,\n";
echo "eine Katastrophe auf einem oeffentlichen Server.\n";

PHP schreibt von sich aus eine Kopfzeile X-Powered-By: PHP/8.5.9 in jede Antwort, mit der Version auf die Nachkommastelle genau. Das klingt nach einer Kleinigkeit und ist eine Einladung: Ein Angreifer schlägt zu deiner Version die bekannten Lücken nach und probiert gezielt die, die passen könnten. expose_php = Off schaltet die Zeile ab. Nachsehen kannst du sie in diesem Kurs in jeder Lektion mit Server: im Reiter Netzwerk bei den Antwort-Kopfzeilen der Seite, oder im Terminal mit curl -si localhost:3000/ | grep X-Powered-By.

phpinfo() ist dieselbe Auskunft in groß: eine ganze Seite mit Version, Pfaden, geladenen Erweiterungen und dem kompletten Satz an Einstellungen. Beim Entwickeln praktisch, auf einem öffentlichen Server eine Katastrophe. Wer sie zum Testen anlegt, löscht sie danach.

Dateien, die nicht öffentlich sein sollten

Neben den Einstellungen sind es Dateien. Ein paar, die immer wieder liegen bleiben:

Eine Verzeichnisliste: Wenn in einem Ordner keine index.php liegt, zeigt mancher Server dessen Inhalt einfach an, jede Datei anklickbar. Ein Hinweis dazu, weil du es hier nicht nachstellen kannst: Der Server in diesem Kurs macht das nicht, er antwortet auf einen Ordner ohne Indexdatei mit seiner eigenen 404-Seite. Ein Apache mit der Einstellung Options +Indexes macht es sehr wohl, und dann liegt dein ganzer Ordner offen.

Sicherungsdateien: index.php.bak, konfiguration.php~, zugang.php.alt. Der Server führt .bak und ~ nicht als PHP aus, sondern liefert sie als Text, mitsamt Passwörtern und Schlüsseln, die im Original nie sichtbar wären. Auch der Server dieses Kurses: Leg in einer Lektion mit Server im Terminal eine index.php.bak an, und curl -s localhost:3000/index.php.bak gibt ihren Quelltext zurück.

Ein .git-Ordner im öffentlichen Verzeichnis: Damit lädt sich jemand die komplette Geschichte deines Codes herunter, alte Passwörter eingeschlossen, die du längst geändert zu haben glaubtest.

Und die Datenbank selbst: Deine SQLite-Datei ist eine gewöhnliche Datei. Liegt sie im öffentlichen Verzeichnis, kann man sie herunterladen und in Ruhe zu Hause auslesen. Sie gehört daneben, nicht dazwischen.

Die Antwort ist eine Verzeichnisstruktur

All das läuft auf denselben Gedanken hinaus: Trenne, was der Browser sehen darf, von allem anderen. Öffentlich ist ein einziger Ordner mit den Seiten, die aufgerufen werden. Datenbank, Konfiguration, eigener Code und Sicherungen liegen eine Ebene höher, außerhalb der Dokumentwurzel, und der Server kommt gar nicht erst an sie heran. Genau diese Struktur baut Abschnitt 16 auf, und die nächste Lektion setzt sie zum ersten Mal ein: Die Datenbank wandert nach draußen, und du weist im Terminal nach, dass ein Aufruf sie nicht mehr erreicht.

Eine .htaccess kann einzelne Dateien zusätzlich sperren, aber verlass dich nicht darauf: Sie gilt nur bei Apache, und ein falsch konfigurierter oder ausgetauschter Server ignoriert sie. Was gar nicht erst im öffentlichen Ordner liegt, kann keine vergessene Regel doch noch preisgeben.

Zum Mitnehmen

Nicht jede Lücke ist ein Angriff auf deinen Code. Manche ist eine Einstellung, die dir Arbeit abnimmt, solange du entwickelst, und dich verrät, sobald die Seite öffentlich ist. Dieselbe Bequemlichkeit, andere Bühne.

Jetzt du

Basis Konto, kostenlos

Zu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.

Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.

In diesem Kurs läuft dein Code auf einem Server. Dafür hat das Basis Konto 1 Stunde im Monat, mehr Zeit gibt es mit dem Premium Konto.

Was in dieser Lektion steckt

  • Artikel mit 2 Beispielen zum Ausprobieren

    Steht hier, ohne Konto lesbar.

  • Aufgabe, auf dem Server geprüft

    Öffnet sich mit dem Basis Konto.