Sinn oder Unsinn des PyLucids JS-MD5-Login...

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Beitragvon sape » Mittwoch 6. Dezember 2006, 13:12

BlackJack hat geschrieben:[...]
Wenn birkenfeld und ich sagen, Dein Verfahren sei zu kompliziert, meinen wir nicht Du sollst auf 1. zurückgreifen.

Wie soll den 2 funktionieren? 0o Du übermittelst den hash von den salt bzw. den salt und dann wird was zurückgeschickt? Das pwd+salt? md5(salt+pwd)?

lg
BlackJack

Beitragvon BlackJack » Mittwoch 6. Dezember 2006, 13:32

Du bekommst vom Server die `challenge`, vom Benutzer das `passwd` und schickst zurück: md5(challenge + md5(passwd)).

Das steht doch da unter 2. eigentlich genau so.

Wobei in `challenge` natürlich noch ein Teil von einem "salt" enthalten sein kann, sollte.
Benutzeravatar
jens
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Mittwoch 6. Dezember 2006, 15:33

So, ich hab mal mit DIA ein Diagramm gebaut:

Ist mein erstes Diagramm mit DIA...


Gut das diese Thread aufgetaucht ist... Beim bauen des Diagramms sind mir noch einige Sachen aufgefallen, die ich bei meiner momentanen Implementierung besser machen kann!

Ein Problem muß aber weiterhin umständlich gelöst werden. Wie kann ich den zurück geschickte MD5 überprüfen???
Also wenn der Client jetzt folgendes zurück schickt: md5(Server-Zufallszahl + Password)
Dann brauche ich auf dem Server das Passwort im Klartext, damit der Server die selbe MD5 bilden kann. Passwörter möchte ich aber nicht im Klartext auf den Server speichern :)

Deswegen ist mein Verfahren etwas anders! Und deswegen benötige ich auch ein crypt-Modul.

Mein Verfahren beruht darauf, das nicht das ganze Passwort mit der Zufallszahl zu einer MD5 zusammen gerechnet wird, sondern nur die Hälfte.
Die andere Hälfte wird dazu gebraucht die zurück geschickte MD5 zu überprüfen.

Gespeichert wird auf dem Server also folgendes:

1. md5(Klartext Passwort) wird aufgeteilt in zwei hälften:
2. eine hälfte wird verschlüsselt mit der anderen hälfte
3. die verschlüsselte Variante wird gespeichert, die andere MD5 gelöscht


Wenn nun ein Client einloggen möchte wird folgendes gemacht:

1. md5(Klartext Passwort) wird aufgeteilt in zwei Hälften:
2. eine Hälfte wird mit der Zufallszahl verknüpft: md5(Server-Zufallszahl + MD5-Hälfte)
3. zweite MD5-Hälfte wird so zum Server geschickt.

Der Server kann nun die MD5 so überprüfen:

1. Mit der einen zugeschickten Hälfte wird die verschlüsselte Hälfte aus der Datenbank entschlüsselt
2. Die entschlüsselten Hälfte wird mit der Zufallszahl verknüpft: md5(Server-Zufallszahl + MD5-Hälfte)
3. Vergleichen der gebildeten MD5 Summe

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

Beitragvon BlackJack » Mittwoch 6. Dezember 2006, 16:17

jens hat geschrieben:Ein Problem muß aber weiterhin umständlich gelöst werden. Wie kann ich den zurück geschickte MD5 überprüfen???
Also wenn der Client jetzt folgendes zurück schickt: md5(Server-Zufallszahl + Password)
Dann brauche ich auf dem Server das Passwort im Klartext, damit der Server die selbe MD5 bilden kann. Passwörter möchte ich aber nicht im Klartext auf den Server speichern :)


Okay, jetzt zum dritten mal:

Der Client schickt md5(challenge + md5(passwd))

Und der Server kennt die von ihm generierte `challenge` und hat ``md5(passwd)`` (mit einem "salt" erstellt) in der Datenbank stehen.

Also vielleicht das mit dem "salt" noch ein wenig expliziter:

Login:

Vorbedingung: Server hat in DB `salt` und `hash(salt + password)`

1. Server schickt `challenge` und `salt` an Client
2. Client antwortet mit `hash(challenge + hash(salt + password))`
3. Server vergleicht mit `hash(challenge + hash(salt + password))`

Der farbige Teil steht schon fertig berechnet in der DB, ist also nicht das Klartextpasswort. Das Verfahren ist ziemlich verbreitet würde ich mal behaupten.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Beitragvon sape » Mittwoch 6. Dezember 2006, 16:36

BlackJack hat geschrieben:[...]Das Verfahren ist ziemlich verbreitet würde ich mal behaupten.

