PHP-Einfach.de
  • PHP Tutorial
  • MySQL Tutorial
  • Für Fortgeschrittene
  • Webhosting
  • Forum

Angemeldet bleiben

12. Mai 2018
  1. Home
  2. »
  3. Für Fortgeschrittene
  4. »
  5. Script-Beispiele
  6. »
  7. Loginscript
  8. »
  9. Angemeldet bleiben

Benutzern die Möglichkeit zu geben angemeldet zu bleiben steigert deutlich die Benutzerfreundlichkeit eurer Anwendung. So ist beispielsweise kein ständiger neuer Login zu Facebook o.ä. nötig, selbst wenn der Browser mal geschlossen wird.

So eine Funktion birgt aber auch ein entsprechendes Sicherheitsrisiko, da auf dem Rechner des Benutzers gewisse Authentifizierungsinformationen abgespeichert werden müssen. Diese könnten von einem Angreifer kopiert werden und ausgenutzt werden. Für sehr kritische Anwendungen sollte man auf diese Funktion lieber verzichten.

Dieser Artikel erklärt euch, wie ihr diese Funktion mit maximaler Sicherheit implementiert. Weitere Sicherheitstipps für den internen Bereich findet ihr im Artikel Authentifizierung in PHP.

Das komplette Login-Script könnt ihr hier herunterladen.

Inhaltsverzeichnis

  • 1 Genereller Ablauf
  • 2 Datenbanktabelle
  • 3 Erweiterung des Login-Formulars
  • 4 Erweiterung des Login-Prozess
  • 5 Interner Bereich
  • 6 Erweiterung des Logout-Prozess
  • 7 Hintergrundwissen zum Sicherheitskonzept

Genereller Ablauf

Der Ablauf unseres Scripts sieht wie folgt aus:

  1. Bietet beim Login die Checkbox 'Angemeldet bleiben' an. Hat der Benutzer diese ausgewählt wird ein zusätzlicher Login-Cookie bei ihm abgespeichert.
  2. Erzeugt zwei sichere Codes. Der erste Code ist der Identifier, der zweite der Securitytoken.
  3. Speichert in einer Tabelle die User-ID, den Identifier sowie den SHA-1 Wert des Securitytokens.
  4. Beim Benutzer selbst wird der Identifier und der Scuritytoken (als Klartext) als Cookie hinterlegt.
  5. Besucht der Nutzer eure Website erneut und ist noch nicht eingeloggt, so lest ihr aus dem Cookie den Identifier und den Securitytoken aus.
    1. Sollte der Identifier und Securitytoken zu den Daten in der Tabelle stimmen, so loggt ihr dem Benutzer ein und erzeugt einen erneuten Securitytoken (den Identifier könnt ihr wiederverwenden). Der neue Securitytoken wird im Cookie und in der Datenbank aktualisiert.
    2. Passt der Securitytoken nicht zu dem Token in der Datenbank, so hat jemand höchst wahrscheinlich die Cookies beim Benutzer gestohlen.
    3. Ist kein Identifier in den Cookies vorhanden oder passt ist keiner in der Datenbank vorhanden, dann könnt ihr den Wert ignorieren.

 

Datenbanktabelle

Zum Abspeichern des Identifiers und des Securitytokens erzeugt eine neue Tabelle securitytokens:

Der SQL-Code für diese Tabelle ist:

1
2
3
4
5
6
7
CREATE TABLE `securitytokens` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `user_id` int(10) NOT NULL,
  `identifier` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `securitytoken` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Erweiterung des Login-Formulars

Das Login-Formular solltet ihr um eine Checkbox erweitern für die Möglichkeit angemeldet zu bleiben. Für das Login-Formular aus unserem Loginscript-Beispiel sähe dies wie folgt aus:

1
2
3
4
5
6
7
8
9
10
11
<form action="?login=1" method="post">
E-Mail:<br>
<input type="email" size="40" maxlength="250" name="email"><br><br>
 
Dein Passwort:<br>
<input type="password" size="40"  maxlength="250" name="passwort"><br>
 
<label><input type="checkbox" name="angemeldet_bleiben" value="1"> Angemeldet bleiben</label><br>
 
<input type="submit" value="Abschicken">
</form>

Erweiterung des Login-Prozess

Den Login-Bereich müsst ihr wie folgt modifizieren:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php
session_start();
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
 
function random_string() {
   if(function_exists('random_bytes')) {
      $bytes = random_bytes(16);
      $str = bin2hex($bytes);
   } else if(function_exists('openssl_random_pseudo_bytes')) {
      $bytes = openssl_random_pseudo_bytes(16);
      $str = bin2hex($bytes);
   } else if(function_exists('mcrypt_create_iv')) {
      $bytes = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
      $str = bin2hex($bytes);
   } else {
      //Bitte euer_geheim_string durch einen zufälligen String mit >12 Zeichen austauschen
      $str = md5(uniqid('euer_geheimer_string', true));
   }  
   return $str;
}
 
