Aktuelle Lage: PHP2Shell. Zwei kritische PHP-Lücken (CVSS 9.2) erlauben Schadcode ohne Anmeldung. Patches seit 21.10.2026.Was jetzt zu tun ist →

Sessions in PHP absichern: PHP-Session-Sicherheit in der Praxis

Sessions halten Nutzer angemeldet und sind damit ein lohnendes Ziel. So konfigurieren Sie Cookies, Session-IDs und Speicherorte sicher und setzen den PHP2Shell-Hinweis zum Upload-Progress um.

Stand 6 Min. LesezeitRatgeber

Kurz gesagt: Für die PHP-Session-Sicherheit zählen vier Dinge: sichere Cookie-Einstellungen, eine neue Session-ID nach jeder Anmeldung, getrennte und geschützte Speicherorte sowie kurze Laufzeiten. Wegen PHP2Shell sollte außerdem session.upload_progress.enabled=0 serverseitig gesetzt sein, solange kein Grund für diese Funktion besteht.

Warum Sessions ein lohnendes Ziel sind

Eine Session merkt sich, dass ein Nutzer angemeldet ist. Technisch erhält der Browser dafür eine zufällige Session-ID in einem Cookie, und der Server legt die zugehörigen Daten ab. Wer diese ID kennt, ist für die Anwendung derselbe Nutzer, ohne Passwort und ohne zweiten Faktor.

Für Betreiber bedeutet das: Eine schwache Session-Verwaltung kann den Login-Schutz aushebeln, auch wenn Passwörter und Zwei-Faktor-Anmeldung sauber umgesetzt sind. Besonders kritisch ist das bei Admin-Bereichen, Kundenkonten und Portalen mit personenbezogenen Daten.

Die typischen Risiken sind das Abgreifen der Session-ID (Session-Hijacking), das Unterschieben einer bekannten ID vor dem Login (Session-Fixation) und zu lange gültige Sitzungen auf geteilten Rechnern.

Sichere Grundeinstellungen in der php.ini

Die wichtigsten Werte lassen sich zentral in der php.ini oder pro Website im FPM-Pool setzen. Im Pool verwenden Sie php_admin_value oder php_admin_flag, damit die Anwendung die Werte nicht überschreiben kann.

; Session-Grundhärtung
session.use_strict_mode   = 1
session.use_only_cookies  = 1
session.use_trans_sid     = 0
session.cookie_httponly   = 1
session.cookie_secure     = 1
session.cookie_samesite   = "Lax"
session.cookie_lifetime   = 0
session.gc_maxlifetime    = 1800
session.name              = "APPSESSID"

; PHP2Shell-Hinweis von php.net, danach alle Worker neu starten
session.upload_progress.enabled = 0

use_strict_mode sorgt dafür, dass PHP keine vom Browser vorgegebenen, unbekannten IDs übernimmt. cookie_httponly verhindert, dass JavaScript die ID lesen kann, was die Folgen von Cross-Site-Scripting begrenzt. cookie_secure sendet das Cookie nur über HTTPS. SameSite=Lax erschwert Cross-Site-Anfragen mit gültiger Sitzung; für reine Admin-Bereiche kann Strict passen.

Die Laufzeit von 1.800 Sekunden ist ein Beispielwert. Legen Sie sie nach dem Schutzbedarf fest: kürzer für Verwaltung und Kundendaten, länger nur dort, wo es fachlich nötig ist.

Beachten Sie, dass gc_maxlifetime nur festlegt, ab wann alte Session-Daten gelöscht werden dürfen. Auf manchen Distributionen räumt nicht PHP selbst, sondern ein regelmäßiger Systemjob die Session-Dateien auf. Prüfen Sie dann, welchen Wert dieser Job verwendet, damit Ihre Einstellung tatsächlich greift. Verlassen Sie sich für das Abmelden nach Inaktivität außerdem nicht allein auf die Aufräumfunktion, sondern prüfen Sie den Zeitpunkt der letzten Aktion in der Anwendung selbst, wie im folgenden Beispiel gezeigt.