Ja außer das ``md5(salt + password)`` nicht schon in der DB ist und stattdessen (vbulletin) den ``salt`` einmal in der DB hat und ``md5(pwd)``. Die Verifizierung sieht so aus: ``$md5pwd = md5(md5($password).$userinfo->salt);``

lg
Benutzeravatar
jens
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Mittwoch 6. Dezember 2006, 16:44

Aha! Nun wird es mir klar, was du meinst, nach dem ich es mal in Python code gegossen hab:

Code: Alles auswählen

import time
from md5 import new as md5_new


def md5(txt):
    return md5_new(txt).hexdigest()



salt = "s_%s" % time.time()
print "salt ist:", salt
print



print "In der DB ist gespeichert 'md5(salt + password)':"
db_md5pass = md5(salt + "Password")
print "db_md5pass:", db_md5pass
print



print "Server sendet challenge + salt zum client:"

challenge = "c_%s" % time.time()
print "challenge:", challenge
print



print "Client bildet 'md5(challenge + md5(salt + password))':"
client_response = md5(challenge + md5(salt + "Password"))
print "client_response:", client_response
print


print "Der Server vergleicht 'md5(challenge + db_md5pass)' mit 'db_md5pass':"
md5check = md5(challenge + db_md5pass)
print "md5check:", md5check

print

print "Login korrekt? ", md5check == client_response


So wie ich das sehe, hat das ganze allerdings einen Nachteil. In der DB werden zwar keine Klartext Passwörter gespeichert, aber zumindest die vollständigen Informationen mit dem man sich einloggen kann.

Will sagen, wenn jemand an diese Daten herran kommt, kann er sich mit den Informationen einloggen.

Durch den salt funktioniert das aber auch nur dort und nicht woanders. Aber immerhin.

Bei meiner aufwändigeren Variante, kann man mit den DB Daten IMHO nichts anfangen, weil es nur die Hälfte der nötigen Informationen sind.

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Beitragvon sape » Mittwoch 6. Dezember 2006, 16:50

jens hat geschrieben:[...]
Bei meiner aufwändigeren Variante, kann man mit den DB Daten IMHO nichts anfangen, weil es nur die Hälfte der nötigen Informationen sind.
So habe ich das auch verstanden und deshalb finde ich deine Idee auch wirklich super :) Hat mMn bisher keine Forensoftware oder CMS so implementiert! :)

lg
BlackJack

Beitragvon BlackJack » Mittwoch 6. Dezember 2006, 17:56

jens hat geschrieben:Bei meiner aufwändigeren Variante, kann man mit den DB Daten IMHO nichts anfangen, weil es nur die Hälfte der nötigen Informationen sind.


Solange Du das nicht beweisen kannst, ist diese Aussage "wertlos".

Irgendwie finde ich auch die Diagramme, frühere Beschreibungen und den vorhandenen Quelltext nicht wirklich zusammenpassend. Da ist mal von geraden/ungerade Buchstaben die Rede, mal von den ersten vier und dem Rest und in `userhandling.py` steht im Kommentar, dass das JS-MD-5 noch nicht implementiert ist, aber irgend etwas wird da ja gemacht.

In PyLucid mag ich mich jetzt nicht einarbeiten. Kannst Du das Verfahren mal isoliert als Skript zeigen. Vielleicht mit Anlegen eines Benutzers und Datenspeicherung in einem gepickleten Dictionary oder so.
Benutzeravatar
jens
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Mittwoch 6. Dezember 2006, 18:36

BlackJack hat geschrieben:Kannst Du das Verfahren mal isoliert als Skript zeigen. Vielleicht mit Anlegen eines Benutzers und Datenspeicherung in einem gepickleten Dictionary oder so.

Hervorragende Idee... Werde ich morgen mal machen...

btw. vielen dank für deine Geduld mir das normale Verfahren aufzuzeigen ;)

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Beitragvon mitsuhiko » Mittwoch 6. Dezember 2006, 19:36

Ich werfe mal HMAC in den Raum.
TUFKAB – the user formerly known as blackbird
Benutzeravatar
jens
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Donnerstag 7. Dezember 2006, 07:58

So, ich hab mal mein Verfahren versucht möglichst einfach in Python Code zu gießen. Verständlicher wird es, wenn man sich die Ausgaben dazu anschaut (s.u.):

Code: Alles auswählen

from md5 import new as md5_new


def md5(txt):
    return md5_new(txt).hexdigest()

def encrypt(txt, key): # Pseudo encrypt
    return "encrypted %s with %s" % (txt, key)

def decrypt(txt, key): # Pseudo decrypt
    txt, _, key2 = txt.split(" ", 3)[1:]
    assert key == key2
    return txt



