PHP Sicheres Login (Manipulation von $_SESSION)

-=Renegade=- schrieb:
Lesen hilft ;) Dieses Beispiel war unter der Annahme, das ein Verfahren komplett geknackt wurde und die Berechnung jedes Hashes innerhalb kurzer Zeit möglich ist.

Du scheinst Hashfunktionen nicht begriffen zu haben.
Du kannst niemals zu einem Hash einen festen Wert berechnen, da es zu einem Hash unendlich viele Werte gibt. Da gibt es nichts komplett zu knacken.

Nimm mal deinen Lieblingsfilm auf DVD und wandle deren Bytestrom mittels md5 in einen Hash um. Wenn du ein Verfahren entwickelst, mit welchem du aus dem Hash wieder deinen Bytestrom der 4,7GB großen DVD extrahieren kannst, dann meldest du dich wieder.
 
-=Renegade=- schrieb:
Also ich glaube wir reden da wirklich aneinander vorbei. Irgendwo musst du ja dein Password in der Datenbank abspeichern. Und wenn du zum hashen das Klartext Passwort verwendest, dann musst du es ergo auch Klartext in der Datenbank abgespeichert haben. Und Klartext Passwörter in der Datenbank sind ein weitaus größeres Risiko als das Verwenden mehrerer Algos oder eines Salts.

Jop das macht ihr! Wie das ganze gemeint war:
Bei der Registrierung des Users kommt das Passwort ja ungehasht aus einem Formular. Dieses wollte er entweder mit einem "streng geheimen" Salt hashen. d.h. er denkt sich ein wort aus und nimmt das als Salt für alle Userpasswörter.
Oder er nimmt eben die aktuelle Zeit als Salt, wobei er die natürlich auch irgendwie in der Datenbank speichern muss.

Man darf ihn also nicht so verstehen dass er bei jedem Login oder so einen neuen Hash berechnen will. Denn dann bräuchte er das Passwort ja wirklich im Klartext!
(Würde natürlich auch gehen:
Erst lässt man ihn sich einlogen, und wenn der Hash des Passwortes stimmt, dann nimmt man seine Formulareingabe und macht wieder nen Hash-Wert draus, den man dann speichert.)

@carom: Na dann wird er reich! Dann steig ich wieder auf Disketten um. Da kann man alle mit 1TB-Platten auslachen, weil auf die Diskette auch 5000 Filme gehen^^
 
carom schrieb:
Du scheinst Hashfunktionen nicht begriffen zu haben.
Du kannst niemals zu einem Hash einen festen Wert berechnen, da es zu einem Hash unendlich viele Werte gibt. Da gibt es nichts komplett zu knacken.

Nimm mal deinen Lieblingsfilm auf DVD und wandle deren Bytestrom mittels md5 in einen Hash um. Wenn du ein Verfahren entwickelst, mit welchem du aus dem Hash wieder deinen Bytestrom der 4,7GB großen DVD extrahieren kannst, dann meldest du dich wieder.

Es geht nicht darum, alle möglichen Kollisionen zu finden, wir reden hier lediglich von Passwörtern die idR < 10 Zeichen sind und daher eindeutig bestimmt werden können. Und sei es einfach nur dadurch, dass es irgendwann mal eine Rainbow Table mit allen Werten gibt ;)

Aber zugegebenermaßen habe ich mich damit vllt sehr schlecht ausgedrückt. Was ich meinte war damit rein, das ich alle Passwörter eines Hash Verfahrens in kurzer Zeit berechnen kann. Nicht das 4,7GB Resultat eines Filmes :)

Aber bevor wir uns zu sehr auf dieses Problem eigentlich versteifen, sollte man sich viel mehr die Frage stellen, in welchem Szenario man überhaupt an einen Hash kommen kann. Es ist ja nicht so, das man immer den Hash sieht.
XSS -> lässt sich dadurch verhindern, dass man nicht den Hash sondern eine Session ID im Cookie abspeichert.
SQL Injection -> Wenn ich die Passwörter Klartext in der DB abgespeichert habe, sind mir die Hashes herzlich egal :)


Hoffe die meisten Missverständnisse sind inzwischen geklärt :)


so long
 
Ja, die meißten, jetzt kömmer ja richtig diskutieren... ;)

XSS mit Session ID, wenn man dann die SID hat, hat man ja auch Zugriff auf alle Funktionen (außer PW ändern, sofern geschützt).
SQL-Injection: Sollte man durch sauberes Programmieren verhindern können. (BTW: Wer ein SQL-Angriff schafft, der kann viel mehr machen als nur Passwörter stehlen...)

Meine Philosophie ist es, keine sensiblen Daten im Klartext, nur Prepared Statements und wichtige Sachen per Passwortabfrage (doppelt) abgesichert. (Es sollte halt nicht so einfach sein über eine gestohlene Session ein Account zu kapern (bspw. bei Steam Emailadresse))
 
Blitzmerker schrieb:
Ja, die meißten, jetzt kömmer ja richtig diskutieren... ;)
Ja so macht das dann ja auch richtig Sinn, haben ja generell was nützliches, solche Diskussionen.

XSS mit Session ID, wenn man dann die SID hat, hat man ja auch Zugriff auf alle Funktionen (außer PW ändern, sofern geschützt).
Ja, aber so wie du es erklärt hast, bleibt es sich ja gleich. Denn ob er dein Hash Cookie klaut oder die Session ID, er erscheint für dein System dadurch als dieser User.

SQL-Injection: Sollte man durch sauberes Programmieren verhindern können. (BTW: Wer ein SQL-Angriff schafft, der kann viel mehr machen als nur Passwörter stehlen...)
Das ist richtig. Trotzdem hab ich es jetzt noch nicht herauslesen können, ob du deine Passwörter im Klartext speicherst oder nicht.


so long
Renegade
 
Was habt ihr eigentlich alle mit euren Hashes? Passwort gehasht mit einem vom Admin beliebig erstelltem Salt in der Datenbank abspeichern und fertig is. Statt dem Speichern der User-ID, Login-ID und/oder gehashtem Passwort beim User im Cookie einfach Fake-IDs nutzen. Fertig ist der Kuchen. Es ist völlig unnötig echte Daten beim User im Cookie zu speichern, und unsicher dazu. Je anonymer die beim User gespeicherten Daten, desto besser!



@carom

Speichern tu ich beim User folgendes:

- fixe UniqueID
- dynamische SessionID

Die beide IDs sind völlig frei erfundene, zwischen 32 und 1024 Zeichen lange Zeichenkette - wobei 32 eigentlich ausreichen, ich persönlich nehm 128. Ich nutz also keinen wirklich wichtigen, real existierenden Datensatz, sodass des praktisch unmöglich sein sollte einen ganz bestimmten Account anzugreifen. Nutzen tu ich diesen Code:

PHP:
function uniqueKey($length = "32")
{
    $uniquekey ="";
    $code = array_merge(range('0', '9'), range('a', 'z'), range('A', 'Z'));
    mt_srand((double)microtime()*1000000);
    if($length > 1024): $length = "1024";endif;
    for ($i = 1; $i <= $length; $i++):
        $swap = mt_rand(0,count($code)-1);
        $uniquekey .= $code[$swap];
    endfor;
    return $uniquekey;
}

Wie gesagt, die dynamische Session ID nutzt dieselbe Funktion, jedoch mit dem Unterschied, dass diese ID bei jedem Seitenaufruf (oder alle x Sekunden) neu generiert wird. Sollte also jemand beispielsweise eine XSS-Attacke erfolgreich durchziehen und an die Cookies des Users kommen, dann weiß er immer noch nicht, welchen Account er hat. Das musser ausprobieren, und da genau einen Account zu treffen, der ein Adminaccount ist - wird schwer. Und wenn der Admin oder ein anderer User gerade online ist und surft (wovon in der Regel auszugehen ist), ist die Session, die der Angreifer gerade besitzt, potentiell eh schon wieder ungültig, da der echte User schon eine neue SessionID bekommen hat.

Gespeichert wird beides als Paar in der Datenbank, zusammen mit den Userdaten, die während der Session ständig gebraucht werden. Ein Sicherheitscheck prüft, ob die Keys der Länge n entsprechen und keine Sonderzeichen enthalten. Sind die Cookies OK, wird eine Anfrage an die DB geschickt, ob das Schlüsselpaar existiert. Wenn die Übereinstimmung nur partiell ist, wird der User ausgeloggt. Stimmen beide Keys, greift noch der Browsercheck (letzter Besuch des Users wird mit aktuellem Besuch verglichen) und, falls der Admin dies vorgegeben hat oder sich der User es selbst ausgesucht hat, noch der IP-Check.

Glaub man könnte da noch ein paar weitere Checks einbauen, vll mach ich des auch.


Fazit jedenfalls: Solang sich die Browserkennung nicht ändert und nicht die wesentlich sichere Session-IP-Bindung ausgewählt wurde, ist ein dauerhafter Login problemlos möglich - völlig ohne die hauseigene PHP-Sessionfunktion oder Speicherung von realen Daten beim User.

//Nachtrag:
Ich habe über diese Methode auch noch nie einen doppelten Key generiert, was bei MD5 ja beispielsweise durchaus passieren kann.
 
Zuletzt bearbeitet:
hm TchiboManns Taktik klingt ganz gut.
Allerdings, wenn der User nicht gerade auf der Seite surft, hilft das auch nicht mehr als ein logedin-Flag...
(Soll mein zweites kleines Browsergame werden, und ich denke mal in Browsergames sind die User nicht immer aktiv)

Ich mach das jetzt erstmal mit nem $_SESSION['logedin']=1
Und später mal, sollte ich das jemals veröffentlichen (das andere Game "schimmelt" auch Passwortgeschützt im Netz), bau ich es noch ein bisschen um.

Vielen Dank an alle Poster, die Diskussion war sehr interessant und hilfreich.

mfg
Michael

edit:
Oh hab grad nochwas gefunden:
PHP:
<?php
    // [...]
    if (!$stmt->fetch()) {
        return 'Das eingegebene Password ist ungültig.';
    }
    $stmt->close();
    setcookie('UserID', $UserID, strtotime("+1 month"));
    setcookie('Password', $Hash, strtotime("+1 month"));
    $_COOKIE['UserID'] = $UserID; // fake-cookie setzen
    $_COOKIE['Password'] = $Hash; // fake-cookie setzen
    // [...]
?>
Wie läuft das hier mit den Fake-Cookies? Überschreiben die nicht die vorher gesetzten Cookies??
 
Zuletzt bearbeitet:
Die Funktion macht aber nicht zwingend Sinn, man kann ja auch einfach mit $UserID und $Hash weiterarbeiten. Cookies sollten eh niemals ohne Check genutzt werden, von daher kann man nach dem Check des Cookies die Inhalte an die entsprechenden Variablen weitergeben (und dann, wenn nötig, in einzelnen Funktionen globalisieren). Aber btw, da ist wieder das Beispiel UserID und Passwort - speichert das nicht beim User in den Cookies.
 
Zurück
Oben