it-swarm.com.de

"Keep me eingeloggt" - der beste Ansatz

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

Ich möchte beim Anmelden die Option "Angemeldet bleiben" anbieten, mit der ein Cookie für zwei Wochen auf dem Computer des Benutzers abgelegt wird. Anschließend wird die Sitzung mit den gleichen Details erneut gestartet, wenn sie zur App zurückkehren.

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

229
Matthew

OK, lassen Sie mich das ganz offen sagen: Wenn Sie Benutzerdaten oder von Benutzerdaten abgeleitete Elemente zu diesem Zweck in ein Cookie einfügen, tun Sie etwas Falsches.

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

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

Stellen Sie sich für eine Sekunde vor, Sie wären ein Angreifer. In Ihrer Sitzung wird ein kryptografisches Cookie für die Erinnerung angezeigt. Es ist 32 Zeichen breit. Gee. Das kann ein MD5 sein ...

Stellen wir uns auch für eine Sekunde vor, dass sie den von Ihnen verwendeten Algorithmus kennen. Zum Beispiel:

md5(salt+username+ip+salt)

Jetzt muss ein Angreifer nur noch das "Salz" erzwingen (was eigentlich kein Salz ist, aber dazu später mehr), und er kann jetzt alle gefälschten Token erzeugen, die er will, mit jedem Benutzernamen für seine IP-Adresse! Aber es ist schwer, ein Salz zu erzwingen, oder? Absolut. Aber moderne GPUs sind außerordentlich gut darin. Und wenn Sie nicht genügend Zufälligkeit verwenden (es groß genug machen), wird es schnell fallen und damit die Schlüssel zu Ihrem Schloss.

Kurz gesagt, das einzige, was Sie schützt, ist das Salz, das Sie nicht so gut schützt, wie Sie denken.

Aber warte!

All das 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 Dunkelheit , auf die sich niemals verlassen sollte .

Der bessere Weg

Der bessere Weg besteht darin, die Informationen eines Benutzers, mit Ausnahme der ID, niemals vom Server zu entfernen.

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

Was passiert, wenn der Angreifer das zufällige Token eines anderen Benutzers errät?

Lass uns hier ein bisschen rechnen. Wir generieren ein 128-Bit-Zufallstoken. Das heißt, es gibt:

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

Um zu zeigen, wie absurd diese Zahl ist, stellen wir uns jeden Server im Internet vor (sagen wir heute 50.000.000), der versucht, diese Zahl mit einer Geschwindigkeit von jeweils 1.000.000.000 pro Sekunde zu erzwingen. In der Realität würden Ihre Server unter dieser Last schmelzen, aber lassen Sie uns dies 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 Vermutungen 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 Sextillionen Sekunden ...

Versuchen wir, das auf freundlichere Zahlen zu bringen.

215,626,585,489,599 years

Oder noch besser:

47917 times the age of the universe

Ja, das ist das 47917-fache des Zeitalters des Universums ...

Grundsätzlich wird es nicht geknackt werden.

Also zusammenfassend:

Ich empfehle, den Cookie in drei Teilen aufzubewahren.

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 validieren:

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 nicht das Token oder die Kombination aus Benutzer und Token, um einen Datensatz in Ihrer Datenbank zu suchen. Stellen Sie immer sicher, dass Sie einen Datensatz basierend auf dem Benutzer abrufen und eine zeitsichere Vergleichsfunktion verwenden, um das abgerufene Token anschließend zu vergleichen. Mehr über Timing-Attacken .

Nun ist es sehr wichtig, dass der SECRET_KEY ein kryptografisches Geheimnis ist (generiert durch etwas wie /dev/urandom und/oder abgeleitet von a Eingabe mit hoher Entropie). Außerdem muss GenerateRandomToken() eine starke Zufallsquelle sein (mt_Rand() ist bei weitem nicht stark genug. Verwenden Sie eine Bibliothek wie RandomLib oder random_compat oder mcrypt_create_iv() mit DEV_URANDOM ) ...

Die hash_equals() soll Timing-Attacken verhindern. Wenn Sie eine PHP Version unter PHP 5.6 verwenden, wird die Funktion hash_equals() nicht unterstützt. In diesem Fall können Sie hash_equals() durch die timingSafeCompare-Funktion 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;
}
701
ircmaxell