Session-ID nach dem Login erneuern

Die wichtigste Regel im Code ist, die Session-ID bei jedem Wechsel der Berechtigung neu zu erzeugen, also beim Login, beim Wechsel in einen Admin-Modus und beim Logout. Das folgende Beispiel zeigt ein sauberes Grundmuster mit password_verify():

<?php
declare(strict_types=1);

session_start();

function anmelden(PDO $db, string $email, string $passwort): bool
{
    $stmt = $db->prepare('SELECT id, passwort_hash FROM benutzer WHERE email = :email');
    $stmt->execute(['email' => $email]);
    $nutzer = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$nutzer || !password_verify($passwort, $nutzer['passwort_hash'])) {
        return false; // gleiche Meldung für falsche Adresse und falsches Passwort
    }

    session_regenerate_id(true);           // neue ID, alte verwerfen
    $_SESSION['benutzer_id']   = (int) $nutzer['id'];
    $_SESSION['letzte_aktion'] = time();
    return true;
}

function pruefeLeerlauf(int $sekunden = 900): void
{
    if (isset($_SESSION['letzte_aktion']) && time() - $_SESSION['letzte_aktion'] > $sekunden) {
        abmelden();
        return;
    }
    $_SESSION['letzte_aktion'] = time();
}

function abmelden(): void
{
    $_SESSION = [];
    $p = session_get_cookie_params();
    setcookie(session_name(), '', [
        'expires'  => time() - 3600,
        'path'     => $p['path'],
        'domain'   => $p['domain'],
        'secure'   => $p['secure'],
        'httponly' => $p['httponly'],
        'samesite' => $p['samesite'],
    ]);
    session_destroy();
}

Ergänzend schützen Sie Formulare, die etwas verändern, mit einem CSRF-Token. Es wird mit bin2hex(random_bytes(32)) erzeugt, in der Session abgelegt und beim Absenden mit hash_equals() verglichen.

Speicherorte und Mandantentrennung

Standardmäßig legt PHP Sessions als Dateien in einem gemeinsamen Verzeichnis ab. Auf einem Server mit mehreren Websites kann das bedeuten, dass eine kompromittierte Website die Sessions einer anderen lesen kann. Deshalb gehört zu jeder Website ein eigener Pool mit eigenem Benutzer und eigenem Session-Pfad.

; im FPM-Pool der jeweiligen Website
php_admin_value[session.save_path] = /var/lib/php/sessions/kunde-a

Das Verzeichnis sollte dem Pool-Benutzer gehören und die Rechte 0700 haben. Wie Sie Websites zusätzlich voneinander abschotten, beschreibt der Ratgeber open_basedir und Isolation pro Website. Werden Sessions in Redis oder einer Datenbank gespeichert, gelten dieselben Grundsätze: eigener Zugang je Anwendung, kein offener Netzwerkzugriff.

PHP-Session-Sicherheit und PHP2Shell

Die PHP2Shell-Lücken CVE-2026-109618 und CVE-2026-109619 liegen in PHP selbst. Als eine der Übergangsmaßnahmen nennt php.net, session.upload_progress.enabled=0 in der php.ini, im FPM-Pool oder in der Serverkonfiguration zu setzen und danach alle Worker neu zu starten. Ausdrücklich nicht über ini_set() oder eine .user.ini.

Am 23.10.2026 folgte ein Hinweis zu CVE-2026-115560. Eine mögliche Codeausführung besteht dort nur bei einer ungewöhnlichen Kombination aus Session-Serialisierung, Umstellung per ini_set() vor session_start() und aktivem Upload-Progress. Die Abhilfe ist dieselbe Einstellung, bis zum nächsten regulären Bugfix-Release.

Wichtig

Diese Einstellung ist eine Übergangsmaßnahme und kein vollständiger Schutz. Entscheidend bleibt das Update auf eine behobene PHP-Version und der anschließende Neustart aller PHP-Worker.

Prüfen Sie nach der Änderung, welcher Wert tatsächlich gilt. Die Kommandozeile zeigt oft eine andere Konfiguration als der Webserver; aussagekräftig ist eine geschützte, danach wieder gelöschte Prüfseite oder die Pool-Konfiguration selbst.

