Wie PHP im Web läuft
Bei jeder Anfrage startet PHP das Skript, erzeugt die Antwort und beendet sich wieder (shared nothing). Dabei füllt PHP automatisch Superglobals:
| Variable | Inhalt |
|---|---|
$_GET | Parameter aus der URL (?name=Mia) |
$_POST | Formulardaten aus dem Body |
$_SERVER | Anfrage-Infos (REQUEST_METHOD, REQUEST_URI, HTTP_HOST) |
$_COOKIE | Cookies |
$_SESSION | Sitzungsdaten (nach session_start()) |
$_FILES | Datei-Uploads |
Zum Ausprobieren: php -S localhost:8000 im Projektordner und http://localhost:8000/formular.php öffnen.
Ein Formular verarbeiten
<?php
declare(strict_types=1);
$fehler = [];
$name = "";
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$name = trim($_POST["name"] ?? "");
$alter = filter_input(INPUT_POST, "alter", FILTER_VALIDATE_INT, ["options" => ["min_range" => 0, "max_range" => 120]]);
if ($name === "") $fehler[] = "Name fehlt.";
if ($alter === false || $alter === null) $fehler[] = "Alter ungültig.";
if (!$fehler) {
// speichern ... dann weiterleiten (Post/Redirect/Get)
header("Location: /danke.php");
exit;
}
}
?>
<form method="post">
<label>Name <input name="name" value="<?= htmlspecialchars($name) ?>"></label>
<label>Alter <input name="alter" type="number"></label>
<button>Senden</button>
</form>
<?php foreach ($fehler as $f): ?>
<p class="fehler"><?= htmlspecialchars($f) ?></p>
<?php endforeach; ?>Regeln für Formulare:
- Validieren auf dem Server (Browser-Prüfung ist nur Komfort)
- Escapen bei der Ausgabe (
htmlspecialchars) - Post/Redirect/Get: Nach dem Speichern umleiten, damit "Neu laden" nichts doppelt sendet
Sessions und Cookies
Eine Session merkt sich Daten pro Besucher auf dem Server; im Browser liegt nur die Session-ID als Cookie.
<?php
session_start([
"cookie_httponly" => true,
"cookie_samesite" => "Lax",
"cookie_secure" => true, // nur über HTTPS
]);
if ($_SERVER["REQUEST_METHOD"] === "POST") {
// Passwort immer gehasht prüfen
if (password_verify($_POST["passwort"] ?? "", $gespeicherterHash)) {
session_regenerate_id(true); // gegen Session-Fixation
$_SESSION["nutzer_id"] = $nutzerId;
}
}
$_SESSION["besuche"] = ($_SESSION["besuche"] ?? 0) + 1;
setcookie("theme", "dunkel", ["expires" => time() + 86400 * 30, "path" => "/", "httponly" => true, "samesite" => "Lax"]);
// Abmelden:
// $_SESSION = []; session_destroy();Passwörter richtig speichern
Passwörter werden nie im Klartext oder mit md5/sha1 gespeichert, sondern mit einem langsamen Hash und Salt:
<?php
$hash = password_hash("geheim123", PASSWORD_DEFAULT); // Bcrypt/Argon2, Salt automatisch
echo strlen($hash) >= 60 ? "Hash ok" : "kurz", " ", str_starts_with($hash, '$2y$') ? "bcrypt" : "anderer", "\n";
var_dump(password_verify("geheim123", $hash), password_verify("falsch", $hash));
var_dump(password_needs_rehash($hash, PASSWORD_DEFAULT));
echo bin2hex(random_bytes(4)) !== "" ? "Zufall ok" : "", " ", random_int(5, 5), " ", hash("sha256", "abc"), "\n";Ausgabe
Hash ok bcrypt bool(true) bool(false) bool(false) Zufall ok 5 ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
Die wichtigsten Angriffe und Gegenmittel
| Angriff | Gegenmittel in PHP |
|---|---|
| SQL-Injection | Prepared Statements (PDO) |
| XSS (Skript im HTML) | htmlspecialchars($text, ENT_QUOTES, 'UTF-8') bei jeder Ausgabe |
| CSRF (fremde Seite löst Aktion aus) | CSRF-Token im Formular, SameSite-Cookies |
| Session-Fixation/-Diebstahl | session_regenerate_id(true), httponly, secure |
| Unsichere Uploads | Typ/Größe prüfen, neuen Namen vergeben, außerhalb des Webroots speichern |
| Schwache Passwörter | password_hash / password_verify |
| Offene Fehlerausgabe | display_errors=0 in Produktion |
| Include aus Nutzereingabe | nie include $_GET['seite'] (Pfad-/Code-Einschleusung) |
CSRF-Token
<?php
session_start();
// Beim Anzeigen des Formulars
$_SESSION["csrf"] ??= bin2hex(random_bytes(16));
$token = $_SESSION["csrf"];
echo '<input type="hidden" name="csrf" value="', strlen($token), ' Zeichen">', "\n"; // im echten Formular: den Token selbst ausgeben
// Beim Verarbeiten (simuliert)
$gesendet = $token;
var_dump(hash_equals($_SESSION["csrf"], $gesendet)); // zeitkonstanter VergleichAusgabe
<input type="hidden" name="csrf" value="32 Zeichen"> bool(true)
Eine einfache Antwort als API
<?php
header("Content-Type: application/json; charset=utf-8");
$method = $_SERVER["REQUEST_METHOD"];
if ($method === "GET") {
echo json_encode(["status" => "ok", "zeit" => date(DATE_ATOM)]);
} elseif ($method === "POST") {
$body = json_decode(file_get_contents("php://input"), true, 512, JSON_THROW_ON_ERROR);
http_response_code(201);
echo json_encode(["empfangen" => $body]);
} else {
http_response_code(405);
echo json_encode(["fehler" => "Methode nicht erlaubt"]);
}Merke
- Pro Anfrage läuft das Skript neu; Superglobals liefern die Eingaben
- Eingaben validieren, Ausgaben escapen, Datenbank mit Prepared Statements
- Sessions mit
session_start,httponly/secure/samesite,session_regenerate_id - Passwörter:
password_hashundpassword_verify - CSRF-Token und
hash_equals, Fehleranzeige in Produktion aus - Post/Redirect/Get nach dem Speichern
Aufgabe
Baue ein Kontaktformular mit Validierung, CSRF-Token und Anzeige der Fehler.