Sicherheitshinweis: Es ist keine gute Idee, den Cookie von einem MD5-Hash mit deterministischen Daten abzugrenzen. Es ist besser, ein zufälliges Token zu verwenden, das von einem CSPRNG abgeleitet ist. Siehe Antwort von ircmaxell zu dieser Frage für einen sichereren Ansatz.

Normalerweise mache ich so etwas:

  1. Benutzer meldet sich mit "mich anmelden" an 
  2. Sitzung erstellen 
  3. Erstellen Sie einen Cookie mit dem Namen SOMETHING, der Folgendes enthält: md5 (Salt + Benutzername + IP + Salt) und einen Cookie mit dem Namen someElse, der die ID enthält 
  4. Cookie in der Datenbank speichern 
  5. User macht Sachen und geht ---- 
  6. Der Benutzer kehrt zurück, prüft, ob etwas vorhandenes Cookie ist, ob er existiert, holt den alten Hash aus der Datenbank für diesen Benutzer ab und überprüft, ob der Inhalt des Cookies SOMETHING mit dem Hash aus der Datenbank übereinstimmt ip) also: cookieHash == databaseHash == md5 (salt + username + ip + salt), wenn dies der Fall ist, gehe zu 2, wenn sie nicht zu 1 wechseln

Natürlich können Sie verschiedene Cookie-Namen usw. verwenden. Sie können auch den Inhalt des Cookies ein wenig ändern. Stellen Sie nur sicher, dass er nicht so einfach erstellt werden kann. Sie können zum Beispiel auch eine user_salt erstellen, wenn der Benutzer erstellt wird, und dies auch in das Cookie setzen. 

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

95
Pim Jager

Einführung

Ihr Titel “Keep me eingeloggt” - der beste Ansatz macht es mir schwer zu wissen, wo ich anfangen soll. Wenn Sie den besten Ansatz suchen, müssen Sie Folgendes berücksichtigen:

  • Identifizierung
  • Sicherheit 

Kekse

Cookies sind anfällig. Zwischen häufigen Browser-Cookie-Diebstahl-Sicherheitsanfälligkeiten und Cross-Site-Scripting-Angriffen müssen wir akzeptieren, dass Cookies nicht sicher sind. Zur Verbesserung der Sicherheit müssen Sie beachten, dass phpsetcookies über zusätzliche Funktionen verfügt, z 

bool setcookie (Zeichenfolge $ name [ Zeichenfolge $ value [ int $ expire = 0 [ Zeichenfolge $ path [ Zeichenfolge $ domain [ bool $ secure = false [ bool $ httponly = false]]]]]])

  • sicher (über HTTPS-Verbindung)
  • httponly (Identitätsdiebstahl durch XSS-Angriff reduzieren)

Definitionen 

  • Token (unvorhersehbare zufällige Zeichenfolge mit n Länge, z. B./dev/urandom)
  • Referenz (unvorhersehbare zufällige Zeichenfolge mit n Länge, z. B./dev/urandom)
  • Signatur (Generieren eines verschlüsselten Hashwerts mit der HMAC-Methode)

Einfacher Ansatz 

Eine einfache Lösung wäre:

  • Benutzer ist mit Remember Me angemeldet
  • Login-Cookie mit Token und Signatur ausgestellt
  • Wenn zurückkehrt, wird die Signatur geprüft
  • Wenn Signatur in Ordnung ist, werden Benutzername und Token in der Datenbank nachgeschlagen 
  • wenn nicht gültig .. zurück zur Anmeldeseite
  • Wenn gültig automatisch anmelden 

Die obige Fallstudie fasst alle Beispiele zusammen, die auf dieser Seite aufgeführt sind, aber ihre Nachteile liegen darin 

  • Es gibt keine Möglichkeit festzustellen, ob die Cookies gestohlen wurden
  • Der Angreifer kann auf sensible Vorgänge wie das Ändern des Passworts oder Daten wie persönliche Informationen und Backinformationen usw. zugreifen.
  • Der kompromittierte Cookie würde noch für die Lebensdauer des Cookies gelten 

Bessere Lösung

Eine bessere Lösung wäre 

  • Der Benutzer ist angemeldet und die Erinnerung an mich ist ausgewählt 
  • Token & Signatur erstellen und in Cookie speichern 
  • Die Token sind zufällig und gelten nur für die Einzelauthentifizierung 
  • Das Token wird bei jedem Besuch der Website ersetzt
  • Wenn ein nicht protokollierter Benutzer die Site besucht, werden Signatur, Token und Benutzername überprüft 
  • Denken Sie daran, dass das Login einen eingeschränkten Zugriff haben sollte und keine Änderung des Passworts, der persönlichen Informationen usw. zulassen sollte.

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());
}

Verwendete Klasse 

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 

enter image description here

Vorteil 

  • Bessere Sicherheit 
  • Eingeschränkter Zugriff für Angreifer 
  • Wenn ein Cookie gestohlen wird, ist er nur für den einmaligen Zugriff gültig 
  • Wenn der ursprüngliche Benutzer als Nächstes auf die Website zugreift, können Sie den Benutzer automatisch erkennen und ihn über Diebstahl informieren 

Nachteil 

  • Unterstützt keine dauerhafte Verbindung über mehrere Browser (Mobile & Web)
  • Das Cookie kann weiterhin gestohlen werden, da der Benutzer die Benachrichtigung erst nach der nächsten Anmeldung 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

Mehrfach-Cookie-Ansatz

Wenn ein Angreifer Cookies stiehlt, konzentrieren Sie sich nur auf eine bestimmte Website oder Domäne, z. example.com

Aber wirklich, Sie können einen Benutzer von zwei verschiedenen Domains (example.com & fakeaddsite.com) authentifizieren und ihn wie "Advert Cookie" aussehen lassen. 

  • Benutzer An example.com mit "Remember me" angemeldet
  • Speichern Sie den Benutzernamen, das Token und die Referenz im Cookie
  • Speichern Sie Benutzername, Token, Referenz in der Datenbank, z. Memcache 
  • Sende die Refence-ID über get und iframe an fakeaddsite.com
  • fakeaddsite.com verwendet die Referenz, um Benutzer und Token aus der Datenbank abzurufen
  • fakeaddsite.com speichert die Signatur
  • Wenn ein Benutzer zurückgibt, rufen Sie Signaturinformationen mit iframe von fakeaddsite.com ab
  • Kombinieren Sie die Daten und führen Sie die Validierung durch 
  • ..... du kennst das restliche

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

enter image description here

Aus dem Bild oben 

  • Die aktuell besuchte Site ist localhost 
  • Es enthält auch Cookies, die von 192.168.1.120 gesetzt werden

192.168.1.120

  • Akzeptiert nur definierte HTTP_REFERER
  • Akzeptiert nur eine Verbindung von dem angegebenen REMOTE_ADDR
  • Kein JavaScript, kein Inhalt, sondern nichts anderes als das Signieren von Informationen und Hinzufügen oder Abrufen von Cookies 

Vorteil

  • 99% der Zeit, in der Sie den Angreifer ausgetrickst haben 
  • Sie können das Konto beim ersten Versuch des Angreifers problemlos sperren 
  • Ein Angriff kann auch vor dem nächsten Login wie bei den anderen Methoden verhindert werden 

Nachteil 

  • Mehrfachanforderung an den Server nur für ein einzelnes Login 

Verbesserung 

  • Fertig iframe verwenden ajax 
71
Baba

Es gibt zwei sehr interessante Artikel, die ich auf der Suche nach einer perfekten Lösung für das "Remember-Me" -Problem gefunden habe:

24
Stefan Gehrig

Ich habe einen Blickwinkel dieser Frage gestellt here , und die Antworten werden Sie zu allen tokenbasierten Timeout-Cookie-Links führen, die Sie benötigen.

Grundsätzlich speichern Sie die userId nicht im Cookie. Sie speichern ein einmaliges Token (riesige Zeichenfolge), mit dem der Benutzer seine alte Anmeldesitzung abholt. Um es wirklich sicher zu machen, müssen Sie ein Kennwort für schwere Vorgänge (wie das Ändern des Kennworts selbst) anfordern.

6
Dan Rosenstark

Ich würde das von Stefan erwähnte Vorgehen empfehlen (dh den Richtlinien in Verbesserte beständige Login-Cookie-Best Practice folgen) und auch empfehlen, sicherzustellen, dass Ihre Cookies HttpOnly-Cookies sind damit sie nicht für potenziell böswillige Personen zugänglich sind , JavaScript.

5
Walter Rumsby

Alter Thread, aber immer noch ein berechtigtes Anliegen. Ich habe einige gute Reaktionen auf die Sicherheit festgestellt und die Verwendung von "Sicherheit durch Unklarheit" vermieden, aber die tatsächlichen technischen Methoden waren für mich nicht ausreichend. Dinge, die ich sagen muss, bevor ich meine Methode einbringe:

  • NEVER Speichern eines Passworts im Klartext ... EVER!
  • NEVER speichert das Hash-Passwort eines Benutzers an mehr als einem Ort in Ihrer Datenbank. 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. Die Umkehrung ist wahr.
  • Ihre Sitzungs-IDs sollten eindeutig sein, sodass keine zwei Benutzer jemals eine ID freigeben können, daher der Zweck einer ID (könnte Ihre Führerschein-ID-Nummer jemals mit einer anderen Person übereinstimmen? Nein.) einzigartige Kombination, basierend auf 2 eindeutigen Zeichenketten. Ihre Sitzungstabelle sollte die ID als PK verwenden. Verwenden Sie eine andere Tabelle für vertrauenswürdige Geräte, die die Liste aller geprüften Geräte enthält (siehe mein Beispiel unten), und lässt sich mithilfe des Benutzernamens zuordnen, damit mehrere Geräte für die automatische Anmeldung als vertrauenswürdig eingestuft werden können.
  • Es dient nicht dazu, bekannte Daten in einen Cookie zu kopieren, der Cookie kann kopiert werden. Was wir suchen, ist ein konformes Benutzergerät, das authentische Informationen liefert, die nicht ohne einen Angreifer abgerufen werden können, der den Computer des Benutzers gefährdet (siehe auch mein Beispiel). Dies würde jedoch bedeuten, dass ein legitimer Benutzer, der die statischen Informationen seines Computers (z. B. MAC-Adresse, Hostname des Geräts, Benutzeragent, falls durch den Browser eingeschränkt, usw.) verbietet, nicht konsistent bleiben kann (oder es überhaupt erst fälscht) Verwenden Sie diese Funktion. Wenn dies jedoch ein Problem ist, sollten Sie die Tatsache in Betracht ziehen, dass Sie Benutzern, die sich eindeutig identifizieren, eine automatische Anmeldung anbieten. Spoofing/Ändern ihres Hostnamens, Verstecken hinter Proxy-Servern usw., dann sind sie nicht identifizierbar und sollten niemals für einen automatischen Dienst authentifiziert werden. Wenn Sie dies wünschen, müssen Sie den Smartcard-Zugriff in Verbindung mit clientseitiger Software prüfen, der die Identität des verwendeten Geräts festlegt.

Alles in allem gibt es zwei großartige Möglichkeiten, ein automatisches Signing auf Ihrem System durchzuführen.

Erstens, der günstige, einfache Weg, der alles auf jemand anderen legt. Wenn Sie Ihren Site-Support mit Ihrem Google + -Konto anmelden, sagen Sie, zB Ihr Google + -Konto, haben Sie wahrscheinlich eine optimierte Google + -Schaltfläche, mit der sich der Benutzer anmelden wird, wenn er bereits bei Google angemeldet ist (ich habe das hier getan, um diese Frage zu beantworten, da ich es immer bin.) bei google angemeldet). Wenn Sie möchten, dass der Benutzer automatisch angemeldet wird, wenn er bereits mit einem vertrauenswürdigen und unterstützten Authentifikator angemeldet ist, und das Kontrollkästchen aktiviert ist, müssen die clientseitigen Skripts vor dem Laden den Code hinter der entsprechenden Schaltfläche "Anmelden mit" ausführen Stellen Sie sicher, dass der Server eine eindeutige ID in einer Tabelle für das automatische Signieren speichert, die den Benutzernamen, die Sitzungs-ID und den für den Benutzer verwendeten Authentifikator enthält. Da diese Anmeldemethoden 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 den angemeldeten Benutzer dann normal weiter. Andernfalls ist die Anmeldung fehlgeschlagen, teilen Sie dem Benutzer jedoch nicht mit, sondern fahren Sie fort, da er nicht angemeldet ist. Dadurch soll verhindert werden, dass ein Angreifer, der Cookies gestohlen hat (oder bei einem Versuch, die Berechtigungen zu erhöhen, gefälscht wurde), erfährt, dass der Benutzer sich automatisch bei der Site anmeldet.