Typische Fehler bei Sessions in PHP-Anwendungen

Viele Schwachstellen entstehen nicht durch fehlende Einstellungen, sondern durch die Art, wie Anwendungen Sessions verwenden. Diese Muster sollten Sie im eigenen Code und bei Dienstleistern gezielt abfragen:

  • Zu viel in der Session: Passwörter, vollständige Zahlungsdaten oder große Datensätze gehören nicht in die Session. Speichern Sie nur, was für die Zuordnung nötig ist, etwa die Benutzer-ID und Rollen.
  • Rechte nur aus der Session: Wer Admin ist, sollte bei kritischen Aktionen gegen die Datenbank geprüft werden. Sonst bleiben entzogene Rechte bis zum Ablauf der Sitzung wirksam.
  • Session-ID in der Adresse: IDs in URLs landen in Logs, Browser-Verläufen und Referer-Angaben. use_only_cookies und use_trans_sid = 0 verhindern das.
  • Kein Ende der Sitzung: Fehlt ein Leerlauf-Timeout, bleibt ein Admin auf einem geteilten Rechner unbegrenzt angemeldet.
  • HTTPS hinter einem Proxy falsch erkannt: Endet die Verschlüsselung am Load Balancer, muss die Anwendung das wissen. Sonst setzt sie Cookies womöglich ohne Secure-Flag.

Nach einem Sicherheitsvorfall gehört außerdem dazu, alle bestehenden Sessions zu verwerfen. Am einfachsten geschieht das, indem Sie den Inhalt des Session-Verzeichnisses löschen oder den Session-Namen ändern. Alle Nutzer müssen sich dann neu anmelden, und gestohlene Sitzungen sind wertlos.

Wie hilft PHP SOS konkret?

  • Wir prüfen Session-Einstellungen, Cookie-Flags und Speicherorte auf allen Websites eines Servers und dokumentieren die tatsächlich wirksamen Werte.
  • Wir setzen die PHP2Shell-Übergangsmaßnahmen pro Pool, starten die Worker kontrolliert neu und weisen das Ergebnis nach.
  • Wir trennen Websites mit eigenen Pools und Session-Pfaden im Rahmen der Server-Härtung.
  • Für Eigenentwicklungen prüfen wir Login, Logout und Session-Wechsel im Code und empfehlen konkrete Änderungen.

Häufige Fragen

Was ist Session-Hijacking?

Beim Session-Hijacking übernimmt ein Angreifer die Sitzung eines angemeldeten Nutzers, indem er dessen Session-ID erlangt. Mit der ID ist er für die Anwendung dieser Nutzer, ohne das Passwort zu kennen. Sichere Cookie-Flags und kurze Laufzeiten erschweren das deutlich.

Warum muss die Session-ID nach dem Login neu erzeugt werden?

Bleibt die ID vor und nach dem Login gleich, könnte eine vorher bekannte oder untergeschobene ID nach der Anmeldung weiter gelten. session_regenerate_id(true) erzeugt eine neue ID und verwirft die alte.

Kann ich session.upload_progress.enabled per ini_set() abschalten?

Nein, das genügt laut php.net nicht. Die Einstellung gehört in die php.ini, den FPM-Pool oder die Serverkonfiguration, nicht in ini_set() oder eine .user.ini. Danach müssen alle PHP-Worker neu gestartet werden.

Wo sollten Session-Dateien gespeichert werden?

In einem Verzeichnis außerhalb des Webroots, das nur der Benutzer des jeweiligen PHP-Pools lesen darf. Auf Servern mit mehreren Websites sollte jede Website ein eigenes Session-Verzeichnis haben.

Quellen

Sofort-Kontakt

Jede Stunde zählt. Schreiben Sie uns jetzt.

Schildern Sie kurz System, PHP-Version und Beobachtung. Wir melden uns, klären die Lage und arbeiten remote, bis der Vorfall abgeschlossen und dokumentiert ist.

Sofort-Hilfe