print "\n\n------------ 1. Ein neuer User in der DB anlegen------------"
print "\n 1.1. Server sendet salt zum Client:",
salt = "s_123"
print "'%s'" % salt

print "\n 1.2. Eingabe des Passwortes auf dem Client:",
password = "Passwort"
print "'%s'" % password

print "\n 1.3. md5(password + salt):",
md5sum = md5(password + salt)
print "'%s'" % md5sum

print "\n 1.4. Übermittlung der MD5 Summe zum Server."



print "\n\n------------ 2. speichern des Users auf dem Server------------"

print "\n 2.1. Server trennt die MD5 in:",
md5_a = md5sum[:16]
md5_b = md5sum[16:]
print "md5_a: '%s' md5_b: '%s'" % (md5_a, md5_b)

print "\n 2.2. encrypt(md5_a, key=md5_b):",
md5checksum = encrypt(md5_a, key=md5_b)
print "'%s'" % md5checksum

print "\n 2.3. Speichern nur der verschlüsselten Checksum + salt\n"


print "_______________________________________________________________________"



print "\n\n------------ 3. Login eines Users------------"

print "\n 3.1. Server sendet salt '%s' + challenge zum client:" % salt,
challenge = "c_123"
print "'%s'" % challenge

print "\n 3.2. Eingabe des Passwortes auf dem Client:",
password = "Passwort"
print "'%s'" % password

print "\n 3.3. md5(password + salt):",
md5sum = md5(password + salt)
print "'%s'" % md5sum

print "\n 3.4. trennen der MD5 in:",
md5_a = md5sum[:16]
md5_b = md5sum[16:]
print "md5_a: '%s' md5_b: '%s'" % (md5_a, md5_b)

print "\n 3.5. md5_a2 = md5(md5_a + challenge):",
md5_a2 = md5(md5_a + challenge)
print "'%s'" % md5_a2

print "\n 3.6. Übermittlung von md5_a2 und md5_b."



print "\n\n------------ 4. check auf dem Server------------"

print "\n 4.1. aus der DB md5checksum: '%s'" % md5checksum

print "\n 4.2. decrypt(md5checksum, key=md5_b):",
md5checksum = decrypt(md5checksum, key=md5_b)
print "'%s'" % md5checksum

print "\n 4.3. md5(md5checksum + challenge):",
md5check = md5(md5checksum + challenge)
print "'%s'" % md5check

print "\n 4.4. Vergleich: %s == %s" % (md5check, md5_a2)


Die Asgaben:
------------ 1. Ein neuer User in der DB anlegen------------

1.1. Server sendet salt zum Client: 's_123'

1.2. Eingabe des Passwortes auf dem Client: 'Passwort'

1.3. md5(password + salt): '7522381eebd7c49a2522d09797ba20d2'

1.4. Übermittlung der MD5 Summe zum Server.


------------ 2. speichern des Users auf dem Server------------

2.1. Server trennt die MD5 in: md5_a: '7522381eebd7c49a' md5_b: '2522d09797ba20d2'

2.2. encrypt(md5_a, key=md5_b): 'encrypted 7522381eebd7c49a with 2522d09797ba20d2'

2.3. Speichern nur der verschlüsselten Checksum + salt

_______________________________________________________________________


------------ 3. Login eines Users------------

3.1. Server sendet salt 's_123' + challenge zum client: 'c_123'

3.2. Eingabe des Passwortes auf dem Client: 'Passwort'

3.3. md5(password + salt): '7522381eebd7c49a2522d09797ba20d2'

3.4. trennen der MD5 in: md5_a: '7522381eebd7c49a' md5_b: '2522d09797ba20d2'

3.5. md5_a2 = md5(md5_a + challenge): 'd3510bc87cf09bd7df69d180805e76fa'

3.6. Übermittlung von md5_a2 und md5_b.


------------ 4. check auf dem Server------------

4.1. aus der DB md5checksum: 'encrypted 7522381eebd7c49a with 2522d09797ba20d2'

4.2. decrypt(md5checksum, key=md5_b): '7522381eebd7c49a'

4.3. md5(md5checksum + challenge): 'd3510bc87cf09bd7df69d180805e76fa'

4.4. Vergleich: d3510bc87cf09bd7df69d180805e76fa == d3510bc87cf09bd7df69d180805e76fa


Zu beachten ist, die verschlüsselte MD5 Summe wird bei Punkt 2.3 und 4.1. anhand des Usernamen in der DB gespeichert bzw. gelesen.


Wie man sieht ist in der DB nur die hälfte der MD5 Summe gespeichert (Punkt 2.3) und das auch noch verschlüsselt mit der anderen hälft.