Dies ist billig und wird von manchen als schmutzig betrachtet, da versucht wird, Ihr möglicherweise bereits angemeldetes Eigenes bei Orten wie Google und Facebook zu überprüfen, ohne Sie darüber zu informieren. Es sollte jedoch nicht für Benutzer verwendet werden, die nicht aufgefordert haben, Ihre Site automatisch zu signieren. Diese spezielle Methode dient nur zur externen Authentifizierung, wie bei Google+ oder FB.

Da ein externer Authentifikator verwendet wurde, um dem Server hinter den Kulissen mitzuteilen, ob ein Benutzer überprüft wurde oder nicht, kann ein Angreifer nur eine eindeutige ID erhalten, die alleine unbrauchbar ist. Ich werde ausarbeiten:

  • Benutzer 'Joe' besucht die Website zum ersten Mal, die Sitzungs-ID wurde in das Cookie 'Sitzung' gesetzt.
  • Benutzer 'joe' Meldet sich an, eskaliert Privilegien, erhält eine neue Sitzungs-ID und erneuert das Cookie 'Sitzung'.
  • Der Benutzer "Joe" wählt die automatische Signatur mit Google + und erhält eine eindeutige ID, die in das Cookie "KeepMesignedin" gesetzt wird.
  • Der Benutzer "Joe" lässt Google weiterhin angemeldet bleiben, sodass Ihre Website den Benutzer automatisch über Google in Ihrem Backend signieren kann.
  • Der Angreifer versucht systematisch eindeutige IDs für 'keepmesignedin' (dies ist öffentliches Wissen, das an jeden Benutzer ausgegeben wird) und ist an keiner anderen Stelle angemeldet. versucht eine eindeutige ID, die "joe" gegeben wurde.
  • Der Server erhält eine eindeutige ID für "Joe", zieht die Übereinstimmung für ein Google + Konto in der Datenbank ab.
  • Der Server sendet einen Angreifer an die Anmeldeseite, auf der eine AJAX - Anforderung ausgeführt wird, um sich bei Google anzumelden.
  • Der Google-Server empfängt eine Anfrage und verwendet seine API, um anzuzeigen, dass der Angreifer zurzeit nicht angemeldet ist.Google sendet eine Antwort, dass derzeit kein Benutzer über diese Verbindung angemeldet ist.
  • Die Seite des Angreifers erhält eine Antwort. Das Skript leitet automatisch auf die Anmeldeseite mit einem POST - Wert um, der in der URL kodiert ist.
  • Die Anmeldeseite erhält den Wert POST. Sendet das Cookie für 'keepmesignedin' auf einen leeren Wert und ein gültiges Datum bis zum Datum 1-1-1970, um einen automatischen Versuch zu verhindern, sodass der Browser des Angreifers das Cookie einfach löscht.
  • Der Angreifer erhält eine normale Anmeldeseite.
  • Selbst wenn ein Angreifer eine nicht vorhandene ID verwendet, sollte der Versuch bei allen Versuchen fehlschlagen, es sei denn, eine validierte Antwort wird empfangen.

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

=========.

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

DB hat einige Tabellen:

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

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

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

Dadurch wird verhindert, dass jemand systematisch den MAC und den Hostnamen eines Benutzers spoofet, für den er automatische Signaturen kennt. NEVER Der Benutzer muss ein Cookie mit seinem Kennwort, Klartext oder auf andere Weise aufbewahren. Lassen Sie das Token auf jeder Seitennavigation neu generieren, genau wie Sie das Sitzungstoken tun würden. Dies verringert massiv die Wahrscheinlichkeit, dass ein Angreifer ein gültiges Token-Cookie erhält und zur Anmeldung verwendet. Einige Leute versuchen zu sagen, dass ein Angreifer die Cookies des Opfers stehlen und einen Sitzungswiederholungsangriff durchführen kann, um sich anzumelden. Wenn ein Angreifer die Cookies stehlen könnte (was möglich ist), hätte er sicherlich das gesamte Gerät gefährdet, was bedeutet, dass er das Gerät ohnehin nur zum Anmelden verwenden könnte, was den Zweck des vollständigen Diebstahls von Cookies verhindert. Solange Ihre Site über HTTPS läuft (was bei Passwörtern, CC-Nummern oder anderen Anmeldesystemen der Fall sein sollte), haben Sie dem Benutzer so viel Schutz geboten, wie Sie innerhalb eines Browsers können.

