JavaScript Login Script, Sicherheit verbessern

Xot

-
ID: 413078
L
26 November 2006
451
36
Hallo zusammen,

ich bastle für meine Seite gerade ein Login Script und mir kam da eine Idee:
Und zwar werden die POST Daten im Klartext übermittelt solange kein HTTPS zur Verfügung steht.

Meine Idee war jetzt das Passwort schon mit JS verschlüssle und dann nur noch den Hash übermittle. Als FallBack Lösung würde ich dann trotzdem das Passwort unverschlüsselt verschicken, wenn kein JS aktiv ist.

Macht dieser zusätzliche Aufwand Sinn?
 
Die Frage ist ja, was hast du an zusätzlicher Sicherheit davon?

Ob du jetzt "passwort" an den Server schickst, und der dir den Zugriff erlaubt, oder du schickst "7a62b45e0972fad91" und der Server erlaubt dir den Zugriff, ist ja erstmal egal.

Wer den übertragenen Schlüssel mitliest, kann genau diesen wieder zum Server schicken und sich damit anmelden.

Der Vorteil wäre höchstens, dass du den User schützt, der dasselbe Passwort an verschiedenen Stellen verwendet, da an anderen Stellen eben nicht "7a62b45e0972fad91" sondern der Klartext des Passworts zum Login erwartet wird.

Eine zusätzliche Sicherheit für deine eigene Seite würdest du aber höchstens erreichen, wenn die clientseitige Verschlüsselung in irgendeiner Form dynamisch erfolgt. Zum Beispiel indem du die Session-ID und/oder Quell-IP oder auch einen beim Aufruf der Login-Seite mitgeschickten SALT mit reinverschlüsselst. Wenn dann jemand den Schlüssel mitliest, kann er sich damit erstmal nicht unbedingt anmelden, weil dann ja der Schlüssel nicht mit den sonstigen Client-Daten, die mit der Abfrage übergeben werden, zusammenpasst.
 
Meine Idee war jetzt das Passwort schon mit JS verschlüssle und dann nur noch den Hash übermittle.
Was heißt da "nur noch"? Ich gehe mal davon aus, dass du heute schon nur (gesalzenene) Hashs in der DB speicherst. Möchte sich jemand einloggen und übermittelt er das PW, wird auf dem Server im gleichen Verfahren der Hash ermittelt und mit dem gespeicherten Hash verglichen.

Wenn du nun den Hash schon beim Client erstellen lässt, würdest du ja dein komplettes Versalzungsverfahren preisgeben, was ein deutlicher Rückschritt in der Sicherheit wäre.

Daher muss der Server weiterhin selbst die Hashs erstellen und prüfen. Deine eigentliche Frage ist ja aber, wie man das eingebene PW möglichst sicher zum Server bekommt. Und hierfür könntest du natürlich ein JS-Verschlüsselungsverfahren nutzen (wobei ich da keine Tipps habe), das sollte aber absolut nichts mit der bisherigen Klartextmethode zu tun haben und es reicht dann natürlich nicht nur einen Hash zu übertragen, sondern eben die entstehende verschlüsselte Zeichenkette.
 
Der Vorteil wäre höchstens, dass du den User schützt, der dasselbe Passwort an verschiedenen Stellen verwendet, da an anderen Stellen eben nicht "7a62b45e0972fad91" sondern der Klartext des Passworts zum Login erwartet wird.

Genau dies will ich erreichen. Somit hat der Angreifer "nur" Zutritt zu meiner Seite und nicht direkt das Passwort des Users.

Wenn du nun den Hash schon beim Client erstellen lässt, würdest du ja dein komplettes Versalzungsverfahren preisgeben, was ein deutlicher Rückschritt in der Sicherheit wäre.

Das ist ein guter Punkt welchen ich noch gar nicht bedacht habe. Also bist du der Meinung, dass das Passwort lieber in Klartext übertragen werden sollte um das Salt-Verfahren zu schützen?
 
Nein, ich habe nur gesagt, dass das Verfahren für die PW-Hashs komplett auf dem Server bleiben soll.