Wenn jemand also an die DB Daten kommt, kann er damit nichts anfangen. Das wäre anders, wenn ich auf die Verschlüsselung verzichte.

EDIT: Noch ein Hinweis. In genau der Form ist es z.Z. auch in PyLucid nicht implementiert. Zwar sehr ähnlich, aber nicht ganz. Mir ist ja erst durch diesen Thread aufgefallen, das man das Verfahren noch etwas optimieren kann ;)
Ich werde das aber erst so wie hier implementieren, wenn ein "Passwort reset" möglich ist :)

Im übrigen steht im Wiki unter [wiki]JensDiemer/CryptoIdee[/wiki] noch eine andere Idee, einses noch sicheren Verfahren, was auf http://de.wikipedia.org/wiki/Diffie-Hellman beruht. Da es Diffie-Hellman nicht als fertiges JavaScript gibt hat sich das aber erledigt. Außerdem ist das Verfahren bei einem man-in-the-middle auch nicht sicher. Zudem kommt noch das es wohl eindeutig Overkill ist, in anbedracht von Session-Hijacking :)

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

Beitragvon BlackJack » Donnerstag 7. Dezember 2006, 17:20

jens hat geschrieben:Wie man sieht ist in der DB nur die hälfte der MD5 Summe gespeichert (Punkt 2.3) und das auch noch verschlüsselt mit der anderen hälft.

Wenn jemand also an die DB Daten kommt, kann er damit nichts anfangen. Das wäre anders, wenn ich auf die Verschlüsselung verzichte.


Kann ich mir, wenn ich die DB-Daten habe, nicht einfach irgendeinen Schlüssel ausdenken? Wenn ich die DB-Daten damit entschlüssele, dann habe ich zwei Hälften die ich für ein Login verwenden kann und die der Prüfung standhalten.
Benutzeravatar
jens
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Donnerstag 7. Dezember 2006, 18:00

BlackJack hat geschrieben:Kann ich mir, wenn ich die DB-Daten habe, nicht einfach irgendeinen Schlüssel ausdenken? Wenn ich die DB-Daten damit entschlüssele, dann habe ich zwei Hälften die ich für ein Login verwenden kann und die der Prüfung standhalten.


Hm. Interessante Frage... Aber ich glaube: "nein".

Es kommt auf das Verschlüsselungsverfahren an. In frage kommen ja DES bzw. Rijndael, siehe: http://www.python-forum.de/topic-8075.html

Wie soll man also die Daten so einfach entschlüsseln, es dürfte nur der eine richtige Schlüssel passen. Also müsste man ein BruteForce versuchen um den richtigen Key zu finden.

Oder hab ich da einen Denkfehler?

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

Beitragvon BlackJack » Donnerstag 7. Dezember 2006, 19:36

jens hat geschrieben:Wie soll man also die Daten so einfach entschlüsseln, es dürfte nur der eine richtige Schlüssel passen. Also müsste man ein BruteForce versuchen um den richtigen Key zu finden.


Es geht ja gar nicht darum das "Original" zu herauszufinden. Ich kann `decrypt()` doch einfach mit einem zufälligen Schlüssel `b` auf den verschlüsselten Eintrag loslassen und bekomme damit etwas das ich mal `a` nenne. Mit diesen beiden Werten müsste jetzt eine Authentifizierung möglich sein. Client bekommt `challenge` und sendet `hash(a + challenge)` und `b` an Server. Der entschlüsselt den Eintrag mit `b`, bekommt `a` heraus und prüft ob `hash(a + challenge)` gleich dem gesendeten ist. Ist es -> Bingo. :-)
Benutzeravatar
jens
Moderator
Beiträge: 8458
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Beitragvon jens » Freitag 8. Dezember 2006, 07:40

BlackJack hat geschrieben:Ich kann `decrypt()` doch einfach mit einem zufälligen Schlüssel `b` auf den verschlüsselten Eintrag loslassen und bekomme damit etwas das ich mal `a` nenne.

Nein. decrypt() sollte eine Exception liefern, wenn der Key nicht der richtige ist.
z.Z. bei meiner XOR Variante, habe ich deswegen ja auch den hash() und jetzt die MD5() in den encrypteten Daten drin.
d.h. es wird zwar die XOR Operartion immer gemacht, egal welcher Key, aber danach muß die checksumme auch stimmen und das tut sie nicht, wenn der Key ein falscher war.

Mit DES bzw. Rijndael statt einem XOR, dürfte IMHO die Entschlüsselung nie mit einem falschen Key gehen. Aber das muß ich noch testen, ob ich da auch mit checksummen arbeiten muß.

CMS in Python: http://www.pylucid.org
GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder