PHP Session Sicherheit: Session Fixation


Eine Session (Sitzung) bezeichnet eine stehende Verbindung eines Clients mit einem Server.

Bei zustandslosen Protokollen wie HTTP gibt es keine stehenden Verbindungen. Jede Kommunikation eines Clients (Browser) zu einem Webserver wird unabhängig voneinander betrachtet. Zudem können Benutzer nicht eindeutig identifiziert werden.

Für diesen Zweck werden Sessions verwendet, die den Zustand einer Webanwendung während einer Session (Sitzung) speichern können und somit eine zusammenhängende Kommunikation ermöglichen.

Sessions sind aber nicht nur nützlich, sondern auch ein beliebtes Ziel von Anfreifern. Es gibt verschiedene Angriffsmethoden. In diesem Artikel geht es um Session Fixation.

Sessions allgemein

Session Files

Durch eine Session können einem Benutzer Sitzungsdaten zugeordnet werden, die auf dem Server in sogenannte Session Files (temporär) gespeichert werden.

Solche Sitzungsdaten werden von einer Webanwendung erstellt. In PHP werden diese Daten im superglobalen Array $_SESSION übertragen.

Session-ID (SID)

Die Authentifizierung findet während einer Session über die Session-ID statt. Die Session-ID ist also wie ein Passwort, das für kurze Zeit gültig ist.

Kennt ein Angreifer die Session-ID, kann er die aktuelle Sitzung übernehmen, indem sein Client (Browser) die selbe Session-ID zum Kommunizieren mit dem Webserver verwendet.

PHP erzeugt eine Session-ID mit einer Länge von 32 Zeichen. Das Erraten ist also praktisch unmöglich und Brute-Force Attacken werden wahrscheinlich auch nicht gelingen.

Die Session-ID wird üblicherweise in Cookies oder in der URL per GET-Parameter übertragen. Die Übertragung per POST-Methode ist allerdings auch möglich.

Sessionverwaltung in PHP

Zur Initialisierung einer Session wird in PHP die Funktion session_start() verwendet.

<?php
  session_start();
  ...
?>

Wenn noch keine Session existiert, wird dem Client eine eindeutige Session-ID zugewiesen. Liefert der Client allerdings eine gültige Session-ID mit, wird die aktuelle Session wieder aufgenommen!

session_start() erzeugt eine Session oder nimmt die aktuelle wieder auf, die auf der Session-ID basiert, die mit einer Anfrage, z.B. durch GET, POST oder ein Cookie, übermittelt wurde. – Quelle: php.net

Die Session-ID kann mit der Funktion session_id() ausgelesen werden.

<?php
  ...
  echo session_id();
?>

Mit unset() können Session Variablen gelöscht werden.

<?php
  ...
  unset($_SESSION['foo']);
?>

Mit session_destroy() werden alle aktuellen Sitzungsdaten gelöscht.

<?php
  ...
  session_destroy();
?>

Bei php.net gibt es eine Liste mit weiteren Session Funktionen.

Session Fixation

Bei einer Session Fixation gibt ein Angreifer eine Session-ID vor und versucht diese fixierte Session-ID einem Benutzer unterzujubeln.

Der Vorteil von Angreifern ist, dass PHP jede beliebige übermittelte Session-ID akzeptiert.

In der folgenden Grafik wird veranschaulicht, wie die einfachste Form eines Session Fixation Angriffs aussieht. Hier wird die Session-ID in der URL übertragen.

Session Fixation Angriff

Schauen wir uns die einzelnen Schritte etwas genauer an.

1. Der Angreifer schickt eine präparierte URL in Form eines Links an sein Opfer, die zu einem Loginbereich einer Online Banking Website führt, wo das Opfer registriert ist. In der URL befindet sich die fixierte Session-ID (1234).

2. Das Opfer ruft den präparierten Link auf. An dieser Stelle erkennt der Webserver bereits die Session-ID und weist sie einer Session zu.

3. Das Opfer loggt sich mit seinen Logindaten ein.

4. Der Angreifer ruft nun mit der selben Session-ID eine Seite des Loginbereichs auf. Der Webserver erkennt die gültige Session-ID, die bereits einer Session zugewiesen ist. Die Webanwendung stellt fest, dass gültige Sitzungsdaten existieren und es sich um den eingeloggten Benutzer “Max Mustermann” handelt. Der Angreifer ist nun also mit den Daten seines Opfers eingeloggt und kann mit dem Account anstellen, was er will.

Proof-Of-Concept

Folgendes Code-Beispiel soll das ganze praktisch demonstrieren. Hierzu sind folgende Einstellungen in der php.ini notwendig, damit die Session-ID in der URL übertragen wird.

session.use_cookies = 0
session.use_only_cookies = 0
session.name = PHPSESSID
<?php
  session_start();
 
  if(!empty($_SESSION['userid']))
    echo 'Willkommen zurueck! ID: ' . $_SESSION['userid'];
  else
  {
    $_SESSION['userid'] = md5(uniqid(mt_rand(), true));
    echo '<p>Du bist neu hier. Deine neue ID: ' . $_SESSION['userid'] . '</p>';
    echo '<p>Session-ID: ' . session_id() . '</p>';
  }
?>

Das ganze können wir jetzt einfach mit zwei verschiedenen Browsern testen.

Browser1 = Opfer
Browser2 = Angreifer

Zuerst wird die Session-ID vom Angreifer bestimmt.

login.php?PHPSESSID=5678

Nun rufen wir die URL in Browser1 auf.

Du bist neu hier. Deine neue ID: 9eb47aed86421db352d3e2c7753c121b

Session-ID: 5678

Rufen wir die URL erneut auf, werden wir identifiziert.

Willkommen zurueck! ID: 9eb47aed86421db352d3e2c7753c121b

Wenn wir die URL nun mit der selben Session-ID (5678) in Browser2 aufrufen, werden wir ebenfalls identifiziert und erhalten die gleiche Ausgabe.

Willkommen zurueck! ID: 9eb47aed86421db352d3e2c7753c121b

Bei einem Loginscript wären wir jetzt mit Browser2 eingeloggt.

Session-ID in der URL

Werden Session-IDs in der URL übertragen, gibt es noch weitere Gefahren. Zum Beispiel die sogenannte Self Exploitation. Interessante Links werden gerne mal in Foren, Blogs, Twitter, etc. weitergegeben. Und was ist der einfachste Weg? Genau, die URL aus der Adresszeile kopieren. Jeder, der den Link aufruft, übernimmt die Session.

Sehr kritisch wird es, wenn andere Methoden (aus)genutzt werden. Zum Beispiel könnte ein Angreifer HTTP Weiterleitungen in PHP Anwendungen (z.B. Foren) ausnutzen. Dann müsste er noch nicht einmal jemanden dazu bringen, die präparierte URL aufzurufen.

Session-ID in Cookies

Wenn die Session-ID in einem Cookie übertragen wird, ist Session Fixation ohne weiteres nicht möglich. Die Betonung liegt hier auf “ohne weiteres”, denn mit einer einfachen XSS Sicherheitslücke auf der Zielseite, sieht die Sache schon wieder anders aus.

Ein Angreifer könnte durch injizierten JavaScript Code sein Opfer dazu bringen das entsprechende Cookie zu setzen.

<script>document.cookie='PHPSESSID=1234';</script>

Das Kombinieren von Sicherheitslücken kann sehr effektiv sein kann.

Gegenmaßnahmen

Session Fixation ist im Gegensatz zu anderen Angriffsmethoden auf Sessions wie z.B. Session Hijacking ziemlich leicht zu verhindern.

Man könnte das Übermitteln der Session-ID per URL deaktivieren. Das geht ganz einfach, indem man den Wert von session.use_only_cookies in der php.ini auf 1 setzt.

Hat man kein Zugriff auf die php.ini, kann man das ganze auch im Script mit ini_set() regeln. Wichtig ist, dass das ganze vor dem Aufruf von session_start() geschieht.

<?php
  ini_set('session.use_only_cookies', 1);
  session_start();
  ...
?>

Das verhindert aber immer noch nicht Session Fixation, da (wie bereits erwähnt) mithilfe von anderen Sicherheitslücken die Session-ID in einem Cookie injiziert werden kann.

Session-ID Regenerierung

Ein sicherer und effizienter Schutz gegen Session Fixation ist die Session-ID Regenerierung. Bei jeder Authentifizierung ersetzen wir die Session-ID durch eine neue.

Dazu wird die Funktion session_regenerate_id() verwendet.

Die Funktion session_regenerate_id() ersetzt die aktuelle Session-ID durch eine neue und übernimmt die aktuellen Session-Informationen. – Quelle: php.net

Der Angreifer schickt eine präparierte URL mit einer fixierten Session-ID an sein “Opfer”. Das “Opfer” loggt sich ein und erhält eine neue Session-ID. Die alte, fixierte Session-ID ist für den Angreifer somit nutzlos.

Bei erfolgreichem Login ersetzen wir also die Session-ID. Zudem sollten bei einem Logout immer alle Sitzungsdaten gelöscht werden.

<?php
  session_start();
 
  // fixierte Session-ID vom Angreifer:
 
  echo '<p>Alte Session-ID: ' . session_id() . '</p>';
 
  // Login
 
  ...
 
  // wenn Login erfolgreich, neue Session-ID und Session Variable:
 
  if(empty($_SESSION['user']))
  {
    session_regenerate_id();
    echo '<p>Neue Session-ID: ' . session_id() . '</p>';
 
    // Session Variable zur Identifikation eines eingeloggten Benutzers definieren:
    $_SESSION['user'] = true;
  }
 
  // bei jedem Request überprüfen, ob es sich um einen eingeloggten Benutzer handelt:
  if(!empty($_SESSION['user']))
  {
    // Benutzer ist eingeloggt, da die Session Variable existiert!
    ...
  }
 
  // Logout:
  session_destroy();
?>

Das war’s soweit für den ersten Teil zum Thema Session Sicherheit in PHP.