Zusätzlich dazu kannst du ein versalzenes Verschlüsselungsverfahren für den Client bereit stellen. Dieser verschlüsselt (bestenfalls kommt bei jedem Aufruf trotz gleicher PW-Eingabe ein anderer Crypttext raus), sendet diesen an den Server, dieser entschlüsselt und macht ab da alles genau so als ob ein unverschlüsseltes Passwort eingegangen wäre (hashen, vergleichen, usw).
 
Dieser verschlüsselt (bestenfalls kommt bei jedem Aufruf trotz gleicher PW-Eingabe ein anderer Crypttext raus), sendet diesen an den Server, dieser entschlüsselt und macht ab da alles genau so als ob ein unverschlüsseltes Passwort eingegangen wäre (hashen, vergleichen, usw).

Da das ganze aber dann Clientseitig implementiert wird, kann der Angreifer das ganze mit entsprechenden Kenntnissen entschlüsseln. In wie weit sich der Aufwand dann lohnt ist fragwürdig. Daher hatte ich Anfangs an den Hash gedacht (Wobei ich den Salt völlig vernachlässigt habe :ugly:).

Oder meinst du folgendes Szenario:
SHA1 vom PW Clientseitig -> Serverseitig dem Hash noch einen Salt hinzufügen und wieder SHA1 -> Das mit der DB abgleichen

Nur irgendwie habe ich in Erinnerung, dass man ein Passwort nicht doppelt hashen soll... Finde dazu aber gerade keine interessanten Links.
 
Ich gehe mal davon aus, dass du etwas in der Art bereits implementiert hast:
1. Eine Funktion, die aus einem Passwort ein Hash macht:
PHP:
class User{
// ...
function MakeHash($pw, $salt2 = NULL){  
  $salt1 = 'ABC';
  $salt3 = '123';
  $hash = md5($salt1.$pw.$salt2.$salt3);
  return $hash;
}
// ...
}

Und momentan wird beim Login eben so geprüft:
PHP:
$pw    = $_POST['passwort'];
$user  = $_POST['benutzername'];
$hash  = User::MakeHash($pw);
$uhash = User::GetUserhash($user); // Fragt DB
if ($hash === $uhash){
  // PW korrekt
} else {
  // PW nicht korrekt
}
Du könntest die obere Zeile 3 auch erweitern mit
PHP:
$salt  = User::GetUsersalt($user); // Fragt DB, könnte z.B. eine eMail sein.
$hash  = User::MakeHash($pw, $salt);

Wie oft ein PW gesalzen wird ist eigentlich völlig egal. Wichtig ist nur, dass das Verfahren immer gleich ist. Sollte es mal gewechselt werden (und dazu zählt auch eine Änderung der Salts), müsste noch erkennbar sein, auf welchem Verfahren welcher Hash basiert, um so nach und nach alte Hashs durch neue zu ersetzen. Aber das ist ein ganz anderes Thema.

Wenn nun also JS das Passwort verschlüsselt, sieht das für den Server so aus:
PHP:
$crypt = $_POST['crypt']; // verschlüsselt
$user  = $_POST['benutzername'];
// ***

$pw = Crypt::decode($crypt); // entschlüsselt

// Ab hier alles wie gehabt..

$hash  = User::MakeHash($pw);
$uhash = User::GetUserhash($user); // Fragt DB
if ($hash === $uhash){
  // PW korrekt
} else {
  // PW nicht korrekt
}

Und jetzt kommt es eben drauf an, wie du deine Crypt-Klasse gestaltest. Du schickst bei der Formularerstellung beispielsweise einen öffentlichen Schlüssel mit, JS verschlüsselt das dann, schickt das Ergebnis im POST und ein Angreifer kann auch unter Kenntnis des öffentlichen Schlüssels nicht ohne weiteres das übertragene Passwort lesen. Der zugehörige private Schlüssel bleibt auf dem Server. Du kannst auch wahlweise mehrere Schlüsselpaare verwenden, dann muss im POST einfach nur noch ein Feld mit einer Schlüssel-ID rein.
 
öffentlicher und privater Schlüssel, wie von joschilein beschrieben, ist die einzige sichere Variante, und oh Wunder, so funktioniert SSL.
Ein Problem gibt es dabei aber: Es gibt keine sicheren RSA-Implementierung in JavaScript die zu OpenSSL kompatibel ist. Man findet nur 1:1 Implementierungen des Algorithmus wie von Rivest, Shamir und dem dritten anno sonstwas beschrieben. Aber der Algorithmus in seiner Reinform ist noch nicht sicher, dazu benötigt es noch PKCS#1. Und das wird dann von 90% der JS-Implementierungen nicht implementiert oder es ist inkompatibel zu anderen Implementierungen.
Und dann kommt noch hinzu, dass heutzutage noch nicht alle JavaScript-Implementierungen von Browsern wirklich schnell sind (IE), bei dem wirklich berechnungskomplexen RSA also wirklich ein Problem, es seiden man verwendet lächerliche 128Bit bei der Verschlüsselung :biggrin:


Was spricht denn so gegen SSL? Wer Sicherheit möchte wird doch wohl die 15€/Jahr für ein SSL-Zertifikat ausgeben können
 
Was spricht denn so gegen SSL? Wer Sicherheit möchte wird doch wohl die 15€/Jahr für ein SSL-Zertifikat ausgeben können

Da das ganze Script nicht für mich gedacht ist, kann ich diese Entscheidung leider nicht treffen :(

Würde denn etwas gegen diese Lösung sprechen?
  1. SHA Clientseitig generieren
  2. Diesem SHA dann den Salt hinzufügen und wieder hashen
  3. Dies dann mit der DB abgleichen

Somit würde das PW wenigstens nicht im Klartext übertragen.
 
  1. SHA Clientseitig generieren
  2. Diesem SHA dann den Salt hinzufügen und wieder hashen
  3. Dies dann mit der DB abgleichen
Wenn wir uns richtig verstehen, sind die Punkte 2 und 3 meine oben beschrieben standardmäßigen Verfahren zum "PW-Hash in DB speichern".

Aber was bringt dir Punkt 1? Gegen was möchtest du dich bzw. den Besucher schützen? Wer den Datenverkehr zwischen Client und Server abfängt, der erfährt als Quasi-Client auch das clientseitige Verfahren und kann so mit relativ geringem Aufwand auch aus dem als PW-Ersatz übermittelten Hash das ursprünglich eingegebene Passwort des Angegriffenen ermitteln. Ja, es ist mehr Aufwand als 0, aber eine einzige Rainbow-Table zu erstellen ist heutzutage auch nicht mehr so wahnsinnig aufwändig. Wer das eine kann (Daten abfangen), kann i.d.R. auch das andere (Rainbow-Table).
 
Da das ganze Script nicht für mich gedacht ist, kann ich diese Entscheidung leider nicht treffen :(

dann sagst du einfach es geht nicht, Punkt. SSL wurde genau dafür erschaffen und da haben sich sehr kluge Leute viele Gedanken darüber gemacht und in mehreren Revisionen auch wieder neue Angriffe unbrauchbar gemacht.

Die einzige halbwegs sinnvolle Möglichkeit ist eben der private und public Key, scheitert aber an den JS-Librarys und der Rechenperformance in JavaScript. Diese SHA-Lösung ist genauso einfach zu knacken. Dann wird das Pw eben nicht im Originaltext übertragen sondern durch einen Hash, mache ich eben eine Bruteforce-Attacke. Wenn ich mir schon soviel Aufwand gemacht habe den Traffic abzufangen, ist ein Bruteforce-Angriff das kleinste Problem, zumal moderne Grafikkarten mittlerweile 5.6 Mrd Hashs pro Sekunde berechnen können.
 
Es ist ja auch so alltagsüblich, dass CAs gehackt werden und dann auch noch "falsche" Zertifikate für kleine Mini-Seiten ausgestellt werden ...

Wer garantiert, dass RSA sicher ist? Das ist das viel größere Problem, denn die Sicherheit von RSA kann nicht bewiesen werden, es wird nur vermutet, dass die zu Grunde liegende Primfaktorzerlegung eines Angriffs auf RSA nicht effizient lösbar ist.