if(isset($_GET['login'])) {
   $email = $_POST['email'];
   $passwort = $_POST['passwort'];
  
   $statement = $pdo->prepare("SELECT * FROM users WHERE email = :email");
   $result = $statement->execute(array('email' => $email));
   $user = $statement->fetch();
      
   //Überprüfung des Passworts
   if ($user !== false && password_verify($passwort, $user['passwort'])) {
      $_SESSION['userid'] = $user['id'];
      
      //Möchte der Nutzer angemeldet beleiben?
      if(isset($_POST['angemeldet_bleiben'])) {
         $identifier = random_string();
         $securitytoken = random_string();
        
         $insert = $pdo->prepare("INSERT INTO securitytokens (user_id, identifier, securitytoken) VALUES (:user_id, :identifier, :securitytoken)");
         $insert->execute(array('user_id' => $user['id'], 'identifier' => $identifier, 'securitytoken' => sha1($securitytoken)));
         setcookie("identifier",$identifier,time()+(3600*24*365)); //1 Jahr Gültigkeit
         setcookie("securitytoken",$securitytoken,time()+(3600*24*365)); //1 Jahr Gültigkeit
      }
      
      die('Login erfolgreich. Weiter zu <a href="geheim.php">internen Bereich</a>');
   } else {
      $errorMessage = "E-Mail oder Passwort war ungültig<br>";
   }
  
}
?>

Interner Bereich

Auf euren internen Seiten, die erst nach der Authentifizierung zugänglich sein sollen, baut ihr folgenden Script ein.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?php
session_start();
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
 
 
function random_string() {
   if(function_exists('random_bytes')) {
      $bytes = random_bytes(16);
      $str = bin2hex($bytes);
   } else if(function_exists('openssl_random_pseudo_bytes')) {
      $bytes = openssl_random_pseudo_bytes(16);
      $str = bin2hex($bytes);
   } else if(function_exists('mcrypt_create_iv')) {
      $bytes = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
      $str = bin2hex($bytes);
   } else {
      //Bitte euer_geheim_string durch einen zufälligen String mit >12 Zeichen austauschen
      $str = md5(uniqid('euer_geheimer_string', true));
   }  
   return $str;
}
 
//Überprüfe auf den 'Angemeldet bleiben'-Cookie
if(!isset($_SESSION['userid']) && isset($_COOKIE['identifier']) && isset($_COOKIE['securitytoken'])) {
   $identifier = $_COOKIE['identifier'];
   $securitytoken = $_COOKIE['securitytoken'];
  
   $statement = $pdo->prepare("SELECT * FROM securitytokens WHERE identifier = ?");
   $result = $statement->execute(array($identifier));
   $securitytoken_row = $statement->fetch();
  
   if(sha1($securitytoken) !== $securitytoken_row['securitytoken']) {
      die('Ein vermutlich gestohlener Security Token wurde identifiziert');
   } else { //Token war korrekt        
      //Setze neuen Token
      $neuer_securitytoken = random_string();            
      $insert = $pdo->prepare("UPDATE securitytokens SET securitytoken = :securitytoken WHERE identifier = :identifier");
      $insert->execute(array('securitytoken' => sha1($neuer_securitytoken), 'identifier' => $identifier));
      setcookie("identifier",$identifier,time()+(3600*24*365)); //1 Jahr Gültigkeit
      setcookie("securitytoken",$neuer_securitytoken,time()+(3600*24*365)); //1 Jahr Gültigkeit
      
      //Logge den Benutzer ein
      $_SESSION['userid'] = $securitytoken_row['user_id'];
   }
}
 
if(!isset($_SESSION['userid'])) {
   die('Bitte zuerst <a href="login.php">Einloggen</a>');
}
 
$userid = $_SESSION['userid'];
 
echo "Hallo User: ".$userid;
?>

Dieser Script überprüft, ob der User nicht eingeloggt ist und ob die notwendigen Cookies gesetzt wurden. Danach wird der Identifier und der Securitytoken aus den Cookies ausgelesen und mit der Datenbank verglichen. Stimmen alle Eingaben überein, so wird ein neuer Securitytoken geniert, dieser in der Datenbank als auch im Cookie abgespeichert und die notwendige Session-Variable $_SESSION['userid'] wird registriert.

Erweiterung des Logout-Prozess

Bei dem Logout müsst ihr sicherstellen, dass die Cookies entsprechend gelöscht werden. Dazu müsst ihr die logout.php Datei wie folgt verändern:

1
2
3
4
5
6
7
8
9
10
<?php
session_start();
session_destroy();
 
//Cookies entfernen
setcookie("identifier","",time()-(3600*24*365));
setcookie("securitytoken","",time()-(3600*24*365));
echo "Logout erfolgreich";
?>

Hintergrundwissen zum Sicherheitskonzept

Leider findet immer noch viele Websites, die statt dieser Variante direkt das Passwort und den Benutzernamen in dem Cookie abspeichern. Dies führt gleich zu mehreren Sicherheitsrisiken. Oftmals sind Cookies im Browser nicht sonderlich gut geschützt und jeder mit Zugriff auf den Computer kann diese kinderleicht auslesen. Somit hat der Angreifer auch direkt die notwendigen Logindaten.

Das Abspeichern des Passworts als Hash, den ihr in der User-Tabelle gespeichert habt, erhöht die Sicherheit in diesem Aspekt etwas, öffnet aber ein weiteres Sicherheitsproblem: Falls jemand an den Passworthash kommt, beispielsweise mittels SQL-Injection, so kann er sich selbst diese Cookies setzen und sich damit als beliebiger Nutzer einloggen.

Diese Variante bietet Schutz gegen mehrere Angriffsszenarien:

  • Auf dem Rechner werden keine sensiblen Daten gespeichert wie beispielsweise das Passwort in Klartext.
  • Gestohlene Daten aus der Datenbank, z.B. per SQL-Injection, erlauben es dem Angreifer nicht sich per Angemeldet bleiben-Cookie sich zu authentifizieren. Nach dem Diebstahl hat er nur den SHA-1 Hash, er benötigt aber den zufälligen Wert der diesen SHA-1-Hash erzeugt, und sofern euer Zufallsgenerator sicher funktioniert hat der Angreifer keine Chance diesen Wert zu berechnen.
  • Stiehlt ein Angreifer den Cookie von der Festplatte des Benutzers, dann kann er sich damit einloggen. Allerdings nur sofern der Securitytoken noch aktuell ist. Hat sich der Benutzer in der Zwischenzeit erneut eingeloggt, ist der gestohlene Securitytoken wertlos.
Autor: Nils Reimers
Zurück: Passwort vergessen
Weiter: E-Mails mit Anhang versenden

Für Fortgeschrittene

  • Objektorientierte Programmierung
  • PHP Sicherheit
  • Script-Beispiele
    • Loginscript
      • Passwort vergessen
      • Angemeldet bleiben
    • E-Mails mit Anhang versenden
    • Gästebuch
    • PDFs erstellen
    • Besucherzähler mit Reload-Sperre
    • Kontaktformular
    • Logfiles mit PHP generieren
    • URLs im Text identifizieren
  • Codeschnipsel
  • Stellenmarkt
Mit freundlicher Unterstützung von:
  • Punkt191 Werbeagentur

Hoster – Geringste Ausfallzeit

  1. netcup Ø 0 Min.
  2. webgo Ø 0 Min.
  3. Linevast Ø 3 Min.
  4. All-Inkl.com Ø 3 Min.
  5. checkdomain Ø 4 Min.
  6. dogado Ø 6 Min.
  7. Strato Ø 8 Min.
  8. manitu Ø 10 Min.
  9. 1&1 Ø 10 Min.
  10. DomainFactory Ø 14 Min.
» Mehr erfahren

Impressum | Datenschutz | Auf PHP-Einfach.de werben

© PHP-Einfach.de 2003 - 2025

Cookie-Zustimmung verwalten
Um dir ein optimales Erlebnis zu bieten, verwenden wir Technologien wie Cookies, um Geräteinformationen zu speichern und/oder darauf zuzugreifen. Wenn du diesen Technologien zustimmst, können wir Daten wie das Surfverhalten oder eindeutige IDs auf dieser Website verarbeiten. Wenn du deine Zustimmung nicht erteilst oder zurückziehst, können bestimmte Merkmale und Funktionen beeinträchtigt werden.
Funktional Immer aktiv
Die technische Speicherung oder der Zugang ist unbedingt erforderlich für den rechtmäßigen Zweck, die Nutzung eines bestimmten Dienstes zu ermöglichen, der vom Teilnehmer oder Nutzer ausdrücklich gewünscht wird, oder für den alleinigen Zweck, die Übertragung einer Nachricht über ein elektronisches Kommunikationsnetz durchzuführen.
Vorlieben
Die technische Speicherung oder der Zugriff ist für den rechtmäßigen Zweck der Speicherung von Präferenzen erforderlich, die nicht vom Abonnenten oder Benutzer angefordert wurden.
Statistiken
Die technische Speicherung oder der Zugriff, der ausschließlich zu statistischen Zwecken erfolgt. Die technische Speicherung oder der Zugriff, der ausschließlich zu anonymen statistischen Zwecken verwendet wird. Ohne eine Vorladung, die freiwillige Zustimmung deines Internetdienstanbieters oder zusätzliche Aufzeichnungen von Dritten können die zu diesem Zweck gespeicherten oder abgerufenen Informationen allein in der Regel nicht dazu verwendet werden, dich zu identifizieren.
Marketing
Die technische Speicherung oder der Zugriff ist erforderlich, um Nutzerprofile zu erstellen, um Werbung zu versenden oder um den Nutzer auf einer Website oder über mehrere Websites hinweg zu ähnlichen Marketingzwecken zu verfolgen.
Optionen verwalten Dienste verwalten Anbieter verwalten Lese mehr über diese Zwecke
Einstellungen ansehen
{title} {title} {title}