Beachten Sie Folgendes: Sitzungsdaten sollten bei Verwendung der automatischen Anmeldung nicht verfallen. Sie können nicht mehr die Möglichkeit haben, die Sitzung falsch fortzusetzen, aber bei einer Überprüfung im System sollten die Sitzungsdaten wieder aufgenommen werden, wenn es sich um persistente Daten handelt, die voraussichtlich zwischen den Sitzungen fortgesetzt werden. Wenn Sie sowohl persistente als auch nicht persistente Sitzungsdaten wünschen, verwenden Sie eine andere Tabelle für persistente Sitzungsdaten mit dem Benutzernamen als PK, und lassen Sie den Server diese wie normale Sitzungsdaten abrufen. Verwenden Sie einfach eine andere Variable.

Wenn auf diese Weise eine Anmeldung erfolgt ist, sollte der Server die Sitzung weiterhin überprüfen. Hier können Sie Erwartungen für gestohlene oder kompromittierte Systeme kodieren. Muster und andere erwartete Ergebnisse von Anmeldungen bei Sitzungsdaten können häufig zu Schlussfolgerungen führen, dass ein System missbraucht wurde oder Cookies erstellt wurden, um Zugriff zu erhalten. Hier kann Ihr ISS - Techniker Regeln festlegen, die eine Kontosperrung oder das automatische Entfernen eines Benutzers aus dem Auto-Signin-System auslösen, sodass Angreifer lange genug vom Angreifer ferngehalten werden, um zu ermitteln, wie der Angreifer erfolgreich war und wie Schneid Sie ab.Als Abschlussnotiz sollten Sie sicherstellen, dass alle Wiederherstellungsversuche, Kennwortänderungen oder Anmeldungsfehler nach dem Schwellenwert dazu führen, dass die automatische Anmeldung deaktiviert wird, bis der Benutzer die Gültigkeit überprüft und bestätigt, dass dies geschehen ist.

Ich entschuldige mich, wenn jemand erwartet hat, dass der Code in meiner Antwort ausgegeben wird. Dies wird hier nicht passieren. Ich werde sagen, dass ich PHP, jQuery und AJAX verwende, um meine Sites zu betreiben, und ich nutze NIEMALS Windows als Server ... überhaupt.

As a closing note, be sure that any recovery attempt, password changes, or login failures past the threshold result in auto-signin being disabled until the user validates properly and acknowledges this has occurred.

I apologize if anyone was expecting code to be given out in my answer, that's not going to happen here. I will say that I use PHP, jQuery, and AJAX to run my sites, and I NEVER use Windows as a server... ever.

4
user253780

Erzeugen Sie einen Hash, vielleicht mit einem Geheimnis, das Sie nur kennen, und speichern Sie es in Ihrer Datenbank, damit es dem Benutzer zugeordnet werden kann. Sollte ganz gut funktionieren.

4

Ich verstehe das Konzept des Speicherns von verschlüsseltem Material in einem Cookie nicht, wenn es sich um die verschlüsselte Version des Cookies handelt, die Sie zum Hacken benötigen. Wenn mir etwas fehlt, bitte kommentieren.

Ich denke darüber nach, diesen Ansatz zu "Remember Me" zu verwenden. Wenn Sie Probleme sehen, kommentieren Sie bitte.

  1. Erstellen Sie eine Tabelle, in der "Remember Me" -Daten gespeichert werden - getrennt von der Benutzertabelle, damit ich mich von mehreren Geräten aus anmelden kann.

  2. Bei erfolgreichem Login (mit Remember me ankreuzen):

    a) Erzeugen Sie eine eindeutige zufällige Zeichenfolge, die als Benutzer-ID auf diesem Computer verwendet werden soll: bigUserID

    b) Erzeugen Sie einen eindeutigen zufälligen String: bigKey

    c) Speichern Sie einen Cookie: bigUserID: bigKey

    d) Fügen Sie in der Tabelle "Remember Me" einen Datensatz hinzu mit: UserID, IP-Adresse, bigUserID, bigKey

  3. Wenn Sie versuchen, auf etwas zuzugreifen, für das ein Login erforderlich ist:

    a) Suchen Sie nach dem Cookie und suchen Sie nach bigUserID & bigKey mit einer übereinstimmenden IP-Adresse

    b) Wenn Sie es finden, melden Sie die Person an, setzen Sie jedoch ein Kennzeichen in der Benutzertabelle "Soft-Login", damit Sie bei gefährlichen Vorgängen eine vollständige Anmeldung anfordern können.

  4. Markieren Sie beim Abmelden alle "Remember Me" -Datensätze für diesen Benutzer als abgelaufen.

