Zum Inhalt springen
Planen wir gemeinsam die passende Software für Ihre Prozesse. Demo oder Angebot: +90 546 737 48 29

TR EN DE

Login- und Sitzungssicherheit

Die Anmeldeseite ist die Tür zu Ihrer Website; die Sitzung ist der Schlüssel, der die Identität der eingetretenen Person bei jeder weiteren Anfrage mitführt. Ist eines von beiden schwach, können Konten übernommen werden, ganz gleich, wie solide der übrige Code ist. Die verbreiteten Probleme sind bekannt: Passwörter, die mit einem schwachen Verfahren gespeichert sind, unbegrenzte Passwortversuche, eine Sitzungs-ID, die bei der Anmeldung nicht erneuert wird, ungeschützte Cookies und Berechtigungen, die nur im Menü ausgeblendet, auf dem Server aber nicht geprüft werden.

Diese Anleitung erklärt, wie die Anmelde- und Sitzungsschicht einer in PHP geschriebenen Website richtig aufgebaut wird. Zu den Maßnahmen auf Benutzerseite siehe die Anleitung Starke Passwörter und Passwortmanager.

Kurz gefasst

  • Passwörter werden mit password_hash gespeichert; MD5 und SHA-1 sind ungeeignet.
  • Anmeldeversuche werden pro Konto und pro IP-Adresse begrenzt; die Fehlermeldung bleibt allgemein.
  • Bei der Anmeldung wird die Sitzungs-ID erneuert; das Cookie erhält die Flags Secure, HttpOnly und SameSite.
  • Die Berechtigung wird bei jeder Anfrage auf dem Server geprüft; das Menü auszublenden genügt nicht.
Auf dieser Seite

Vier Grundlagen

  1. Speichern Sie nicht das Passwort, sondern seinen Hash

    Passwörter werden in der Datenbank niemals im Klartext oder in einer umkehrbaren Form gespeichert. Auch schnelle Hashfunktionen wie MD5, SHA-1 und SHA-256 ohne Salt sind für die Speicherung von Passwörtern ungeeignet; weil sie schnell sind, lassen sich die Passwörter aus einer abgeflossenen Datenbank in kurzer Zeit durchprobieren. Die PHP-Funktion password_hash verwendet einen langsamen, für Passwörter entwickelten Algorithmus und erzeugt den Salt von selbst.

    PHP
    <?php
    // Registrierung oder Passwortänderung
    $karma = password_hash($parola, PASSWORD_DEFAULT);
    // $karma wird in die Datenbank geschrieben (Spalte: VARCHAR(255))
    
    // Anmeldung
    if (password_verify($girilenParola, $kullanici['parola_karma'])) {
        // Hash aktualisieren, wenn sich Algorithmus oder Kostenfaktor geändert haben
        if (password_needs_rehash($kullanici['parola_karma'], PASSWORD_DEFAULT)) {
            $yeni = password_hash($girilenParola, PASSWORD_DEFAULT);
            $pdo->prepare('UPDATE kullanicilar SET parola_karma = ? WHERE id = ?')
                ->execute([$yeni, $kullanici['id']]);
        }
    }
    Vergleich: Passwörter im Klartext, mit MD5 oder SHA-1 zu speichern ist falsch; password_hash und password_verify sind richtig : Vergrößern
  2. Begrenzen Sie die Anzahl der Versuche

    Unbegrenzte Versuche machen das Erraten von Passwörtern möglich. Zählen Sie fehlgeschlagene Versuche sowohl pro Konto als auch pro IP-Adresse; verhängen Sie ab einer bestimmten Anzahl eine Wartezeit und verlängern Sie diese stufenweise. Die Fehlermeldung darf nicht erkennen lassen, ob der Benutzername oder das Passwort falsch war.

    Ablaufdiagramm: Anmeldeversuch, Prüfung der Versuchsanzahl, Passwortprüfung, Erneuerung der Sitzungs-ID, Berechtigung : Vergrößern
  3. Erneuern Sie die Sitzungs-ID bei der Anmeldung

    Session-Fixation entsteht, wenn die Sitzungs-ID, die der Benutzer vor der Anmeldung hatte, auch nach der Anmeldung gültig bleibt; wer die ID vorher kennt, kann dieselbe Sitzung nutzen, sobald sich der Benutzer angemeldet hat. Die Lösung ist einfach: Erneuern Sie die Sitzungs-ID, wenn die Anmeldung erfolgreich war und wenn sich die Berechtigungsstufe ändert.

    Schaubild: Flags des Sitzungs-Cookies; Secure, HttpOnly, SameSite und die bei der Anmeldung erneuerte Sitzungs-ID : Vergrößern
  4. Prüfen Sie die Berechtigung bei jeder Anfrage auf dem Server

    Eine Schaltfläche oder einen Menüpunkt auszublenden ist keine Berechtigungsprüfung; jeder, der die Adresse kennt, kann die Anfrage direkt senden. Jede Seite und jede Aktion muss auf dem Server zuerst prüfen, ob die Sitzung gültig ist, und dann, ob der Benutzer berechtigt ist, diese Aktion an genau diesem Datensatz auszuführen.

    Vergleich: Nur das Menü auszublenden ist falsch; bei jeder Anfrage auf dem Server Sitzung, Rolle und Eigentümerschaft des Datensatzes zu prüfen ist richtig : Vergrößern

