Abschnitt 15 · Lektion 6
Die Liste absichern
Alles, was du in diesem Abschnitt gelernt hast, gehört bei jeder Anwendung an dieselbe Stelle: an den Anfang, als Liste, die man abarbeitet. Sechs Punkte, und jeder hatte seine eigene Lektion.
Die sechs Punkte
Platzhalter überall. Jeder Wert, der nicht im Quelltext steht, geht als Platzhalter in eine Abfrage, nie geklebt. Das ist Lektion 14.4 und 15.1, und es ist die wichtigste Zeile der Liste, weil eine einzige geklebte Abfrage reicht.
Ausgabe maskiert. Alles, was nicht aus deinem Code kommt, geht bei der Ausgabe durch
htmlspecialchars, im richtigen Zusammenhang: e() für Text, eine Schemaprüfung für Links,
json_encode für JavaScript. Lektion 11.4 und 15.2.
Token im Formular. Jedes Formular, das etwas ändert, trägt ein CSRF-Token, und die Gegenstelle
prüft es mit hash_equals. Lektion 15.3.
Passwörter gehasht. Nie im Klartext, immer mit password_hash und password_verify, und beim
Anmelden bei Bedarf angehoben. Lektion 12.5 und 15.4.
Fehlermeldungen aus. Auf dem öffentlichen Server display_errors = Off und log_errors = On,
damit der Besucher einen ruhigen Satz sieht und du die Einzelheiten im Protokoll. Lektion 7.5 und
15.5.
Datenbank außerhalb der Wurzel. Öffentlich ist nur, was öffentlich sein muss, alles andere liegt daneben.
Warum das der wichtigste Punkt zum Anschauen ist
<?php
// Eine Seite, deren Datenbank im selben Ordner liegt wie sie selbst,
// also im oeffentlichen Verzeichnis. Genau das ist der Fehler.
$db = new PDO("sqlite:" . __DIR__ . "/daten.db");
$db->exec("CREATE TABLE IF NOT EXISTS geheim (wert TEXT)");
if ((int) $db->query("SELECT count(*) FROM geheim")->fetchColumn() === 0) {
$db->exec("INSERT INTO geheim (wert) VALUES ('streng vertraulich')");
}
?>
<!doctype html>
<html lang="de">
<head><meta charset="utf-8"><title>Shop</title>
<style>body{font-family:system-ui;max-width:32rem;margin:2rem auto}</style></head>
<body>
<h1>Willkommen im Shop</h1>
<p>Die Datenbank dieser Seite liegt im selben Ordner wie diese Datei.
Ruf im Terminal <code>curl -s localhost:3000/daten.db | head -c 16</code>
auf und sieh dir an, was zurueckkommt: der Anfang der Datei, im
Klartext lesbar.</p>
</body>
</html> Den letzten Punkt kann man am eigenen Server sehen, und das erste Beispiel zeigt ihn. Die Datenbank
liegt im selben Ordner wie die Seite. Im Reiter Browser siehst du das nicht, und der Grund ist
selbst eine Auskunft: Der Server schickt die Datei ohne Angabe, was sie ist, ein Browser will so
etwas speichern statt anzeigen, und der Rahmen in diesem Kurs lässt keinen Download zu. Tippst du
/daten.db in die Adresszeile, passiert schlicht nichts. Im Terminal siehst du, was ein Angreifer
bekäme: curl -s localhost:3000/daten.db | head -c 16 gibt SQLite format 3 aus, und
curl -s localhost:3000/daten.db | strings | grep vertraulich findet den Eintrag, der nie
öffentlich sein sollte. Wer die Adresse errät, und daten.db ist keine schwer zu erratende Adresse,
lädt sich deine ganze Datenbank herunter.
<?php
// Liegt in privat/, ausserhalb des oeffentlichen Ordners. Der Browser
// kommt an diese Datei und an die Datenbank daneben nicht heran.
function datenbank(): PDO
{
$db = new PDO("sqlite:" . __DIR__ . "/daten.db");
$db->exec("CREATE TABLE IF NOT EXISTS geheim (wert TEXT)");
if ((int) $db->query("SELECT count(*) FROM geheim")->fetchColumn() === 0) {
$db->exec("INSERT INTO geheim (wert) VALUES ('streng vertraulich')");
}
return $db;
} <?php
// Das Einzige im oeffentlichen Ordner. Die Datenbank liegt eine Ebene
// hoeher, in privat/, und ist damit nicht mehr herunterladbar.
require __DIR__ . "/../privat/daten-anlegen.php";
datenbank();
?>
<!doctype html>
<html lang="de">
<head><meta charset="utf-8"><title>Shop</title>
<style>body{font-family:system-ui;max-width:32rem;margin:2rem auto}</style></head>
<body>
<h1>Willkommen im Shop</h1>
<p>Diesmal liegt die Datenbank in privat/. Ruf wieder
<code>/daten.db</code> auf, in der Adresszeile oder mit curl: Diesmal
kommt keine Datei zurueck, sondern diese Seite, denn im oeffentlichen
Ordner gibt es keine solche Datei.</p>
</body>
</html> Dasselbe noch einmal, nur liegt die Datenbank jetzt in privat/, eine Ebene über dem öffentlichen
Ordner. Ruf wieder /daten.db auf, diesmal geht es auch in der Adresszeile: Es kommt keine Datei
zurück, sondern die Startseite, und eine Seite kann der Rahmen zeigen. Der
Grund ist schlicht, dass es im öffentlichen Ordner keine Datei daten.db gibt, und der Server jeden
unbekannten Pfad an die index.php weiterreicht. Was nicht im öffentlichen Ordner liegt, kann man
nicht herunterladen, und keine vergessene Regel kann es doch noch preisgeben.
Warum die Liste kurz sein muss
Sechs Punkte kann man sich merken und vor jedem Livegang durchgehen. Zwanzig kann man das nicht, und eine Sicherheitsliste, die zu lang ist, um benutzt zu werden, ist keine. Deshalb steht hier das Wesentliche und nicht jede denkbare Vorsichtsmaßnahme. Die Aufgabe zu dieser Lektion nimmt die Notizanwendung und bringt sie durch alle sechs Punkte, und danach hast du an einem echten Beispiel gesehen, wie wenig sich für den ehrlichen Benutzer ändert und wie viel für den Angreifer.
Zum Mitnehmen
Kein neuer Stoff, sondern eine Liste zum Abhaken. Sechs Punkte, jeder aus einer Lektion dieses Abschnitts, und am Ende eine Anwendung, die durch alle sechs geht. Genau so eine Liste braucht man, damit beim nächsten Projekt keiner vergessen wird.
Jetzt du
Basis Konto, kostenlosZu 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, dein Code läuft auf einem Server
Öffnet sich mit dem Basis Konto.