Die einzigen Schwachstellen, die ich sehen kann, sind:

  • sie könnten sich mit dem Cookie an jemandes Laptop wenden und dessen IP-Adresse fälschen.
  • sie könnten jedes Mal eine andere IP-Adresse fälschen und das Ganze erraten - aber mit zwei großen Zeichenketten wäre das ... eine ähnliche Berechnung wie oben ... Ich habe keine Ahnung ... große Chancen?
2
Enigma Plus

Meine Lösung ist so. Es ist nicht 100% kugelsicher, aber ich denke, es wird Sie für die meisten Fälle retten.

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

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

$data verschlüsseln, Typ auf HttpOnly setzen und Cookie setzen.

Wenn der Benutzer zu Ihrer Site zurückkehrt, führen Sie folgende Schritte aus:

  1. Cookie-Daten abrufen. Entferne gefährliche Charaktere im Cookie. Explodiere es mit : Zeichen. 
  2. Gültigkeit prüfen. Wenn das Cookie älter als X Tage ist, leiten Sie den Benutzer zur Anmeldeseite weiter.
  3. Wenn der Keks nicht alt ist; Rufen Sie die letzte Kennwortänderungszeit aus der Datenbank ab. Wenn das Kennwort nach der letzten Anmeldung des Benutzers geändert wird, wird der Benutzer zur Anmeldeseite umgeleitet.
  4. Wenn der Pass in letzter Zeit nicht geändert wurde; Den aktuellen Browseragenten des Benutzers abrufen. Prüfen Sie, ob (currentUserAgentHash == cookieUserAgentHash). Wenn die Agenten identisch sind, fahren Sie mit dem nächsten Schritt fort, andernfalls zur Anmeldeseite weiterleiten.
  5. Wenn alle Schritte erfolgreich sind, autorisieren Sie den Benutzernamen.

Wenn sich der Benutzer abmeldet, entfernen Sie dieses Cookie. Erstellen Sie ein neues Cookie, wenn sich der Benutzer erneut anmeldet.

2
trante

Ich las alle Antworten und fand es immer noch schwierig herauszufinden, was ich tun sollte. Wenn ein Bild 1-k-Wörter wert ist, hoffe ich, dass dies anderen hilft, einen sicheren dauerhaften Speicher zu implementieren, der auf Barry Jaspans Improved Persistent Login Cookie Best Practice basiert.

 enter image description here

Wenn Sie Fragen, Rückmeldungen oder Vorschläge haben, werde ich versuchen, das Diagramm zu aktualisieren, damit der Neuling versucht, eine sichere persistente Anmeldung zu implementieren.

2
Josh Woodcock

Das Implementieren einer Funktion "Angemeldet bleiben" bedeutet, dass Sie genau definieren müssen, was dies für den Benutzer bedeutet. Im einfachsten Fall würde ich damit sagen, dass die Sitzung viel länger dauert: 2 Tage (sagen wir) anstatt 2 Stunden. Dazu benötigen Sie Ihren eigenen Sitzungsspeicher, wahrscheinlich in einer Datenbank, um benutzerdefinierte Ablaufzeiten für die Sitzungsdaten festzulegen. Dann müssen Sie sicherstellen, dass Sie ein Cookie setzen, das einige Tage (oder länger) verbleibt und nicht abläuft, wenn der Browser geschlossen wird.

Ich höre Sie fragen "Warum 2 Tage? Warum nicht 2 Wochen?". Dies liegt daran, dass bei Verwendung einer Sitzung in PHP der Ablauf automatisch zurückgeschoben wird. Dies liegt daran, dass das Ablaufdatum einer Sitzung in PHP tatsächlich ein Timeout ist. 

Nachdem ich das gesagt habe, würde ich wahrscheinlich einen härteren Timeout-Wert implementieren, den ich in der Sitzung selbst und nach etwa zwei Wochen abspeichere. Dann füge ich Code hinzu, um das zu sehen und die Sitzung zwangsweise ungültig zu machen. Oder zumindest um sie abzumelden. Dies bedeutet, dass der Benutzer regelmäßig aufgefordert wird, sich anzumelden. Yahoo! macht dies.

0
staticsan