Anzeichen: Woran erkennt man das Problem?

  • In der Datenbank: Sehen Sie in der Passwortspalte hexadezimale Werte mit 32 oder 40 Zeichen (MD5, SHA-1) oder lesbare Passwörter, ist das Speicherverfahren schwach. Die Ausgabe von password_hash beginnt mit $2y$ oder $argon2.
  • In den Protokollen: Zahlreiche POST-Anfragen an die Anmeldeadresse in kurzer Zeit; aufeinanderfolgende Versuche mit unterschiedlichen Benutzernamen; erfolgreiche Anmeldungen aus ungewöhnlichen Ländern oder zu ungewöhnlichen Uhrzeiten.
  • Im Browser: In den Entwicklertools fehlen dem Sitzungs-Cookie die Flags Secure, HttpOnly und SameSite; die Sitzungs-ID ist vor und nach der Anmeldung dieselbe.
  • In der Anwendung: Nach der Abmeldung wird der Inhalt angezeigt, wenn die Adresse einer Administrationsseite direkt eingegeben wird; ändert man die Datensatznummer in der Adresse, öffnet sich der Datensatz eines anderen Benutzers.

Sicherer Anmeldeablauf

Das folgende Beispiel zeigt die Begrenzung der Versuche, die Prüfung des Passworts, bei Bedarf die Aktualisierung des Hashes und die Erneuerung der Sitzungs-ID im Zusammenspiel.

PHP
<?php
$eposta = strtolower(trim((string) ($_POST['eposta'] ?? '')));
$parola = (string) ($_POST['parola'] ?? '');
$ip     = $_SERVER['REMOTE_ADDR'] ?? '';

if (cokFazlaDeneme($pdo, $eposta, $ip)) {
    http_response_code(429);
    echo 'Zu viele Versuche. Bitte versuchen Sie es in 15 Minuten erneut.';
    return;
}

$sorgu = $pdo->prepare('SELECT id, parola_karma, rol FROM kullanicilar WHERE eposta = ? AND aktif = 1');
$sorgu->execute([$eposta]);
$kullanici = $sorgu->fetch();

// Auch ohne Benutzer eine Prüfung durchführen: Die Antwortzeit soll nicht verraten, ob der Benutzer existiert.
// SAHTE_KARMA: Wert, der einmalig mit password_hash() erzeugt und als Konstante in der Konfiguration abgelegt wird
$karma = $kullanici ? $kullanici['parola_karma'] : SAHTE_KARMA;
$dogru = password_verify($parola, $karma) && $kullanici !== false;

if (!$dogru) {
    denemeKaydet($pdo, $eposta, $ip);
    echo 'E-Mail-Adresse oder Passwort ist falsch.';
    return;
}

session_regenerate_id(true);               // gegen Session-Fixation
$_SESSION['kullanici_id'] = (int) $kullanici['id'];
$_SESSION['rol']          = $kullanici['rol'];
$_SESSION['son_islem']    = time();
$_SESSION['csrf']         = bin2hex(random_bytes(32));
  • Auch wenn der Benutzer nicht gefunden wird, findet eine Hash-Prüfung statt; so lässt sich aus der Antwortzeit nicht ablesen, ob der Benutzer existiert.
  • Es gibt nur eine, allgemein gehaltene Fehlermeldung: „E-Mail-Adresse oder Passwort ist falsch.“
  • Ziehen Sie eine befristete Wartezeit einer dauerhaften Kontosperre vor; andernfalls kann jemand die Konten der Benutzer durch absichtlich falsche Versuche sperren.
  • Das Anmeldeformular muss ein CSRF-Token enthalten.

Für den Zähler der Versuche genügt eine einfache Tabelle:

PHP
<?php
// Tabelle: giris_denemeleri (eposta VARCHAR(190), ip VARCHAR(45), zaman DATETIME, INDEX(eposta), INDEX(ip))
function cokFazlaDeneme(PDO $pdo, string $eposta, string $ip): bool
{
    $sorgu = $pdo->prepare(
        'SELECT COUNT(*) FROM giris_denemeleri
         WHERE (eposta = ? OR ip = ?) AND zaman > (NOW() - INTERVAL 15 MINUTE)'
    );
    $sorgu->execute([$eposta, $ip]);
    return (int) $sorgu->fetchColumn() >= 5;
}

function denemeKaydet(PDO $pdo, string $eposta, string $ip): void
{
    $pdo->prepare('INSERT INTO giris_denemeleri (eposta, ip, zaman) VALUES (?, ?, NOW())')
        ->execute([$eposta, $ip]);
}

Umstieg von einem alten Hash-Verfahren

Werden die Passwörter auf einer bestehenden Website mit MD5 oder SHA-1 gespeichert, ist der Umstieg möglich, ohne dass die Benutzer ihr Passwort zurücksetzen müssen: Meldet sich ein Benutzer an, wird das Passwort mit dem alten Verfahren geprüft; ist es richtig, wird es im selben Moment mit password_hash neu gehasht und gespeichert. Da das Risiko fortbesteht, solange alte Hashes in der Datenbank liegen, sollte nach einer bestimmten Frist für Konten, die noch nicht umgestellt sind, das Zurücksetzen des Passworts verpflichtend sein.

Sitzungseinstellungen

Legen Sie die Flags des Sitzungs-Cookies und das Verhalten der Sitzung vor dem Aufruf von session_start fest.

PHP
<?php
ini_set('session.use_strict_mode', '1');   // IDs ablehnen, die nicht vom Server erzeugt wurden
ini_set('session.use_only_cookies', '1');  // die ID wird nicht in der Adresse übertragen

session_set_cookie_params([
    'lifetime' => 0,          // endet, wenn der Browser geschlossen wird
    'path'     => '/',
    'secure'   => true,
    'httponly' => true,
    'samesite' => 'Lax',
]);
session_start();

// Timeout bei Inaktivität: 30 Minuten
$sure = 30 * 60;
if (isset($_SESSION['son_islem']) && time() - $_SESSION['son_islem'] > $sure) {
    $_SESSION = [];
    session_destroy();
    header('Location: giris?oturum=doldu');
    return;
}
$_SESSION['son_islem'] = time();
  • Secure: Das Cookie wird nur über HTTPS gesendet.
  • HttpOnly: JavaScript kann das Cookie nicht auslesen; das erschwert bei XSS den Diebstahl des Cookies.
  • SameSite: Schränkt ein, dass das Cookie bei Anfragen mitgesendet wird, die von anderen Websites ausgehen.
  • Strikter Modus (strict mode): Lehnt Sitzungs-IDs ab, die nicht vom Server erzeugt wurden.
  • Timeout: Beenden Sie eine Sitzung, in der eine bestimmte Zeit lang nichts geschieht; halten Sie die Zeitspanne in Administrationsbereichen kurz.
  • Schreiben Sie die Sitzungs-ID niemals in die Adresse (URL).

Zerstören Sie die Sitzung bei der Abmeldung tatsächlich:

PHP
<?php
session_start();
$_SESSION = [];
if (ini_get('session.use_cookies')) {
    $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'] ?: 'Lax',
    ]);
}
session_destroy();
header('Location: giris');

Berechtigungsprüfung bei jeder Anfrage

Bündeln Sie die Berechtigungsprüfung in einer gemeinsamen Funktion, die am Anfang jeder Seite aufgerufen wird. Es gibt zwei getrennte Fragen: Darf der Benutzer diese Art von Aktion ausführen (Rolle), und gehört dieser Datensatz ihm (Eigentümerschaft)?

PHP
<?php
// Wird am Anfang jeder Administrationsseite aufgerufen
function yetkiIste(string $rol): void
{
    if (empty($_SESSION['kullanici_id'])) {
        header('Location: giris');
        exit;
    }
    if (($_SESSION['rol'] ?? '') !== $rol) {
        http_response_code(403);
        echo 'Sie sind nicht berechtigt, diese Seite anzuzeigen.';
        exit;
    }
}

// Eigentümerschaft des Datensatzes: Die Benutzer-ID aus der Sitzung wird in die Abfrage aufgenommen
$sorgu = $pdo->prepare('SELECT * FROM siparisler WHERE id = ? AND kullanici_id = ?');
$sorgu->execute([(int) ($_GET['id'] ?? 0), $_SESSION['kullanici_id']]);
$siparis = $sorgu->fetch();
if (!$siparis) {
    http_response_code(404);
    echo 'Die Bestellung wurde nicht gefunden.';
    return;
}

Vertrauen Sie nicht der Datensatznummer in der Adresse oder im Formular; nehmen Sie immer auch die Benutzer-ID aus der Sitzung in die Abfrage auf. Fehler in der Zugriffskontrolle gehören zu den Risiken ganz oben in den OWASP Top 10.

Schützen Sie den Administrationsbereich mit zusätzlichen Schichten

  • Zwei-Faktor-Authentifizierung: Machen Sie sie für Administratorkonten verpflichtend. Zur Benutzerseite siehe die Anleitung Zwei-Faktor-Authentifizierung.
  • IP-Beschränkung: Wird der Administrationsbereich nur von der festen IP-Adresse Ihres Büros oder über ein VPN aufgerufen, beschränken Sie das Verzeichnis auf diese IP-Adresse.
  • Zusätzliche Passwortschicht: Gibt es keine feste IP-Adresse, lässt sich das Verzeichnis auf Serverebene mit einem zweiten Passwort versehen (HTTP-Basisauthentifizierung).
  • Adresse des Administrationsbereichs: Eine nicht leicht zu erratende Adresse verringert das Grundrauschen automatisierter Scans, ist für sich allein aber kein Schutz.
  • Persönliche Konten: Verwenden Sie kein gemeinsames „admin“-Konto; jeder Administrator erhält ein eigenes Konto, und die ausgeführten Aktionen werden protokolliert.
.htaccess (Apache)
# admin/.htaccess : Zugriff auf den Administrationsbereich nur von bestimmten IP-Adressen
# (203.0.113.10 ist eine Beispieladresse; tragen Sie Ihre eigene feste IP-Adresse ein)
Require ip 203.0.113.10

# Ohne feste IP-Adresse: zweite Passwortschicht auf Serverebene
# AuthType Basic
# AuthName "Verwaltung"
# AuthUserFile /home/benutzer/.htpasswd
# Require valid-user

Passwort zurücksetzen

  • Das Token im Link zum Zurücksetzen muss zufällig (random_bytes), nur einmal verwendbar und kurzlebig sein; in der Datenbank wird sein Hash gespeichert.
  • Sagen Sie nicht „Diese E-Mail-Adresse ist nicht registriert“; zeigen Sie in jedem Fall dieselbe Meldung: „Falls die Adresse registriert ist, wurde ein Link zum Zurücksetzen gesendet.“
  • Beenden Sie nach einer Passwortänderung die übrigen offenen Sitzungen des Benutzers und senden Sie eine Benachrichtigung per E-Mail.
  • Versenden Sie das neue Passwort nicht per E-Mail; der Benutzer legt es selbst fest.
  • Stellen Sie in den Passwortregeln die Länge in den Vordergrund; verlangen Sie mindestens 12 Zeichen und lehnen Sie sehr verbreitete Passwörter ab.

Checkliste

  • Passwörter werden mit password_hash gespeichert; für alte Hashes gibt es einen Umstiegsplan.
  • Anmeldeversuche sind pro Konto und pro IP-Adresse begrenzt; die Fehlermeldung ist allgemein gehalten.
  • Nach erfolgreicher Anmeldung wird session_regenerate_id(true) aufgerufen.
  • Das Sitzungs-Cookie hat die Flags Secure, HttpOnly und SameSite; der strikte Modus ist aktiv.
  • Es gibt ein Timeout bei Inaktivität; die Abmeldung zerstört die Sitzung.
  • Jede Seite und jede Aktion prüft auf dem Server Sitzung, Rolle und Eigentümerschaft des Datensatzes.
  • Zwei-Faktor-Authentifizierung für Administratorkonten; IP-Beschränkung oder zusätzliche Passwortschicht für den Administrationsbereich.
  • Das Token zum Zurücksetzen des Passworts ist nur einmal verwendbar und befristet.
  • Erfolgreiche und fehlgeschlagene Anmeldungen werden protokolliert.

Häufige Fragen

Welchen Algorithmus verwendet PASSWORD_DEFAULT?

Die Konstante wählt den für die jeweilige PHP-Version empfohlenen Algorithmus (in aktuellen Versionen bcrypt). Ändert sich der Algorithmus später, wird der Hash mit password_needs_rehash bei der nächsten Anmeldung des Benutzers von selbst aktualisiert. Definieren Sie die Spalte deshalb als VARCHAR(255).

Soll ich dem Passwort einen eigenen Salt hinzufügen?

Nein. password_hash erzeugt für jedes Passwort selbst einen zufälligen Salt und speichert ihn im Hash. Ein manuell hinzugefügter Salt ist nicht nötig.

Ersetzt ein Captcha die Begrenzung der Versuche?

Für sich allein nicht. Ein Captcha erschwert automatisierte Versuche; die Anzahl der Versuche auf dem Server zu begrenzen, bleibt trotzdem notwendig. Beides lässt sich kombinieren.

Ist die Funktion „Angemeldet bleiben“ sicher?

Richtig umgesetzt, ja: Im langlebigen Cookie darf weder die Benutzer-ID noch das Passwort stehen, sondern ein zufälliges Token, dessen Hash in der Datenbank liegt; das Token muss bei jeder Verwendung erneuert und bei einer Passwortänderung widerrufen werden.

BYK Yazılım Support-Team
Diese Anleitung wird vom Support-Team von BYK Yazılım erstellt und regelmäßig überprüft. Letzte Aktualisierung: 04.10.2026.

Verwandte Anleitungen

Sehen wir uns Ihre Website gemeinsam an

BYK Yazılım entwickelt Unternehmenswebsites. Schreiben Sie uns, wenn Sie Fragen zu Ihrer Website haben.

Kontakt aufnehmen Unsere Leistung: Unternehmenswebsite