“Keep Me Logged In” – der beste Ansatz

Meine Webanwendung verwendet Sitzungen, um Informationen über den Benutzer nach der Anmeldung zu speichern und diese Informationen beizubehalten, wenn sie innerhalb der App von Seite zu Seite wechseln. In dieser speziellen Anwendung speichere ich die user_id , first_name und last_name der Person.

Ich möchte beim Einloggen eine “Keep Me Logged In” -Option anbieten, die zwei Wochen lang einen Cookie auf dem Computer des Benutzers speichert, der seine Sitzung mit den gleichen Details neu startet, wenn sie zur App zurückkehren.

Was ist der beste Ansatz dafür? Ich möchte ihre user_id im Cookie speichern, da es so aussieht, als würde es einem Benutzer leicht fallen, die Identität eines anderen Benutzers zu fälschen.

    OK, lassen Sie mich das offen sagen: Wenn Sie Benutzerdaten oder irgendetwas, das aus Benutzerdaten stammt, zu diesem Zweck in einen Cookie schreiben, tun Sie etwas falsch.

    Dort. Ich sagte es. Jetzt können wir zur eigentlichen Antwort übergehen.

    Was ist los mit Hashing Benutzerdaten, fragen Sie? Nun, es kommt auf Belichtungsfläche und Sicherheit durch Dunkelheit an.

    Stellen Sie sich für eine Sekunde vor, dass Sie ein Angreifer sind. Auf Ihrer Sitzung wird ein kryptografisches Cookie für das Erinnerungs-Passwort angezeigt. Es ist 32 Zeichen breit. Meine Güte. Das kann ein MD5 sein …

    Stellen wir uns auch für eine Sekunde vor, dass sie den Algorithmus kennen, den Sie verwendet haben. Beispielsweise:

     md5(salt+username+ip+salt) 

    Alles was ein Angreifer jetzt tun muss, ist Brute Force “Salz” (was nicht wirklich ein Salz ist, aber dazu später mehr), und er kann nun alle gefälschten Tokens, die er will, mit einem beliebigen Benutzernamen für seine IP-Adresse erzeugen! Aber Brute-Forcing ein Salz ist hart, oder? Absolut. Aber moderne GPUs sind außerordentlich gut darin. Und wenn du nicht genügend Zufälligkeit darin verwendest (mach es groß genug), wird es schnell fallen und damit die Schlüssel zu deinem Schloss.

    Kurz gesagt, das Einzige, was Sie schützt, ist das Salz, das Sie nicht wirklich schützt, wie Sie denken.

    Aber warte!

    All dies wurde vorausgesetzt, dass der Angreifer den Algorithmus kennt! Wenn es geheim und verwirrend ist, dann bist du in Sicherheit, oder? Falsch . Diese Denkrichtung hat einen Namen: Sicherheit durch Obskur , auf die man sich NIE verlassen sollte.

    Der bessere Weg

    Der bessere Weg ist, niemals die Informationen eines Benutzers den Server verlassen zu lassen, mit Ausnahme der ID.

    Wenn sich der Benutzer anmeldet, generieren Sie ein großes (128 bis 256 Bit) zufälliges Token. Fügen Sie das zu einer databasetabelle hinzu, die das Token der Benutzer-ID zuordnet, und senden Sie es dann an den Client im Cookie.

    Was ist, wenn der Angreifer das zufällige Token eines anderen Benutzers erraten hat?

    Nun, lass uns hier etwas Mathe machen. Wir erzeugen ein zufälliges 128-Bit-Token. Das bedeutet, dass es

     possibilities = 2^128 possibilities = 3.4 * 10^38 

    Nun, um zu zeigen, wie absurd groß diese Zahl ist, stellen wir uns vor, jeder Server im Internet (sagen wir 50.000.000 heute) versucht, diese Zahl mit einer Rate von 1.000.000.000 pro Sekunde brute-force. In Wirklichkeit würden Ihre Server unter einer solchen Last schmelzen, aber lassen Sie uns das ausprobieren.

     guesses_per_second = servers * guesses guesses_per_second = 50,000,000 * 1,000,000,000 guesses_per_second = 50,000,000,000,000,000 

    Also 50 Billiarden Raten pro Sekunde. Das ist schnell! Recht?

     time_to_guess = possibilities / guesses_per_second time_to_guess = 3.4e38 / 50,000,000,000,000,000 time_to_guess = 6,800,000,000,000,000,000,000 

    Also 6,8 Sextillionsekunden …

    Versuchen wir, das auf freundlichere Zahlen zu reduzieren.

     215,626,585,489,599 years 

    Oder noch besser:

     47917 times the age of the universe 

    Ja, das ist 47917 mal das Alter des Universums …

    Grundsätzlich wird es nicht geknackt werden.

    Also zusammenfassend:

    Der bessere Ansatz, den ich empfehle, ist, den Keks mit drei Teilen zu lagern.

     function onLogin($user) { $token = GenerateRandomToken(); // generate a token, should be 128 - 256 bit storeTokenForUser($user, $token); $cookie = $user . ':' . $token; $mac = hash_hmac('sha256', $cookie, SECRET_KEY); $cookie .= ':' . $mac; setcookie('rememberme', $cookie); } 

    Dann, um zu bestätigen:

     function rememberMe() { $cookie = isset($_COOKIE['rememberme']) ? $_COOKIE['rememberme'] : ''; if ($cookie) { list ($user, $token, $mac) = explode(':', $cookie); if (!hash_equals(hash_hmac('sha256', $user . ':' . $token, SECRET_KEY), $mac)) { return false; } $usertoken = fetchTokenByUserName($user); if (hash_equals($usertoken, $token)) { logUserIn($user); } } } 

    Hinweis: Verwenden Sie das Token oder die Kombination aus Benutzer und Token nicht zum Suchen eines Datensatzes in Ihrer database. Achten Sie immer darauf, einen Datensatz basierend auf dem Benutzer abzurufen, und verwenden Sie eine zeitsparende Vergleichsfunktion, um das abgerufene Token anschließend zu vergleichen. Mehr über Timing-Attacken .

    Nun ist es sehr wichtig, dass das SECRET_KEY ein kryptografisches Geheimnis ist (erzeugt durch etwas wie /dev/urandom und / oder abgeleitet von einer Eingabe mit hoher Entropie). Außerdem muss GenerateRandomToken() eine starke mt_rand() ( mt_rand() ist nicht annähernd stark genug. Verwenden Sie eine Bibliothek wie RandomLib oder random_compat oder mcrypt_create_iv() mit DEV_URANDOM ) …

    Das hash_equals() soll Timing-Attacken verhindern. Wenn Sie eine PHP-Version unter PHP 5.6 verwenden, wird die function hash_equals() nicht unterstützt. In diesem Fall können Sie hash_equals() durch die function timingSafeCompare ersetzen:

     /** * A timing safe equals comparison * * To prevent leaking length information, it is important * that user input is always used as the second parameter. * * @param string $safe The internal (safe) value to be checked * @param string $user The user submitted (unsafe) value * * @return boolean True if the two strings are identical. */ function timingSafeCompare($safe, $user) { if (function_exists('hash_equals')) { return hash_equals($safe, $user); // PHP 5.6 } // Prevent issues if string length is 0 $safe .= chr(0); $user .= chr(0); // mbstring.func_overload can make strlen() return invalid numbers // when operating on raw binary strings; force an 8bit charset here: if (function_exists('mb_strlen')) { $safeLen = mb_strlen($safe, '8bit'); $userLen = mb_strlen($user, '8bit'); } else { $safeLen = strlen($safe); $userLen = strlen($user); } // Set the result to the difference between the lengths $result = $safeLen - $userLen; // Note that we ALWAYS iterate over the user-supplied length // This is to prevent leaking length information for ($i = 0; $i < $userLen; $i++) { // Using % here is a trick to prevent notices // It's safe, since if the lengths are different // $result is already non-0 $result |= (ord($safe[$i % $safeLen]) ^ ord($user[$i])); } // They are only identical strings if $result is exactly 0... return $result === 0; } 

    Sicherheitshinweis : Es ist keine gute Idee, den Cookie von einem MD5-Hash deterministischer Daten zu entfernen. Es ist besser, ein zufälliges Token zu verwenden, das von einem CSPRNG abgeleitet wurde. Siehe ircmaxells Antwort auf diese Frage für einen sichereren Ansatz.

    Normalerweise mache ich so etwas:

    1. Der Benutzer meldet sich mit ‘Ich möchte angemeldet sein’ an
    2. Erstellen Sie eine Sitzung
    3. Erstellen Sie einen Cookie namens SOMETHING mit: md5 (salt + username + ip + salt) und einem Cookie namens somethingElse mit id
    4. Speichern Sie das Cookie in der database
    5. Benutzer macht Sachen und verlässt —-
    6. Der Benutzer gibt zurück, sucht nach etwasEllen Cookie, wenn es existiert, holen Sie sich den alten Hash aus der database für diesen Benutzer, überprüfen Sie den Inhalt von Cookie SOMETHING mit dem Hash aus der database, der auch mit einem neu berechneten Hash (für die ip) also: cookieHash == databaseHash == md5 (salt + benutzername + ip + salt), wenn sie es tun, gehe zu 2, wenn sie nicht zu 1 kommen

    Natürlich können Sie auch verschiedene Cookie-Namen verwenden. Sie können auch den Inhalt des Cookies ein wenig ändern, aber stellen Sie sicher, dass er nicht einfach erstellt werden kann. Sie können zum Beispiel auch eine user_salt erstellen, wenn der Benutzer erstellt wird, und diese auch in den Cookie einfügen.

    Sie könnten auch sha1 anstelle von md5 (oder so ziemlich jeden Algorithmus) verwenden

    Einführung

    Ihr Titel “Keep Me Logged In” – der beste Ansatz macht es mir schwer zu wissen, wo ich anfangen soll, denn wenn Sie den besten Ansatz suchen, müssten Sie Folgendes beachten:

    • Identifizierung
    • Sicherheit

    cookies

    Cookies sind anfällig. Zwischen den üblichen Browser-Sicherheitslücken und Cross-Site-Scripting-Angriffen müssen wir akzeptieren, dass Cookies nicht sicher sind. Um die Sicherheit zu verbessern, müssen Sie beachten, dass php setcookies zusätzliche functionen wie

    bool setcookie (string $ name [, string $ value [, int $ expire = 0 [, string $ pfad [, string $ domain [, bool $ secure = false [, bool $ htponly = falsch]]]]]])

    • Sicher (Verwendung der HTTPS-Verbindung)
    • httponly (Reduziere Identitätsdiebstahl durch XSS-Angriff)

    Definitionen

    • Token (Unvorhersagbare Zufallsfolge mit n Länge zB / dev / urandom)
    • Referenz (Unvorhersagbare Zufallsfolge mit n Länge zB / dev / urandom)
    • Signatur (generiert einen verschlüsselten Hash-Wert mit der HMAC-Methode)

    Einfacher Ansatz

    Eine einfache Lösung wäre:

    • Benutzer ist mit Remember Me angemeldet
    • Login Cookie mit Token und Signatur
    • Bei der Rückgabe wird die Signatur überprüft
    • Wenn die Signatur in Ordnung ist, wird der Benutzername und das Token in der database nachgeschlagen
    • wenn nicht gültig .. zurück zur Anmeldeseite
    • Wenn gültig, loggen Sie sich automatisch ein

    Die obige Fallstudie fasst alle auf dieser Seite gegebenen Beispiele zusammen, aber sie haben Nachteile

    • Es gibt keine Möglichkeit zu wissen, ob die Cookies gestohlen wurden
    • Angreifer können zugriffsempfindliche Vorgänge wie die Änderung des Passworts oder Daten wie persönliche Informationen und Backinformationen usw. sein.
    • Der kompromittierte Cookie wäre weiterhin für die Cookie-Lebensdauer gültig

    Bessere Lösung

    Eine bessere Lösung wäre

    • Benutzer ist eingeloggt und merke, dass ich ausgewählt bin
    • Generiere Token & Signatur und speichere sie in einem Cookie
    • Die Token sind zufällig und nur für die Authentifizierung gültig
    • Die Token werden bei jedem Besuch der Site ersetzt
    • Wenn ein nicht angemeldeter Benutzer die Site besucht, werden die Signatur, das Token und der Benutzername verifiziert
    • Erinnere mich daran, dass mein Login eingeschränkt zugänglich sein sollte und keine Änderung des Passworts, der persönlichen Daten usw. erlauben darf.

    Beispielcode

     // Set privateKey // This should be saved securely $key = 'fc4d57ed55a78de1a7b31e711866ef5a2848442349f52cd470008f6d30d47282'; $key = pack("H*", $key); // They key is used in binary form // Am Using Memecahe as Sample Database $db = new Memcache(); $db->addserver("127.0.0.1"); try { // Start Remember Me $rememberMe = new RememberMe($key); $rememberMe->setDB($db); // set example database // Check if remember me is present if ($data = $rememberMe->auth()) { printf("Returning User %s\n", $data['user']); // Limit Acces Level // Disable Change of password and private information etc } else { // Sample user $user = "baba"; // Do normal login $rememberMe->remember($user); printf("New Account %s\n", $user); } } catch (Exception $e) { printf("#Error %s\n", $e->getMessage()); } 

    class verwendet

     class RememberMe { private $key = null; private $db; function __construct($privatekey) { $this->key = $privatekey; } public function setDB($db) { $this->db = $db; } public function auth() { // Check if remeber me cookie is present if (! isset($_COOKIE["auto"]) || empty($_COOKIE["auto"])) { return false; } // Decode cookie value if (! $cookie = @json_decode($_COOKIE["auto"], true)) { return false; } // Check all parameters if (! (isset($cookie['user']) || isset($cookie['token']) || isset($cookie['signature']))) { return false; } $var = $cookie['user'] . $cookie['token']; // Check Signature if (! $this->verify($var, $cookie['signature'])) { throw new Exception("Cokies has been tampared with"); } // Check Database $info = $this->db->get($cookie['user']); if (! $info) { return false; // User must have deleted accout } // Check User Data if (! $info = json_decode($info, true)) { throw new Exception("User Data corrupted"); } // Verify Token if ($info['token'] !== $cookie['token']) { throw new Exception("System Hijacked or User use another browser"); } /** * Important * To make sure the cookie is always change * reset the Token information */ $this->remember($info['user']); return $info; } public function remember($user) { $cookie = [ "user" => $user, "token" => $this->getRand(64), "signature" => null ]; $cookie['signature'] = $this->hash($cookie['user'] . $cookie['token']); $encoded = json_encode($cookie); // Add User to database $this->db->set($user, $encoded); /** * Set Cookies * In production enviroment Use * setcookie("auto", $encoded, time() + $expiration, "/~root/", * "example.com", 1, 1); */ setcookie("auto", $encoded); // Sample } public function verify($data, $hash) { $rand = substr($hash, 0, 4); return $this->hash($data, $rand) === $hash; } private function hash($value, $rand = null) { $rand = $rand === null ? $this->getRand(4) : $rand; return $rand . bin2hex(hash_hmac('sha256', $value . $rand, $this->key, true)); } private function getRand($length) { switch (true) { case function_exists("mcrypt_create_iv") : $r = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM); break; case function_exists("openssl_random_pseudo_bytes") : $r = openssl_random_pseudo_bytes($length); break; case is_readable('/dev/urandom') : // deceze $r = file_get_contents('/dev/urandom', false, null, 0, $length); break; default : $i = 0; $r = ""; while($i ++ < $length) { $r .= chr(mt_rand(0, 255)); } break; } return substr(bin2hex($r), 0, $length); } } 

    Testen in Firefox und Chrome

    Bildbeschreibung hier eingeben

    Vorteil

    • Bessere Sicherheit
    • Begrenzter Zugriff für Angreifer
    • Wenn ein Cookie gestohlen wird, ist es nur für den einmaligen Zugriff gültig
    • Wenn der ursprüngliche Benutzer als nächstes auf die Site zugreift, können Sie den Benutzer automatisch auf Diebstahl aufmerksam machen

    Nachteil

    • Unterstützt keine dauerhafte Verbindung über mehrere Browser (Mobile & Web)
    • Der Cookie kann trotzdem gestohlen werden, da der Benutzer die Benachrichtigung erst nach dem nächsten Login erhält.

    Schnelle Lösung

    • Einführung eines Genehmigungssystems für jedes System, das eine dauerhafte Verbindung haben muss
    • Verwenden Sie mehrere Cookies für die Authentifizierung

    Mehrfacher Cookie-Ansatz

    Wenn ein Angreifer dabei ist, Koche zu stehlen, konzentriert er sich nur auf eine bestimmte Website oder Domain, z. beispiel.com

    Aber wirklich können Sie einen Benutzer von 2 verschiedenen Domänen ( example.com & fakeaddsite.com ) authentifizieren und es wie "Advert Cookie" aussehen lassen

    • Benutzer An mich angemeldet mit example.com
    • Speichern Sie Benutzername, Token, Referenz in Cookie
    • Speichern Sie Benutzername, Token, Referenz in der database zB. Memcache
    • Senden Sie refrence id über get und iframe an fakeaddsite.com
    • fakeaddsite.com verwendet den Verweis zum Abrufen von Benutzer und Token von der database
    • fakeaddsite.com speichert die Signatur
    • Wenn ein Benutzer Signaturinformationen mit iframe von fakeaddsite.com zurückholt
    • Kombinieren Sie die Daten und führen Sie die validation durch
    • ..... du kennst den Rest

    Einige Leute fragen sich vielleicht, wie können Sie 2 verschiedene Cookies verwenden? Nun, es ist möglich, fakeaddsite.com = 192.168.1.120 example.com = localhost und fakeaddsite.com = 192.168.1.120 . Wenn Sie die Cookies überprüfen, würde es so aussehen

    Bildbeschreibung hier eingeben

    Aus dem Bild oben

    • Die aktuelle besuchte Seite ist localhost
    • Es enthält auch Cookies von 192.168.1.120 gesetzt

    192.168.1.120

    • HTTP_REFERER nur den definierten HTTP_REFERER
    • REMOTE_ADDR nur die Verbindung von der angegebenen REMOTE_ADDR
    • Kein JavaScript, kein Inhalt aber besteht aus nichts als Zeicheninformation und fügt sie hinzu oder holt sie vom Plätzchen zurück

    Vorteil

    • 99% Prozent der Zeit haben Sie den Angreifer ausgetrickst
    • Sie können den Account beim ersten Versuch des Angreifers einfach sperren
    • Der Angriff kann bereits vor dem nächsten Login wie bei den anderen Methoden verhindert werden

    Nachteil

    • Mehrere Anfragen an den Server nur für eine einzige Anmeldung

    Verbesserung

    • Fertig benutze iframe benutze ajax

    Es gibt zwei sehr interessante Artikel, die ich auf der Suche nach einer perfekten Lösung für das “remember-me” -Problem gefunden habe:

    • Persistent Login Cookie Best Practice
    • Verbesserte Best Practice für beständiges Login-Cookie

    Ich habe hier einen Aspekt dieser Frage gestellt, und die Antworten führen Sie zu allen Token-basierten Cookie-Links, die Sie benötigen.

    Grundsätzlich speichern Sie die userId nicht im Cookie. Sie speichern ein einmaliges Token (große Zeichenfolge), mit dem der Benutzer seine alte Anmeldesitzung abruft. Um es wirklich sicher zu machen, fragen Sie nach einem Passwort für schwere Operationen (wie das Ändern des Passworts selbst).

    Ich empfehle den von Stefan erwähnten Ansatz (dh befolge die Richtlinien in Verbesserter Beständiger Login Cookie Best Practice ) und empfiehl auch, dass du sicherstellst, dass deine Cookies HttpOnly Cookies sind, damit sie nicht für potentiell bösartigen JavaScript zugänglich sind.

    Erzeugen Sie einen Hash, vielleicht mit einem nur Ihnen bekannten Geheimnis, und speichern Sie ihn in Ihrer database, damit er dem Benutzer zugeordnet werden kann. Sollte recht gut funktionieren.

    Alter Thread, aber immer noch eine berechtigte Sorge. Ich bemerkte einige gute Reaktionen auf die Sicherheit und die Vermeidung von “Sicherheit durch Dunkelheit”, aber die tatsächlichen technischen Methoden waren in meinen Augen nicht ausreichend. Dinge, die ich sagen muss, bevor ich meine Methode einbringe:

    • Speichern Sie niemals ein Passwort im Klartext … EVER!
    • Speichern Sie NIEMALS das Hash-Passwort eines Benutzers an mehr als einem Ort in Ihrer database. Ihr Server-Backend ist immer in der Lage, das Hash-Passwort aus der Benutzertabelle zu ziehen. Es ist nicht effizienter, redundante Daten anstelle von zusätzlichen DB-Transaktionen zu speichern, das Gegenteil ist der Fall.
    • Ihre Sitzungs-IDs sollten eindeutig sein, so dass zwei Benutzer niemals eine ID teilen können, daher der Zweck einer ID (könnte Ihre Führerschein-ID jemals mit einer anderen Person übereinstimmen? Nein.) Dies erzeugt eine zweiteilige eindeutige Kombination, basierend auf 2 eindeutige Zeichenfolgen Ihre Sitzungstabelle sollte die ID als PK verwenden. Damit mehrere Geräte für die automatische Anmeldung vertrauenswürdig sind, verwenden Sie eine andere Tabelle für vertrauenswürdige Geräte, die die Liste aller validierten Geräte enthält (siehe Beispiel unten), und mithilfe des Benutzernamens zugeordnet wird.
    • Es dient nicht dazu, bekannte Daten in einen Cookie zu hacken, der Cookie kann kopiert werden. Was wir suchen, ist ein kompatibles Benutzergerät, das authentische Informationen liefert, die nicht ohne einen Angreifer erhalten werden können, der den Computer des Benutzers kompromittiert (siehe auch mein Beispiel). Dies würde jedoch bedeuten, dass ein legitimer Benutzer, der die statischen Informationen seiner Maschine verbietet (z. B. MAC-Adresse, Gerätehostname, Benutzeragent, wenn durch Browser eingeschränkt, usw.) konsistent bleibt (oder es an erster Stelle verfälscht), nicht in der Lage ist Verwenden Sie diese function. Bedenken Sie jedoch, dass Sie Benutzern, die sich eindeutig identifizieren , die automatische Anmeldung anbieten. Wenn sie sich also weigern, bekannt zu werden, indem sie ihren MAC spoofen, ihren Useragent spoofen, ihren Hostnamen manipulieren / ändern, sich hinter Proxies verstecken, usw., dann sind sie nicht identifizierbar und sollten niemals für einen automatischen Dienst authentifiziert werden. Wenn Sie dies möchten, müssen Sie sich den Smartcard-Zugriff ansehen, der mit clientseitiger Software gebündelt ist, die die Identität für das verwendete Gerät festlegt.

    Alles in allem gibt es zwei gute Möglichkeiten, sich automatisch in Ihrem System anzumelden.

    Erstens, der billige, einfache Weg, der alles auf jemand anderen legt. Wenn Sie Ihre Website bei der Anmeldung mit Ihrem google + Konto unterstützen, haben Sie wahrscheinlich eine optimierte Google + -Schaltfläche, mit der der Nutzer angemeldet wird, wenn er bereits bei Google angemeldet ist (ich habe dies hier getan, um diese Frage zu beantworten in Google signiert). Wenn Sie möchten, dass sich der Benutzer automatisch anmeldet, wenn er bereits bei einem vertrauenswürdigen und unterstützten Authentifikator angemeldet ist und das entsprechende Kontrollkästchen aktiviert hat, lassen Sie die clientseitigen Skripts den Code hinter der entsprechenden Schaltfläche “Anmelden mit” ausführen, bevor Sie ihn laden Stellen Sie sicher, dass der Server eine eindeutige ID in einer automatisch signierten Tabelle mit dem Benutzernamen, der Sitzungs-ID und dem für den Benutzer verwendeten Authentifikator speichert. Da diese Anmeldeverfahren AJAX verwenden, warten Sie trotzdem auf eine Antwort, und diese Antwort ist entweder eine validierte Antwort oder eine Ablehnung. Wenn Sie eine validierte Antwort erhalten, verwenden Sie sie wie gewohnt und laden Sie dann den angemeldeten Benutzer normal weiter. Andernfalls ist die Anmeldung fehlgeschlagen, aber sagen Sie dem Benutzer nicht, einfach weiter als nicht angemeldet, werden sie bemerken. Dies soll verhindern, dass ein Angreifer, der Cookies gestohlen hat (oder sie gefälscht hat, um Privilegien zu eskalieren) erfährt, dass sich der Benutzer automatisch auf der Website anmeldet.

    Dies ist billig und könnte auch von einigen als dreckig angesehen werden, da es versucht, Ihre potentiell bereits signierten Websites wie Google und Facebook zu validieren, ohne Ihnen dies zu sagen. Es sollte jedoch nicht für Nutzer verwendet werden, die keine automatische Anmeldung für Ihre Website angefordert haben. Diese Methode ist nur für die externe Authentifizierung gedacht, z. B. mit Google+ oder FB.

    Da ein externer Authentifizierer verwendet wurde, um dem Server hinter den Kulissen mitzuteilen, ob ein Benutzer validiert wurde oder nicht, kann ein Angreifer nur eine eindeutige ID erhalten, die für sich alleine nutzlos ist. Ich werde es ausarbeiten:

    • Benutzer ‘joe’ besucht die Seite zum ersten Mal, Sitzungs-ID wird in ‘Sitzung’ des Cookies gesetzt.
    • Benutzer ‘joe’ Loggt sich ein, eskaliert Privilegien, erhält eine neue Session ID und erneuert die ‘Sitzung’ des Cookies.
    • Benutzer ‘joe’ wählt sich automatisch mit google + aus, erhält eine eindeutige ID in Cookie ‘keepmesignedin’.
    • Nutzer “Joe” hat Google angemeldet, damit Ihre Website den Nutzer mithilfe von Google in Ihrem Back-End automatisch anmelden kann.
    • Der Angreifer versucht systematisch eindeutige IDs für “keepmesignedin” (dies ist öffentliches Wissen, das jedem Benutzer ausgehändigt wird) zu testen und wird nirgendwo anders angemeldet; versucht eine eindeutige ID für “Joe”.
    • Der Server erhält eine eindeutige ID für “joe”, zieht eine Übereinstimmung in der DB für ein Google + -Konto.
    • Der Server sendet Angreifer an die Anmeldeseite, die eine AJAX-Anforderung an Google zum Anmelden ausführt.
    • Der Google-Server erhält eine Anfrage und verwendet seine API, um zu sehen, dass Angreifer derzeit nicht angemeldet ist.
    • Google sendet eine Antwort, dass derzeit kein Nutzer über diese Verbindung angemeldet ist.
    • Die Seite des Angreifers erhält eine Antwort. Das Skript leitet automatisch zur Anmeldeseite mit einem POST-Wert um, der in der URL codiert ist.
    • Die Login-Seite erhält den POST-Wert, sendet den Cookie für ‘keepmesignedin’ an einen leeren Wert und ein gültiges Datum bis zum 1.1.1970, um einen automatischen Versuch zu verhindern. Der Browser des Angreifers löscht den Cookie einfach.
    • Der Angreifer erhält eine normale Anmeldeseite zum ersten Mal.

    Unabhängig davon, ob ein Angreifer eine ID verwendet, die nicht existiert, sollte der Versuch bei allen Versuchen fehlschlagen, außer wenn eine validierte Antwort empfangen wird.

    Diese Methode kann und sollte in Verbindung mit Ihrem internen Authentifikator für diejenigen verwendet werden, die sich mit einem externen Authentifikator bei Ihrer Site anmelden.

    =========

    Nun, für Ihr eigenes Authentifizierungssystem, das Benutzer automatisch anmelden kann, mache ich es so:

    Die DB hat ein paar Tabellen:

     TABLE users: UID - auto increment, PK username - varchar(255), unique, indexed, NOT NULL password_hash - varchar(255), NOT NULL ... 

    Beachten Sie, dass der Benutzername 255 Zeichen lang sein kann. Ich habe Server-Programm begrenzen Benutzernamen in meinem System auf 32 Zeichen, aber externe Authentifikatoren haben möglicherweise Benutzernamen mit ihrer @ domain.tld größer als das, so dass ich nur die maximale Länge einer E-Mail-Adresse für maximale Kompatibilität unterstützen.

     TABLE sessions: session_id - varchar(?), PK session_token - varchar(?), NOT NULL session_data - MediumText, NOT NULL 

    Beachten Sie, dass in dieser Tabelle kein Benutzerfeld vorhanden ist, da sich der Benutzername bei der Anmeldung in den Sitzungsdaten befindet und das Programm keine Nulldaten zulässt. Die session_id und das session_token können mit zufälligen md5-Hashes, sha1 / 128/256-Hashes, Datums- / Zeitstempeln mit zufälligen Strings, Hashes oder was immer Sie möchten, erzeugt werden, aber die Entropie Ihrer Ausgabe sollte so hoch wie tolerierbar bleiben Brute-Force-Attacken können sogar vom Boden getrennt werden, und alle Hashes, die von Ihrer Sitzungsklasse generiert werden, sollten vor dem Hinzufügen auf Übereinstimmungen in der Sitzungstabelle überprüft werden.

     TABLE autologin: UID - auto increment, PK username - varchar(255), NOT NULL, allow duplicates hostname - varchar(255), NOT NULL, allow duplicates mac_address - char(23), NOT NULL, unique token - varchar(?), NOT NULL, allow duplicates expires - datetime code 

    MAC-Adressen sollen ihrer Natur nach UNIQUE sein, daher ist es sinnvoll, dass jeder Eintrag einen eindeutigen Wert hat. Hostnamen hingegen könnten in verschiedenen Netzwerken legitimiert werden. Wie viele Leute benutzen “Home-PC” als einen ihrer Computernamen? Der Benutzername wird von den Sitzungsdaten vom Server-Backend übernommen, sodass eine Manipulation nicht möglich ist. Was das Token betrifft, sollte die gleiche Methode zum Erzeugen von Sitzungstoken für Seiten verwendet werden, um Tokens in Cookies für die automatische Anmeldung des Benutzers zu generieren. Schließlich wird der datetime-Code hinzugefügt, wenn der Benutzer seine Anmeldeinformationen erneut validieren muss. Aktualisieren Sie dieses Datum entweder bei der Benutzeranmeldung, behalten Sie es innerhalb weniger Tage bei, oder erzwingen Sie, dass es unabhängig von der letzten Anmeldung abläuft, und behalten Sie es nur für einen Monat oder so, je nachdem, was Ihr Design vorgibt.

    Dies verhindert, dass jemand den MAC und den Hostnamen für einen Benutzer, der sich automatisch anmeldet, systematisch manipuliert. NIEMALS darf der Benutzer ein Cookie mit seinem Passwort, Klartext oder auf andere Weise aufbewahren. Lassen Sie das Token bei jeder Seitennavigation genauso wie das Sitzungstoken neu generieren. Dies reduziert massiv die Wahrscheinlichkeit, dass ein Angreifer ein gültiges Token-Cookie erhalten und es zur Anmeldung verwenden kann. Einige Leute werden versuchen zu sagen, dass ein Angreifer die Cookies von dem Opfer stehlen und einen Session Replay-Angriff zum Login durchführen kann. Wenn ein Angreifer die Cookies stehlen könnte (was möglich ist), hätten sie sicherlich das gesamte Gerät kompromittiert, was bedeutet, dass sie das Gerät einfach dazu verwenden könnten, sich trotzdem anzumelden, was den Zweck des vollständigen Diebstahls von Cookies vereitelt. Solange Ihre Website über HTTPS läuft (was bei Passwörtern, CC-Nummern oder anderen Anmeldesystemen der Fall sein sollte), haben Sie dem Benutzer den gesamten Schutz gewährt, den Sie in einem Browser haben können.

    Eine Sache, die Sie beachten sollten: Sitzungsdaten sollten nicht ablaufen, wenn Sie die automatische Anmeldung verwenden. Sie können die Möglichkeit ablehnen, die Sitzung fälschlicherweise fortzusetzen, aber die validation in das System sollte die Sitzungsdaten fortsetzen, wenn es sich um persistente Daten handelt, die voraussichtlich zwischen den Sitzungen fortgesetzt werden. Wenn Sie sowohl persistente als auch nicht persistente Sitzungsdaten verwenden möchten, verwenden Sie eine andere Tabelle für persistente Sitzungsdaten mit dem Benutzernamen als PK, und lassen Sie den Server sie wie die normalen Sitzungsdaten abrufen, verwenden Sie einfach eine andere Variable.

    Sobald eine Anmeldung auf diese Weise erreicht wurde, sollte der Server die Sitzung noch validieren. Hier können Sie die Erwartungen für gestohlene oder kompromittierte Systeme kodieren. Muster und andere erwartete Ergebnisse von Logins zu Sitzungsdaten können oft zu Schlüssen führen, dass ein System entführt wurde oder Cookies gefälscht wurden, um Zugang zu erhalten. Hier kann Ihr ISS-Tech Regeln erstellen, die eine Kontosperrung oder automatische Entfernung eines Benutzers aus dem Auto-Sign-in-System auslösen, so dass der Angreifer lange genug abwesend ist, um festzustellen, wie der Angreifer erfolgreich war und wie er sie abbricht.

    Als Abschlussbemerkung sollten Sie sicherstellen, dass alle Wiederherstellungsversuche, Kennwortänderungen oder Anmeldeerrors nach dem Schwellenwert dazu führen, dass die automatische Anmeldung deaktiviert wird, bis der Benutzer ordnungsgemäß überprüft und bestätigt hat, dass dies der Fall ist.

    Ich entschuldige mich, wenn jemand erwartet hätte, dass Code in meiner Antwort ausgegeben wird, das wird hier nicht passieren. Ich werde sagen, dass ich PHP, jQuery und AJAX verwende, um meine Aufstellungsorte laufen zu lassen, und ich NIEMALS Windows als ein Server verwende … überhaupt.

    Meine Lösung ist so. Es ist nicht 100% kugelsicher, aber ich denke, es wird dich in den meisten Fällen retten.

    Wenn sich ein Benutzer erfolgreich angemeldet hat, erstellen Sie eine Zeichenfolge mit diesen Informationen:

     $data = (SALT + ":" + hash(User Agent) + ":" + username + ":" + LoginTimestamp + ":"+ SALT) 

    Verschlüsseln Sie $data , setzen Sie den Typ auf HttpOnly und setzen Sie cookie.

    Wenn der Nutzer auf Ihre Website zurückkehrt, führen Sie folgende Schritte aus:

    1. Erhalte Cookie-Daten. Entfernen Sie gefährliche Zeichen in Cookies. Explode es mit : Charakter.
    2. Überprüfen Sie die Gültigkeit. Wenn der Cookie älter als X Tage ist, leiten Sie den Benutzer zur Anmeldeseite um.
    3. Wenn Cookie nicht alt ist; Holen Sie sich die letzte Passwortänderung aus der database. Wenn das Passwort nach dem letzten Login des Benutzers geändert wird, wird der Benutzer zur Anmeldeseite umgeleitet.
    4. Wenn Pass kürzlich nicht geändert wurde; Erhalte den aktuellen Browser-Agenten des Benutzers. Überprüfen Sie, ob (currentUserAgentHash == cookieUserAgentHash). Wenn die Agenten gleich sind, gehen Sie zum nächsten Schritt, andernfalls auf die Anmeldeseite redirect.
    5. Wenn alle Schritte erfolgreich bestanden wurden, autorisieren Sie den Benutzernamen.

    Wenn Benutzer sich abmelden, entfernen Sie diesen Cookie. Erstellen Sie ein neues Cookie, wenn sich der Benutzer erneut anmeldet.

    I don’t understand the concept of storing encrypted stuff in a cookie when it is the encrypted version of it that you need to do your hacking. If I’m missing something, please comment.

    I am thinking about taking this approach to ‘Remember Me’. If you can see any issues, please comment.

    1. Create a table to store “Remember Me” data in – separate to the user table so that I can log in from multiple devices.

    2. On successful login (with Remember Me ticked):

      a) Generate a unique random string to be used as the UserID on this machine: bigUserID

      b) Generate a unique random string: bigKey

      c) Store a cookie: bigUserID:bigKey

      d) In the “Remember Me” table, add a record with: UserID, IP Address, bigUserID, bigKey

    3. If trying to access something that requires login:

      a) Check for the cookie and search for bigUserID & bigKey with a matching IP address

      b) If you find it, Log the person in but set a flag in the user table “soft login” so that for any dangerous operations, you can prompt for a full login.

    4. On logout, Mark all the “Remember Me” records for that user as expired.

    The only vulnerabilities that I can see is;

    • you could get hold of someone’s laptop and spoof their IP address with the cookie.
    • you could spoof a different IP address each time and guess the whole thing – but with two big string to match, that would be…doing a similar calculation to above…I have no idea…huge odds?

    I read all the answers and still found it difficult to extract what I was supposed to do. If a picture is worth 1k words I hope this helps others implement a secure persistent storage based on Barry Jaspan’s Improved Persistent Login Cookie Best Practice

    Bildbeschreibung hier eingeben

    If you have questions, feedback, or suggestions, I will try to update the diagram to reflect for the newbie trying to implement a secure persistent login.

    Implementing a “Keep Me Logged In” feature means you need to define exactly what that will mean to the user. In the simplest case, I would use that to mean the session has a much longer timeout: 2 days (say) instead of 2 hours. To do that, you will need your own session storage, probably in a database, so you can set custom expiry times for the session data. Then you need to make sure you set a cookie that will stick around for a few days (or longer), rather than expire when they close the browser.

    I can hear you asking “why 2 days? why not 2 weeks?”. This is because using a session in PHP will automatically push the expiry back. This is because a session’s expiry in PHP is actually an idle timeout.

    Now, having said that, I’d probably implement a harder timeout value that I store in the session itself, and out at 2 weeks or so, and add code to see that and to forcibly invalidate the session. Or at least to log them out. This will mean that the user will be asked to login periodically. Yahoo! does this.