PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


Schöner hashen mit bcrypt

with 84 comments

Gastartikel von Oliver Sperke.

Ich bin 34 Jahre alt und seit 10 Jahren selbständiger Webentwickler. Mein Fokus liegt dabei auf der Erstellung, Beratung und Optimierung in den Bereichen High Performance, Usability und Sicherheit in den gängisten Internetsprachen: PHP, HTML, Javascript und CSS.

Bei meinem vorherigem Gastbeitrag wurde ich direkt im ersten Kommentar aus meiner heilen Welt geworfen. Dort stand nämlich folgender „erschütternder Kommentar“ zu lesen:

Das du Salting und Mehrfachhashing predigst, während der Rest der Welt schon einen Schritt weiter zu bcrypt geht… Traurig.

Nun ja, dazu möchte ich drei Dinge sagen.

  1. Ich predige nicht (Ausnahme: „Es heißt Standard, verdammt, nicht Standart!“).
  2. Ach, wenn die Welt schon mal auf dem Stand des einfachen md5 wäre …
  3. Bcrypt verdient einen eigenen Beitrag.

Natürlich hatte der Autor völlig recht. Über Hashfunktionen im Web zu schreiben und bcrypt nicht zu erwähnen ist fast schon schändlich. Also bcrypt ist eine Hashfunktion, die auf Langsamkeit optimiert wurde. Um genauer zu sein, es ist nicht mal ein richtiger Hashalgorithmus, sondern eine Blowfish Verschlüsselung, bei der am Ende „die Schlüssel weggeworfen werden“, daher lässt sich das Ergebnis nicht mehr entschlüsseln. bcrypt ist eine Weiterentwicklung der „Traditional DES Scheme“ Funktion aus der Unixwelt. Obwohl dieses Verfahren 30 Jahre lang (!) gute Dienste geleistet hat, stellen sich so langsam „Alterserscheinungen“ ein. Der Zahn der Zeit nagt auch hier in Form von gestiegener Rechenleistung.

Kurze Rückschau

Hashalgorithmen wie md5 und die shaX sind auf Schnelligkeit optimiert. Das ist gut, wenn man prüfen will, ob der heruntergeladen Film auch wirklich korrekt übertragen wurde. Das ist auch gut, wenn man testen möchte, ob das SSL Zertifikat einer Webseite nicht verfälscht wurde. Das ist aber eher suboptimal, wenn man damit Passwörter speichern will. Wie schon im letzten Artikel erklärt und ausgiebig diskutiert, ist die gestiegene Rechenleistung auch ein Problem für die klassischen Hashfunktionen in Webanwendungen. Zwar kann man mit Salts, mehr Salts und Mehrfachhashes das Schlimmste verhindern, aber irgendwie ist das alles nicht so nachvollziehbar für jeden. Kurz gesagt: „Das geht besser“.

Was bcrypt kann

Selbst wenn wir dafür sorgen, dass unsere Passwörter gut geschützt sind, können wir natürlich kaum verhindern, dass ein Benutzer trotzdem ein schwaches Passwort verwendet. Wenn es der Angreifer gezielt auf eine Person, z. B. den Admin abgesehen hat, wird die Situation noch schlimmer, denn bis 10 Zeichen kann man auch schon mal eine Brute Force Attacke für einen einzelnen Hash probieren. Deshalb sollten wir unserem Cracker das Leben grundsätzlich so schwer wie möglich machen.

Bcrypt bringt gewisse Vorkehrungen für genau diesen Fall mit. Der Algorithmus ist auch optimiert noch sehr langsam, und das ist gut so. Der Hash beinhaltet einen Wert für die Rundenanzahl, den sog. „Kostenfaktor“. Dies ist der Aufwand der bei der Berechnung betrieben werden muss. Jedem Hash kann zusätzlich ein individueller Salt zugeordnet werden. In PHP ist bcrypt über die crypt() Funktion seit Version 5.3 fest implementiert. Davor hing der Einsatz vom Betriebsystem ab.

Was bcrypt nicht kann

Bcrypt bringt einige schöne Fähigkeiten mit, die wir in der Webwelt wunderbar nutzen können. Allerdings gibt es einige Dinge, die designbedingt nicht vorgesehen sind. Dazu zählt ein geheimer Salt, den wir in unserer Anwendung hinterlegen können. Der Sinn resultiert aus der Überlegung, dass wenn ein Angreifer aus welchen Gründen auch immer auf die Datenbank zugreifen kann, er auch noch auf den Quelltext der Webanwendung zugreifen können muss. Im Zweifel entscheidet sich dort, ob unsere Passwörter weiter geschützt sind oder nicht.

Die zweite fehlende Funktion, ist die Möglichkeit Hashes von zusätzlichen Faktoren, wie der E-Mail Adresse (ersatzweise dem Benutzernamen oder die UserID) abhängig zu machen. Der Hintergrund ist etwas speziell. Nehmen wir mal an, ein Angreifer findet in unserer Webanwendung eine XSS Lücke, mit denen er die Session eines Benutzers übernehmen kann. Was wäre das Schlimmste, was er tun kann? Richtig, er ändert das Passwort oder die E-Mail Adresse. Wie kann ich das am effektivsten verhindern? Ganz klar, ich muss ihn zwingen zur Überprüfung das alte Passwort einzugeben. Durch die Kopplung der Hashes an die E-Mail kann ich das gar nicht vergessen. Man könnte natürlich den Salt abhängig von der E-Mail machen, aber was ist wenn ein Benutzer mehrfach angemeldet ist? Brute Force Attacken werden mit jedem Ziel lohnenswerter.

Vom Rein und Raus

Ein ganz einfacher Hash ensteht so:

crypt ( 'Passwort', '$2a$04$EinSaltFuerDasPasswort' );

Als Ausgabe ergibt sich:

$2a$04$EinSaltFuerDasPasswore.oNHNUzZrs1V5tpdv/WJ64.DIyBV1kC

Auf den ersten Blick ist das im Vergleich zu md5(‘Passwort’) natürlich etwas verwirrend, aber dafür schreibe ich das ja hier. Im ersten Argument steht der zu hashende String. Das zweite Argument besteht aus drei Teilen, die je mit einem $ eingeleitet werden. Der erste Block bestimmt die verwendete Funktion. Die möglichen Werte könnt Ihr auf php.net nachschauen. Wir nutzen hier nur $2a für bcrypt.

Der zweite Block beschreibt die Anzahl der Runden, mit dem der Hash erstellt wird. Der Wert darf zwischen 04 und 31 liegen. Mit jeder Runde verdoppelt sich die Zeit zur Erstellung, das System ist also exponentiell. Wenn eine Runde etwa 1 ms dauert, dann dauern 31 Runden ca. 74 Minuten. Genug Luft nach oben also. Brauchbare Werte liegen derzeit bei etwa 08 bis 12, je nach eingesetzter Hardware und Geduld. Gibt man Zahlen ausserhalb des Bereichs an, wird *0 zurück gegeben.

Der dritte Block ist der individuelle Salt. Dieser darf aus Groß- und Kleinbuchstaben, Zahlen, sowie ./ bestehen. Tauchen andere Zeichen auf, wird ebenfalls *0 zurück gegeben. Die Eingabewerte müssen also gut gewählt sein. Weiterhin darf der Salt aus 128 Bits, also 21 1/2 Zeichen bestehen. Wen das verwundert, 21 Zeichen werden komplett dargestellt, beim letzten Zeichen werden die Hälfte der Bits verworfen. Deshalb wird aus ‘EinSaltFuerDasPasswort’ im hash ‘EinSaltFuerDasPasswore’.

Die Ausgabe entspricht der Eingabe, gefolgt vom eigentlichem Hash. Jetzt kann man natürlich berechtigt fragen, was daran sicher sein soll, wenn da ja alles steht. Stimmt, aber genau dieses Verfahren ist gleichzeitig ein Vorteil.

Einmal bcrypt …

Ich erstelle der Einfachheit halber eine Funktion, mit der man die Eigenschaften von bcrypt richtig nutzen kann. Auch hier gilt wieder – nichts ist in Stein gemeisselt. Wenn Ihr Vorschläge habt, her damit. Die Funktionen nenne ich (besonders kreativ) bcrypt_encode und bcrypt_check.

function bcrypt_encode ( $password )
{
	return crypt ( $password, '$2a$04$EinSaltFuerDasPasswort' );
}

Diese Funktion gibt uns einen ersten Anfang. Ein Aufruf von bcrypt(‘Passwort’) gibt uns den o. g. Hash zurück. Die Saltfunktion nutzt natürlich überhaupt nichts, wenn man überall den gleichen Salt verwendet. Da der Salt in der Datenbank steht und daher nicht geheim ist, kann dieser pseudozufällig sein kann. Folgendes Konstrukt ist also ausreichend.

$salt = substr ( str_shuffle ( './0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ) , 0, 22 );

Die Anzahl der Runden sollte variabel sein. Der Hintergrund ist ganz einfach. Manche Accounts sind wichtiger als andere. Wenn ich als Admin 3 Sekunden zum Login warten muss, dann stört mich das nicht. Einem Besucher diese Wartezeit zu erklären, könnte sich aber schwierig gestalten oder als technische Schwäche fehlinterpretiert werden. Als Bonbon kann man dem Besucher auch anbieten, die sichere Variante zu wählen. Ein Normalwert sollte festgelegt werden, aber mit der Möglichkeit zu abweichenden Werten. Übertragen auf unsere Funktion ergibt sich.

function bcrypt_encode( $password, $rounds='08' )
{
	$salt = substr ( str_shuffle ( './0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ) , 0, 22 );
	return crypt ( $password, '$2a$' . $rounds . '$' . $salt );
}

Zur Passwortspeicherung habe ich jetzt eigentlich alle Möglichkeiten von bcrypt ausgeschöpft. Mit jedem Aufruf wird ein neuer Hash mit einem anderem Salt erzeugt. Nur sicherer fühl ich mich jetzt nicht, denn genau wie oben schon erwähnt gebe ich dem Angreifer freiwillig alle Daten. Es ist natürlich besser als ein purer md5 hash, aber eher noch gut gemeint als gut gemacht.

Daher würde ich diese Funktion gerne erweitern. Wie bei unserem md5 Beispiel bringe ich zusätzlich einen Salt ein, der nur im Quelltext hinterlegt ist. Dieser muss vor dem ersten Aufruf der Funktion mit define(‘SALT’, ‘beliebigerWert’) definiert werden. Ausserdem möchte ich, dass man bei einer Änderung der E-Mail Adresse das alte Passwort eingeben werden muss. In den Kommentaren zum letzten Beitrag hat ein Besucher erwähnt, dass das Einbringen eines Salt mit hash_hmac() sicherer wäre als einfaches voranstellen oder anhängen. Auch wenn ich die Bedenken in diesem speziellem Fall nur bedingt teile, da sich der individuelle Salt in jeder Zeile ändert und daher ein Angriff auf den systemweiten Salt sinnlos wäre, schaden kann es auch nicht und wenn wir schon einmal dran sind, klotzen wir mal richtig. ;-)

Dazu erweitern wir zunächst einmal mit str_pad() unseren String auf die viefachefache Länge des Passwortes, indem wir ihn mit dem sha1 hash der E-Mail Adresse davor und dahinter auffüllen. Ich nehme hier sha1, weil ich möchte, dass sich bei jeder E-Mail der Salt komplett ändert. Diesen String jagen wir dann durch hash_hmac() mit unserem systemweiten Salt in Whirlpool als Binärausgabe. Den entstandenen Zeichensalat verpacken wir mit bcrypt.

function bcrypt_encode ( $email, $password, $rounds='08' )
{
	$string = hash_hmac ( "whirlpool", str_pad ( $password, strlen ( $password ) * 4, sha1 ( $email ), STR_PAD_BOTH ), SALT, true );
	$salt = substr ( str_shuffle ( './0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ) , 0, 22 );
	return crypt ( $string, '$2a$' . $rounds . '$' . $salt );
}

Ein Aufruf von

bcrypt_encode( 'oliver@anonsphere.com', 'Test-Null8Fünfzehn' );

führt also zu diesen Ergebnissen:

// Passwort mit E-Mail auf vierfache Länge aufgefüllt
4e707693b984367edadf5a022867Test-Null8Fünfzehn4e707693b984367edadf5a022867e

// hash_hmac mit Whirlpool und systemweitem Salt (im Original binär)
b0492febbd81ef10387e2e7127295e09c197ff0cadf8ae5dc98182178f9e0891cf86b91abb1c723e0de510361cb67e1149460a687271672d77de439d7c572b57

// Verpackt mit bcrypt
$2a$08$jb8v67zmCNMO9dlX1tkVqOxGlhQkJNL45AvfpbEWvqXnGC8YcO7Hm

Die Sicherheit dürfte nur schwer anzuzweifeln sein. :-)

… und zurück

Jetzt fehlt noch die Möglichkeit den gespeicherten Passworthash mit unserem Passwort zu vergleichen. Jetzt kommen wir zu dem Teil, wo bcrypt von nett, auf cool wechselt.

$2a$08$jb8v67zmCNMO9dlX1tkVqOxGlhQkJNL45AvfpbEWvqXnGC8YcO7Hm


Schauen wir uns doch mal diesen Hash genauer an. Wir haben oben gesehen, der Hash besteht aus den Einstellungen und dem Ergebnis. Anders gesagt, der Hash liefert uns alles, was wir zum Berechnen brauchen. Dazu schreibe ich ihn mal anders, damit es klarer wird.

Einstellungen: $2a$08$jb8v67zmCNMO9dlX1tkVqOx
         Hash: GlhQkJNL45AvfpbEWvqXnGC8YcO7Hm


Der erste Teil entspricht genau dem Code, den wir zur Erzeugung genutzt haben, daher muss auch bei korrekter E-Mail und Passwort und diesen Einstellungen unser Hash heraus kommen. Dass heißt, wenn der hinterlegte hash die Variable $stored hat, dann muss folgende Bedingung wahr sein.

crypt ( $string, substr ( $stored, 0, 30 ) ) == $stored;

Oder übertragen in eine eigene Funktion.

function bcrypt_check ( $email, $password, $stored )
{
	$string = hash_hmac ( "whirlpool", str_pad ( $password, strlen ( $password ) * 4, sha1 ( $email ), STR_PAD_BOTH ), SALT, true );
	return crypt ( $string, substr ( $stored, 0, 30 ) ) == $stored;
}

Der Vorteil erschliesst sich erst auf den zweiten Blick. Während man bei der Umstellung von md5 auf sha1 oder von sha1 auf sha256 Probleme mit bestehenden Benutzerlogins bekommt, weil die alten Passwörter ungültig werden, nimmt uns bcrypt alle nötigen Workarounds ab. Bei der Prüfung ist es nämlich vollkommen egal, was als Ursprungswert an Runden und Salt hinterlegt wurde. Wenn ich irgendwann mal die Sicherheit erhöhen will oder auf einen schnelleren/langsameren Server umziehe, ändere ich den $rounds Wert und trotzdem funktionieren alte Logins weiter. Sobald aber die E-Mail oder das Passwort geändert wird, wird der neue Wert übernommen.

Was noch zu sagen wäre

Bcrypt ist eine schöne Sache, entbindet Euch aber keineswegs von zusätzlichen Sicherheitsüberlegungen. Wer zukunftsorientiert an ein neues Projekt geht oder wer schon beim letzten Artikel überlegt hat, ob seine Passwortspeicherung wirklich so sicher ist, wie er dachte, sollte überlegen, ob der Einsatz lohnt. Ein sicheres Verfahren deshalb abzulösen, ist aber auf jeden Fall unnötig.

Bleibt mir abschliessend nur noch eins zu sagen: „ES HEISST STANDARD!!11“.

Written by Oliver

Juli 18th, 2011 at 8:56 am

Posted in Linux,PHP

Tagged with , , , ,

84 Responses to 'Schöner hashen mit bcrypt'

Subscribe to comments with RSS or TrackBack to 'Schöner hashen mit bcrypt'.

  1. … dass die Welt noch nicht mal bei Hashing angekommen ist kann man ja auch schön an den Webseiten erkennen, die einem auf Anfrage gern das aktuelle Passwort zusenden… :)

    Dominik

    18 Jul 11 at 09:40

  2. @Dominik
    Oder alternetiv das alte Passwort im value-Attribut stehen lassen…

    Quu

    18 Jul 11 at 10:05

  3. Bezüglich der Generierung eines Salts:

    Natürlich generierst du dir mit dem str_shuffle() von einem Alphabet einen zufälligen String. Da kein Zeichen hier jedoch doppelt vorkommen kann, nimmst du dir einiges an möglichen Kombinationen aus deinem endgültigen String.

    Mit deinem Beispiel hast du also folgende Möglichkeiten (Unabhängig, dass du davon nur 22 Zeichen herausschneidest):

    64 mögliche Zeichen für den ersten Wert
    * 63 mögliche Zeichen für den zweiten Wert
    * 62 mögliche Zeichen für den dritten Wert
    * 61 mögliche Zeichen für den vierten Wert
    * 60 mögliche Zeichen für den fünften Wert
    * 59 mögliche Zeichen für den sechsten Wert

    * 2 mögliche Zeichen für den 63 Wert
    * 1 mögliches Zeichen für den 64 Wert

    Damit ergeben sich dann 1.2688693218588E+89 verschiedene Möglichkeiten.

    Solltest du dich jetzt entscheiden, dass du einen 64 Zeichen String zusammenbaust, bei dem Zeichen auch doppelt vorkommen dürfen, würdest du mit 64*64*64…*64 auf 3.9402006196394E+115 verschiedene Möglichkeiten kommen.

    Gut, letzten Endes wird der Unterschied in diesen Dimensionen wahrscheinlich keinen Unterschied machen, allerdings denke ich mir halt, warum sollte man diese Möglichkeit verschenken, wenn sie nur 2 Minuten länger dauert, zu implementieren? ;-)

    P.s. Ich hoffe, die Rechnung war soweit korrekt, sonst blamier ich mich jetzt aber definitiv ;-)

    Christian

    18 Jul 11 at 10:46

  4. „Weiterhin darf der Salt aus 128 Bits, also 21 1/2 Zeichen bestehen. Wen das verwundert, 21 Zeichen werden komplett dargestellt, beim letzten Zeichen werden die Hälfte der Bits verworfen.“

    Das verwirrt mich. 128 Bits sind bei 8 Bit pro Zeichen genau 16 Zeichen. 128 Bits geteilt durch 21.5 macht bei mir 5.95…, eine völlig krumme Zahl.

    Die Idee einer einzigen crypt()-Funktion, die die Parameter, die für die einzelnen Hash-Funktionen benötigt werden, in einen String-Parameter zusammenmanscht, halte ich übrigens für eine ganz, ganz blöde Idee. Wer kommt auf sowas?

    GodsBoss

    18 Jul 11 at 12:29

  5. Noch eine Frage zur check-Funktion:
    crypt($string, substr($stored, 0, 30)) == $stored;

    Wenn die zusätzlichen Bits hinter dem Salt verworfen werden, sollte das substr doch eigentlich unnötig sein, oder? Die 30 Zeichen scheinen ja ein Spezifikum von bcrypt zu sein. Wenn ich jetzt nicht nur die Parameter, sondern den ganzen Algorithmus wechseln möchte, der möglicherweise mehr oder weniger als 30 Zeichen erwartet, würde die schöne Eigenschaft der rückwärtskompatiblen Hashes kaputtgehen.

    Jetzt die Frage: Würde in deinem Fall
    crypt($string, $stored) == $stored;
    als Prüfung ausreichen und würde das auch für andere Crypt-Algorithmen funktionieren?

    Marcel

    18 Jul 11 at 12:36

  6. @GodsBoss:
    Das Salt wird auf dem base64-Alphabet angegeben, da steht 1 Zeichen für 6 Bits. 6*21 = 126, genaugenommen sind es also 21 1/3 Zeichen á 6 Bit.

    Die Idee mit dem “zusammengemanschten” Parameter halte ich für gar nicht so doof, weil man (wenn ich das richtig verstehe) den Output der crypt-Funktion direkt wieder zur Prüfung verwenden kann, explizit speichern zu müssen, welchen Algorithmus man mit welchen Parametern verwendet hat.

    Marcel

    18 Jul 11 at 12:41

  7. Ups, da hab ich ein Wort vergessen. Ich meinte natürlich “…, ohne explizit speichern zu müssen, …”

    Marcel

    18 Jul 11 at 12:44

  8. @Marcel:
    Achso! Gut, dass das nicht im Artikel steht, sonst könnte das noch jemand verstehen. ;-)

    Aus Entwicklersicht ist es einfach nur hässlich, mehrere Parameter einer Funktion in einen zu packen. Wahrscheinlich wäre das Erste, was ich machen würde, das Schreiben eines Wrappers. Zusammenpfriemeln kann ich jederzeit, zerlegen ist viel schwieriger.

    GodsBoss

    18 Jul 11 at 12:48

  9. @Quu
    Schlüsselerlebnis neulich beim ICQ Support

    Ich: „Mein Passwort geht nicht“
    Support: „Hm bei xy kann es ändern“
    Ich: „Ja, hab ich grade“
    Support: „Lassen Sie mal beim Passwort das letzte Zeichen weg. Das funktioniert nur bis 20 Zeichen“
    Ich: „Was zum …?“

    @Christian
    Mach Dir nix draus, ich verdreh die Zahlen auch immer, nicht wahr Michael? :-)
    Die Rechnung stimmt nicht ganz … 32^22 + 64^21 … 64^1 – macht aber, wie Du schon sagst, auch nicht den Megaunterschied, weil 64^21 übersteigt locker die Weltbevölkerung. Wenn Dir das trotzde zu wenig ist, kannst Du den String beliebig verlängern.

    @GodsBoss
    Das base64 Alphabet kommt mit 6 Bit aus, also eigentlich sind es 21 2/3 Zeichen, weil von den letzten 6 Bit werden 2 verworfen. Mit den Parametern, soweit ich weiß machen das alle Umsetzungen so, denn Du ersparst Dir damit das auseinander ziehen der Parameter.

    Oliver

    18 Jul 11 at 13:02

  10. Zerlegen sollte ja eigentlich unnötig sein, und beim Zusammenpfriemeln musst du über die erwarteten Parameter Bescheid wissen, und bist damit auf die crypt-Algorithmen beschränkt, die du vorgesehen hast. Dabei ist das gar nicht nötig. Die Ausgabe crypt-Funktion enthält alle Informationen die nötig sind, du brauchst also nur den String abspeichern, und kannst direkt gegen diesen prüfen, ohne dass du was über den Algorithmus oder dessen Parameter wissen musst.
    So kannst du zum Beispiel auch direkt Hashes aus einer anderen Nutzerbasis verwenden, die möglicherweise mit einem Algorithmus erzeugt wurde, der von dir gar nicht vorgesehen war.

    Marcel

    18 Jul 11 at 13:06

  11. @GodsBoss
    21 1/3, wie auch Marcel geschrieben hat. Es sind 126 Bits + 2 = 128

    @Marcel
    Ohne das substr sollte es auch gehen, ja. Hab ich aber selbst nicht probiert.

    Oliver

    18 Jul 11 at 13:06

  12. @Oliver:
    20 Zeichen? Bei meinem letzten ICQ-Passwortwechsel waren es noch 8 (habe ich aber selbst herausgefunden, Holladiewaldfee!).

    Base64 kenne ich, ich habe nur die erlaubten Zeichen nicht genau im Kopf. Meine Verwirrung lässt sich aber leicht erklären – so wie es im Artikel steht, ist es einfach irreführend. Erstens wird Base64 gar nicht erwähnt, zweitens ist es folgendermaßen:
    Der Salt selbst ist 128 Bit lang. 128 Bit kriege ich nicht in 21 Base64-Zeichen rein (die haben nur 126 Bit), also werden 22 gebraucht. Die haben dann 132 (22*6) Bit, also 4 zuviel, das sind die, die verworfen werden. Im Artikel steht nun, dass die Hälfe des _Zeichens_ verworfen wird – wenn aber 4 Bit verworfen werden, heißt das, dass ein Zeichen aus 8 Bit besteht. Das ist aber natürlich nur insoweit korrekt, dass der String tatsächlich aus 22 Bytes besteht. Von jedem Byte sind aber 2 Bit ungenutzt (da Base64) und vom letzten Byte sogar 6 Bit (die 2, die sowieso ungenutzt sind plus die 4, die zum Auffüllen dienten).

    GodsBoss

    18 Jul 11 at 13:29

  13. @GodsBoss
    Das Lustige ist ja, dass der Support
    a) mein Passwort kannte (und zwar alle Zeichen)
    b) aber von meinem Passwort mit 21 Zeichen nur 20 Zeichen genutzt wurden

    Mit den Bits hast Du natürlich Recht. Evtl. müsste Michael das anpassen.

    Oliver

    18 Jul 11 at 13:34

  14. @Oliver:
    Klingt so, als hätte ein ausreichend engagierter ICQ-Mitarbeiter gute Chancen, einen Artikel auf TheDailyWTF.com platzieren zu können. ;-)

    GodsBoss

    18 Jul 11 at 13:36

  15. [...] den vorhergehenden Beiträgen habe ich ja schon locker über Passwörter und wie man mit Ihnen umgehen sollte erzählt. Dabei [...]

  16. Stehe ich da jetzt auf dem Schlauch oder lässt sich mit der Funktion nicht überprüfen, ob der Benutzer in einer Datenbank existiert? Für die Variable $stored brauche ich ja erstmal das vorhandene Passwort, an welchem ich prüfen kann…

    Sicher wird das grad ein Eigentor. ^^

    Monti

    28 Jul 11 at 00:32

  17. Sicher wird das grad ein Eigentor. ^^

    Nö!

    $email ist der Benutzername, der eingegeben wurde
    $password ist das Passwort, was eingegeben wurde
    $stored ist der Hash aus der Datenbank

    SELECT `password` AS `stored` FROM `user` WHERE `email`=’$email’

    $valid_user = bcrypt_check ( $email, $password, $stored )

    Klar?

    Oliver

    28 Jul 11 at 01:42

  18. Oje, ich hab die Möglichkeit, die Einträge per Email einzugrenzen komplett übersehen…
    Danke nochmal.

    Monti

    28 Jul 11 at 08:47

  19. Ich persönlich bin eh kein Fan von diesen Konstrukten, wo E-Mail und Passwort aus der DB abgefragt werden, also …

    SELECT * FROM `user` WHERE `email`=’$email’ AND `password`=’$password’

    Die Gefahr von SQL Injections ist mir zu hoch. Allerdings muss man dann drauf achten, dass die E-Mail oder auch der Benutzername einmalig sind, was aber meistens der Fall sein sollte.

    Oliver

    28 Jul 11 at 12:17

  20. @Oliver:

    Prepared Statements nutzen -> Keine SQL Injection.

    GodsBoss

    28 Jul 11 at 12:22

  21. Der Gedanke ist noch aus der Zeit vor MySQL 5.0 hängen geblieben. :-)

    Ich selbst benutze eh fast nur die Active Records von Yii, sofern möglich.
    http://www.yiiframework.com/doc/guide/1.1/de/database.ar

    Oliver

    28 Jul 11 at 12:29

  22. GodsBoss

    28 Jul 11 at 12:33

  23. Ja, die Kritik ist bei Yii weitgehend unberechtigt, weil auch komplexe Relationen möglich sind und der Rest geht über Parameter Binding. Mir fällt spontan auch kaum etwas ein, dass sich nicht über AR abbilden lässt. Ich habe auch bisher kaum Abfragen gesehen, wo ich sagen würde, dass ist umständlich oder unnötig – die SQL Befehle kann man sich ja ausgeben lassen. Sofern da doch mal was drin steht, sitzt wie so oft das Problem vorm Monitor. :-)

    Auch die Validierung ist in meinen Augen kein großes Problem, denn Form und DB Controller sind bei mir getrennt. Falls also ein Feld nicht korrekt ist, kommt es erst gar nicht zur SQL – die Validierung in den AR selbst übernimmt Yii mit den Eigenschaften, die ich auch in den Tabellenfeldern hinterlege. Durch die Trennung wird die Applikation auch nicht aufgeblasen.

    Füge ich z. B. ein Feld dazu, lasse ich Gii laufen und kopiere die neuen Eigenschaften in meinen DB Controller.

    Oliver

    28 Jul 11 at 13:13

  24. Aus meiner Sicht ist die Hauptkritik an ActiveRecord die Verletzung des SRP. Die ist aber prinzipbedingt und kann durch keine Implementierung verhindert werden.

    Das von dir verlinkte Parameter Binding bezieht sich übrigens auf DAOs, die, wenn ich die Informationen bezüglich Yii richtig interpretiere, mit ActiveRecord gar nichts zu tun haben. Aber da liege ich eventuell falsch.

    GodsBoss

    28 Jul 11 at 13:45

  25. Ja, das Parameterbinding kann man dann benutzen, wenn man mit AR nicht weiter kommt. Das ist dann die „Softwareversion“ von Prepared Statements, die aber bei unterstützten Datenbanken auch die Technik von Prepared Statements nutzt.

    SRP halte ich bei Datenbanken, aber nicht nur da für ein sehr schwaches Argument, denn es würde ja bedeuten, dass ich nicht einen DB Controller sondern mindestens mal 4 bräuchte (=> CRUD plus Abhängigkeiten plus evtl. Validierung plus plus), was die Wartung garantiert nicht einfacher macht und auch die Performance nicht verbessert.

    Oliver

    28 Jul 11 at 14:01

  26. Wie ist der Passwort-Code zu erzeugen, wenn nur die eMail-Adresse geändert wurde?

    Christian

    5 Sep 11 at 17:30

  27. @Christian
    In der Variante muss das Passwort bei Änderung der E-Mail Adresse neu eingegeben werden.

    Oliver

    5 Sep 11 at 17:33

  28. Sehe ich es richtig, dass die eMail-Adresse nur unter Angabe des aktuellen Kennworts geändert werden kann?

    Christian

    5 Sep 11 at 17:37

  29. Sorry, hatte nicht eine solch schnelle Antwort erwartet und die Seite in der Zeit nicht aktualisiert. Dieser und mein vorheriger Eintrag können gerne gelöscht werden. Danke.

    Christian

    5 Sep 11 at 17:40

  30. “Ach, wenn die Welt schon mal auf dem Stand des einfachen md5 wäre …”
    Dazu kann ich nur sagen: Die Welt ist dir weit voraus, nur weil einige wenige noch weniger wissen, musst du nicht gleich überheblich werden.

    1. Nutze Standards, denke dir keine eigenen Verfahren aus, wenn du sie nicht ganz verstehst.

    2. “In den Kommentaren zum letzten Beitrag hat ein Besucher erwähnt, dass das Einbringen eines Salt mit hash_hmac() sicherer wäre als einfaches voranstellen oder anhängen. Auch wenn ich die Bedenken in diesem speziellem Fall nur bedingt teile, da sich der individuelle Salt in jeder Zeile ändert und daher ein Angriff auf den systemweiten Salt sinnlos wäre, schaden kann es auch nicht und wenn wir schon einmal dran sind, klotzen wir mal richtig.”

    So läuft IT-Sicherheit nicht, du weißt nicht, ob es unsicher ist, du nimmst Dinge an, aber du verstehst die mathematischen Zusammenhänge dahinter nicht. Wenn man die Mathematik hinter einem kryptographischer Hash nicht versteht, weil man sie wahrscheinlich gar nicht kennt, KANN MAN KEINE Schlussfolgerungen darüber treffen, wann er wie angewendet sicher ist, sondern sich nur auf Standards verlassen.

    3. Ein Salt MUSS zufällig sein, dein str_replace verwendet 1. eine unsichere Zufallsfunktion, 2. können dadurch einige Bitfolgen nicht vorkommen, da zweimal das gleiche Zeichen nicht vorkommen kann. Verstehst du die Mathematik hinter dem Hash? Nein, woher willst du dann wissen, dass aufgrund dessen ein Angreifer keinen Vorteil hat, vielleicht hat er ihn nicht, aber du kannst es nicht wissen, deshalb, mache es richtig. Am besten verwendet man /dev/urandom, wenn man auf Linux Servern arbeitet, um einen Salt zu generieren, denn die Jungs, die /dev/random programmiert haben, wissen was sie tun.

    Auch E-Mail-Adressen gehören nicht ein einen Salt, sondern NUR Zufallswerte! Wenn du einen zufälligen Salt generiert hast, kannst du da gerne noch eine E-Mail-Addresse reinpacken, dass bringt dir aber 0 an Sicherheit und erhöht nur die Rechenleistung. Wie kommst du überhaupt darauf das zu tun? Verstehst du die Mathematik dahinter und hast bewiesen, dass es sichere ist? Annahmen aus Halbwissen heraus zu schlussfolgern ist in der IT-Sicherheit mehr als falsch, es ist einfach unsicher.

    Beispiel:

    sha256(salt, passwort) ist sehr viel unsicherer als hmac(Salt, Passwort) mit sha256, warum? Ich weiß es nicht, es hat mit der Mathematik zu tun und das ist mir zu hoch, aber Leute die es verstehen, geben diese Tipps, also nutze sie.

    Max

    24 Sep 11 at 13:41

  31. Hallo,

    ich habe das Problem, dass die Passwörter ab und zu nicht mehr als gültig erkannt werden. Erst wenn ein Passwort neu erzeugt wird funktioniert es dann wieder…

    Kann mir jemand erklären wieso das passiert (alles laut Beispiel implementiert)?

    thx!

    Kim

    12 Jan 12 at 11:27

  32. @Kim: Was hast Du denn vorher benutzt?

    Oliver

    12 Jan 12 at 11:42

  33. @Oliver: Ich meinte, dass die Passwörter welche über das hier vorgestellte Verfahren “bcrypt_encode” erstellt wurden nicht immer über die Funktion “bcrypt_check” beim Verifizierungsverfahren als richtig erkannt werden.

    Kim

    12 Jan 12 at 13:09

  34. @Kim: Kannst Du Deinen Code posten? Also wie Du das aufrufst? Und warum nicht immer? Wie sind die anderen Daten erstellt?

    Oliver

    12 Jan 12 at 13:16

  35. Also der Code sieht folgender maßen aus. Das Einzige was hinzugefügt wurde ist dass das Passwort zuvor noch per md5 zusätzlich kodiert wird.

    function bcrypt_encode ( $email, $password, $rounds=’12′ ){
    $password = md5($password);
    $string = hash_hmac ( “whirlpool”, str_pad ( $password, strlen ( $password ) * 4, sha1 ( $email ), STR_PAD_BOTH ), BLOWFISCH_SALT, true );
    $salt = substr ( str_shuffle ( ‘./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ’ ) , 0, 22 );
    return crypt ( $string, ‘$2a$’ . $rounds . ‘$’ . $salt );
    }

    Kim

    12 Jan 12 at 13:24

  36. Und die Passwörter waren vorher in md5 und Du hast die nach bcrypt konvertiert?

    Oliver

    12 Jan 12 at 13:36

  37. Nein, das sind neu erstellte Passwörter mit dem hier vorgestelltem Verfahren. Deshalb ist ja alles so komisch! Kann es damit zusammenhängen, dass ich das Passwort zuvor mit md5 kodiere?

    Kim

    12 Jan 12 at 13:41

  38. @Kim:
    Nö, eigentlich kann das damit nix zu tun haben, denn ein md5 hash ist ja auch immer gleich. Ich verstehe aber immer noch nicht so ganz das Problem. Also wenn Deine Passwörter mit der Funktion kodiert sind, kannst Du sie auch vergleichen?

    @Max:
    Deinen Kommentar hab ich gar nicht gesehen. Ich bin nicht überheblich. Ich weiß aber, welche Funktionen in der weiten Welt unterwegs sind.

    1. Ja

    2. Du hast das Beispiel nicht verstanden. hash_hmac ist dann wichtig, wenn der systemweite Salt geschützt werden muss. Dieser systemweite Salt ist hier nur bedingt sicherheitsrelevant. Er stellt nur eine Hürde dar. Wenn das System kompromittiert ist, muss man davon ausgehen, dass er bekannt ist. Ist er nicht bekannt, kann hash_hmac davor schützen diesen zu berechnen. Den Aufwand wird zwar im normalen Leben kaum jemand betreiben, aber er könnte es. Ich habe den Unterschied verstanden. Deshalb kann man das mit hash_hmac machen, muss es aber nicht.

    3. Du hast offensichtlich das Prinzip nicht verstanden. /dev/random ist auch nur pseudozufällig und /dev/urandom ist in diesem Fall Unsinn. Ein Angreifer hat keinen Vorteil dadurch, auch wenn der Salt aus unterschiedlichen Zeichen besteht, das hab ich oben aber auch schon erklärt. Ein Angriff auf Basis von bestimmten Salts in bcrypt ist derzeit nicht bekannt. Das mit den E-Mail Adressen hast Du übrigens auch nicht verstanden. Warum ich das mache, obwohl ich es doch ausführlich erklärt habe.

    Das hier ist keine endgültige Sicherheitsempfehlung, denn die gibt es nicht. Es ist eine Möglichkeit der Implementierung, die ICH persönlich für sehr sicher halte. Beweise, dass es angreifbar und ich werde den Vorschlag nachbessern. Bis dahin ist er als sicher zu behandeln.

    Oliver

    12 Jan 12 at 14:09

  39. Kleiner Zwischenruf zu einer Aussage/Frage die ich mir auch gestellt habe

    > Sehe ich es richtig, dass die eMail-Adresse nur unter
    > Angabe des aktuellen Kennworts geändert werden kann?

    Ich würde sagen nein. Um den neuen Hash zu generieren brauche ich ja nur 1. die eMailadresse und 2. irgendein Passwort. Die Prüfung darauf, ob es das alte Passwort ist, also mit der alten eMailadresse zusammen den alten Hash ergibt, erfolgt ja nicht automatisch. bcrypt_check() muss man in die Applikationslogik übernehmen, das hat aber mit dem ganzen Verfahren imho nichts zu tun, man sollte schon alle relevanten Zugriffe durch eine zusätzliche Passwortabfrage sichern.

    chorn

    13 Jan 12 at 12:15

  40. @chorn:
    In dem Beispiel schon. Wenn Du die E-Mail änderst, ändert sich der Wert vor und nach dem Passwort und damit dann auch der bcrypt hash. Du benötigst also zur Prüfung und zum Ändern immer eine E-Mail (alt oder neu) und das Passwort (alt oder neu). Das ist wirklich nur, damit ich nicht vergessen kann, bei einer Änderung der Mail das Passwort abzufragen. Wer das nicht will, kann das einfach entfernen. Das würde es auch tun:

    function bcrypt_encode ( $password, $rounds='08' )
    {
        $string = hash_hmac ( "whirlpool", $password, SALT, true );
        $salt = substr ( str_shuffle ( './0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' ) , 0, 22 );
        return crypt ( $string, '$2a$' . $rounds . '$' . $salt );
    }
    
    function bcrypt_check ( $password, $stored )
    {
        $string = hash_hmac ( "whirlpool", $password, SALT, true );
        return crypt ( $string, substr ( $stored, 0, 30 ) ) == $stored;
    }
    

    Oliver

    13 Jan 12 at 13:11

  41. Hi,

    wenn man den Salt festeinstellt, kommen bei mir auf verschiedenen Systemen auch unterschiedliche Werte raus, ab dem 30. Zeichen.

    Man kann also nicht auf System A crypten und diese Werte dann auf System B einsetzen weil bcrypt_check dann nicht funktioniert.

    Das ganze liegt an:

    hash_hmac ( “whirlpool”, $password, SALT, true );

    Per:

    hash_hmac ( “whirlpool”, $password, SALT, false );

    kommen auf beiden Systeme identische Werte raus. Liegt also an der Rückgabe der Binärdaten.

    Weiß jemand wie das zusammenhängt?

    Mario

    13 Jan 12 at 19:31

  42. noch als Ergänzung:

    Wenn ich die Binärausgabe nutze und über das Ergebnis noch einmal sha1 laufen lasse, kommen auch identische Werte raus:

    sha1(hash_hmac ( “whirlpool”, $password, SALT, true ));

    Mario

    13 Jan 12 at 19:38

  43. @Mario:
    Schräg! Ne, kann ich Dir nicht sagen, warum das unterschiedlich ist. Was sind denn die Systeme?

    Oliver

    13 Jan 12 at 21:33

  44. @Oliver:
    mein Offlinesystem: Mac mit MAMP, PHP 5.3.6. Und online mein Hoster, PHP 5.3.8

    Mario

    14 Jan 12 at 16:01

  45. @Mario

    Kann das nur bestätigen.

    Habe mir stundenlang darüber den Kopf zerbrochen!!!
    Testsystem XAMPP auf Windows und Live Linux Server

    Kim

    31 Jan 12 at 12:24

  46. Vielen Dank für den wunderbaren Beitrag.
    Um die Sicherheit einer Webseite zu erhöhen sollte das Passwort in der Formulareingabe jedoch bereits vor dem POST-Versand verschlüsselt werden. Daher ist die Frage, wie die Javascript-Funktionen zu bcrypt aussehen würden

    Meier

    8 Feb 12 at 09:09

  47. Das wird nicht funktionieren, weil der Salt ja serverseitig gespeichert ist. Du kannst aber TLS/SSL verwenden oder http://www.phpgangsta.de/tlsssl-fur-heimwerker :-)

    Oliver

    8 Feb 12 at 09:45

  48. Sollte $2a eigentlich noch verwendet werden und nicht $2y ?

    siehe hier: http://lists.opensuse.org/opensuse-security-announce/2011-08/msg00015.html

    und hier: http://en.wikipedia.org/wiki/Crypt_%28Unix%29

    Ausserdem wäre noch ein Hinweis auf phpass nicht schlecht, das automatisch ein Fallback auf das nächst sichere Verfahren verwendet, falls bcrypt im System nicht verfügbar ist: http://www.openwall.com/phpass/
    (siehe auch c’t 13/2011)

    Holger

    25 Mrz 12 at 04:19

  49. Hm, müsste ich nachher noch mal in Ruhe durchlesen, aber so wie ich das sehe, betrifft das nur die PAM Module bzw. die glibc des Linuxsystems. Da aber PHP 5.3 nicht mehr auf das darunter liegende System angewiesen ist, dürfte es auch nicht betroffen sein. Die 2x und 2y Werte sind ja dementsprechend auch nicht in PHP verfügbar. Auch ein Problem mit Umlauten wäre mir nicht bekannt.

    PHP Pass kann man sicher erwähnen, allerdings braucht man das wirklich nur, wenn man Versionen vor 5.3 einsetzt. Da ja der Support lange eingestellt wurde, ist der Einsatz von 5.2.x sowieso ein Sicherheitsrisiko. Wer sich mit bcrypt beschäftigt, wird keine alte Version mehr einsetzen.

    Oliver

    25 Mrz 12 at 15:39

  50. [...] [...]

    Anonymous

    25 Mai 12 at 09:45

  51. Hallo, super Artikel.
    Eine Frage stellt sich mir noch. Und zwar, macht es einen Unterschied ob ich diesen Kostenfaktor erhöhe oder stattdessen einfach das ganze durch eine Schleife laufen lasse. Vorteile? Nachteile? So sah das bei mir bis jetzt aus.

    $password = “passwort123″;
    $salt = substr(str_shuffle(‘ABCDEFGHIJ……………..qrstuvwxyz0123456789′),0,22);
    $key = “AAAAAAAAAABBBBBBBBBBCC”;
    $hash = sha1($password.$salt.$key);

    for($i=0;$i kleiner 100;$i++){
    $hash = crypt($hash,’$2a$07$’.$key.’$’);
    }

    Auf mich wirken diese paar zeilen eig. ganz sinvoll.
    Was sagt ihr? (ich ahne pöses :D)

    ricH

    17 Jun 12 at 06:15

  52. Joah, kann man machen – muss man aber nicht, denn wie Du schon bemerkt hast, dafür ist eigentlich der Kostenfaktor da. (ich sehe jetzt aber auch keinen Nachteil).

    Oliver

    17 Jun 12 at 13:29

  53. Hallo Oliver,

    ich glaube du willst mich nicht verstehen, deshalb nur noch eins. Ein Satz wie “Beweise, dass es angreifbar ist” zeugt nur davon, dass du dich nicht viel mit IT-Sicherheit auseinander gesetzt hast. IT-Sicherheit läuft nach dem Prinzip, dass ein Verfahren Jahrelang getestet wird, weltweit von etlichen Sicherheitsexperten und dann langsam als sicher akzeptiert wird und nicht nach dem Motto, ich mache da mal was eigenes, denn das geht zu 100% schief. Es sei denn, man hat in dem Bereich promoviert und sein halbes Leben der Zahlentheorie gewidmet.

    Max

    13 Aug 12 at 21:48

  54. IT-Sicherheit läuft nach dem Prinzip, dass ein Verfahren Jahrelang getestet wird, weltweit von etlichen Sicherheitsexperten und dann langsam als sicher akzeptiert wird und nicht nach dem Motto, ich mache da mal was eigenes, denn das geht zu 100% schief.

    Das ist kein neuer Hashalgorithmus, sondern ein Verfahren, dass auf gängigen Empfehlungen und bestehenden Algorithmen beruht. Da die Maßgabe der Schutz des Originalpassworts ist, kann das Verfahren als so sicher gelten wie der stärkste verwendete Algorithmus. Ist bcrypt angreifbar, ist es immer noch so sicher wie whirlpool usw.

    Standardisierte Verfahren alleine (sagen wir nur bcrypt) haben immer den Nachteil, dass diese oft von Standardprogrammen berechnet werden können (Bsp. http://www.openwall.com/john/) und es deshalb für eine breitere Basis angreifbar ist. Das ist schlecht. Dieses Verfahren hier legt die Messlatte schon bedeutend höher.

    Und daher nochmal: Sag mir, wo ich einen Fehler gemacht habe bzw. finde einen Sicherheitsfachmann, der mir sagt, wo ich einen Fehler gemacht habe und ich ziehe meinen Vorschlag sofort zurück.

    Ich plane aber schon einen Folgeartikel, der noch mal einen drauf setzt. Ich komme nur derzeit nicht dazu, ihn auszuarbeiten.

    Oliver

    13 Aug 12 at 22:54

  55. [...] wählt. Selbst OpenVPN bietet standardmässig nur einfaches Hashing mit SHA1 oder MD5 an – das ist zu wenig! AnonSphere funktioniert so, dass die Daten durch den Datenbanktreiber in Freeradius umgeschrieben [...]

  56. [...] zwischen alten Verfahren wie MD5/SHA und neueren Algorithmen wie bcrypt. Dank Artikeln zum Thema bcrypt von Oliver sollte auch jedem mindestens eine Lösung bekannt sein die man heutzutage einsetzen sollte. Doch [...]

  57. [...] bringt prinzipiell alles mit, um anständiges Hashing zu betreiben. bcrypt ist der Way To Go, aber auch ohne bcrypt kann man mit vernünftigem Einsatz der vorhanden [...]

  58. Hallo Oliver,

    Meiner Meinung nach wird in dem Beitrag zu sehr auf dem Salt herumgeritten und damit letztlich auch deutlich, dass der Autor nicht wirklich verstanden hat, wozu ein Salt da ist.

    Der einzige Zweck eines Salts besteht darin zu verhindern, dass der Angreifer aus den für den Angriff auf einen User gewonnenen Daten einen Nutzen für den Angriff auf einen anderen User ziehen kann. Dazu muss der Salt (oder Teile davon) nicht geheim sein, das Ziel ist auch mit einem bekannten Salt je User absolut erreicht. Mehr geht hier nicht.

    Das Ziel der Passwortverschlüsselung bzw. des Passworthashens ist es auch nicht, es einem Angreifer möglichst schwer zu machen. Es muss unmöglich sein, in einem vertretbaren Zeitrahmen aus den erbeuteten Daten Passworte zu gewinnen. Mit BCrypt erreicht man dieses Ziel über den Work-Faktor. Damit kann man den Aufwand, ein einzelnes Passwort per Brute-Force zu ermitteln (bei MD5/SHA geht das binnen Sekunden/Minuten) in die Region von Jahren und Jahrzehnte rücken. Dadurch erhält der Site-Betreiber genügend Zeit um bei einem Userdatenklau entsprechend zu reagieren.

    Ein serverweiter geheimer Salt bringt hier rein gar nichts, weil man immer im Sinne der Sicherheit davon ausgehen muss, dass auch dieser dem Angreifer in die Hände gefallen ist. Es gibt keinen vernünftigen Grund, bei einem Angriff vom Gegenteil auszugehen.

    D.h. auch mit einem solchen serverweiten Salt hängt die Sicherheit einzig und allein am verwendeten Algorithmus, hier also an BCrypt. Hat man hier sichere Parameter verwendet, dann sorgt ein serverweiter Salt für keinen weiteren Sicherheitsgewinn mehr, denn mehr als “in einem vertretbaren Zeitraum unhackbar” geht nicht und wird auch nicht benötigt.

    Hat man einen leicht angreifbaren Algorithmus oder schwache Parameter verwendet, dann wäre es vollkommen idiotisch, jetzt noch darauf zu hoffen, dass dem Angreifer der serverweite Salt nicht in die Hände gefallen ist. Genauso gut könnte man darauf vertrauen, dass der Angreifer zu blöd ist, eine Brute-Force-Attacke zu fahren. Jeder sicherheitsbewusste Admin nimmt die Site jetzt sofort vom Netz.

    Und man könnte sogar behaupten, dass der serverweite Salt eher noch schadet, weil er den entwickelten Code unnützerweise etwas komplizierter macht, wodurch sich auch die Möglichkeiten von Programmierfehlern in einem wesentlichen Teil der Anwendung erhöht. Das gilt insbesondere auch für das Mehrfachhashen per Schleife, oder andere unnütze Befrachtung, wie z.B. die Verknüpfung mit der E-Mail-Adresse.

    Auch die Idee mit der XSS-Lücke und einer so erreichten Session-Übernahme ist ein absurdes Szenario. Das schlimmste ist hier nicht die Möglichkeit, das Passwort zu ändern (denn das würde ja wenigstens auffallen), sondern die Sessionübernahme selbst. Hier ist dem Angreifer ja ganz ohne Userdatenbank das gelungen, was der Brute-Force-Angreifer ebenfalls versucht: auf eine Benutzersession zuzugreifen bzw. eine zu erzeugen. Wie man das verhindert, hat allerdings mit dem Thema dieses Beitrages nichts zu tun.

    Auch unterschiedlich Work-Faktoren für unterschiedliche Userklassen ist Unsinn. Ein Admin ist nicht schützenswerter als ein normaler User, da es die Aufgabe des Admins ist, die User zu schützen. Wenn der Userspace kompromittiert ist, dann ist der Worst-Case bereits erreicht, weil dann der Adminspace seinen Sinn eingebüsst hat. Es ist allenfalls zu verschmerzen, wenn nur einzelne User als kompromittiert gelten. Ein einheitlicher niedriger Work-Faktor für die User setzt aber alle User auf die Abschussliste. Kurz: der Workfaktor muss immer so hoch sein, dass weder Admin noch User kompromittiert werden können. Und hier ist der mediale GAU, der Eintritt, wenn etliche User kompromittiert wurden, noch gar nicht angesprochen.

    Auch der letzte Hinweis, auf den Vorteil den BCrypt in Bezug auf die Erhöhung der Sicherheit nach langjährigem Betrieb per einfacher Änderung des Work-Faktors im Programmcode, weil dann die “alten” Passworte noch funktionieren, kann man so nicht stehen lassen:

    Der Work-Faktor ist nicht abhängig von der Hardware, die der Betreiber der Site einsetzt, sondern von der Hardware, die der Angreifer einsetzen könnte. Wenn der Server für das Hashen des PW mit einem sicheren Work-Faktor zu langsam ist, dann ist es höchste Zeit für einen Hardware-Upgrade, da auf dem Server der Work-Faktor ein weit geringeres Problem darstellt, als für den Angreifer. Aus dem selben Grund kann man auch nicht einfach nur den Work-Faktor im Anwendungscode ändern und den ursprünglichen Work-Faktor für alte Passworte in der Datenbank bestehen lassen, denn entscheidend für die Sicherheit ist letztlich, wie sicher die Passwort-Hashes der User sind. Als Angreifer würde ich mir zuerst die älteren und weniger sicheren aussuchen. Und es ist relativ einfach, beim nächsten Login des Users (der ja das PW im Klartext eingibt) das PW neu zu hashen. User, die sich über einen längeren Zeitraum nicht einloggen, sollte man irgendwann deaktivieren. Und genau das ist auch mit anderen Algorithmen nicht viel schwerer zu implementieren.

    Was noch zu sagen wäre:

    PW-Hashes, die auf SHA, MD5 usw. basieren, sollte man heute als unsicher betrachten, egal ob mit oder ohne Salt, und man sollte deswegen z.B. zu BCrypt oder anderen Verfahren, die einen Work-Faktor berücksichtigen, wechseln.

    TomW

    30 Okt 12 at 16:34

  59. SELECT * FROM `user` WHERE `email`=’$email’ AND `password`=’$password’

    ist auch mit prepared_statements keine gute Idee, wenn man Indices einsetzt, weil mit jeder PW-Änderung unützerweise der Index aktualisiert werden muss. Den Abgleich des PWs kann man auch per PHP machen, nachdem man den User per

    SELECT * FROM `user` WHERE `email`=?

    geholt hat.

    TomW

    30 Okt 12 at 16:49

  60. Es muss unmöglich sein, in einem vertretbaren Zeitrahmen aus den erbeuteten Daten Passworte zu gewinnen.

    Hast Du den Artikel gelesen, weil darum ging es ja? :-)

    Dadurch erhält der Site-Betreiber genügend Zeit um bei einem Userdatenklau entsprechend zu reagieren.

    Wie soll er denn reagieren? Die Benutzerdaten sind doch schon weg. Deshalb ist es ja essentiell, die Hürden so hoch wie möglich zu legen.

    Ein serverweiter geheimer Salt bringt hier rein gar nichts, weil man immer im Sinne der Sicherheit davon ausgehen muss, dass auch dieser dem Angreifer in die Hände gefallen ist. Es gibt keinen vernünftigen Grund, bei einem Angriff vom Gegenteil auszugehen.

    Das ist natürlich richtig nicht ganz falsch, aber es gibt durchaus Szenarien, in denen das nicht so ist, z. B. wenn nur die Datenbank geleakt wird. Die Passwörter darin sind völlig wertlos, wenn der systemweite Salt fehlt. Und aus meiner Erfahrung kann ich sagen, dass man an eine Datenbank meist viel schneller kommt als an Serverdaten.

    Genauso gut könnte man darauf vertrauen, dass der Angreifer zu blöd ist, eine Brute-Force-Attacke zu fahren. Jeder sicherheitsbewusste Admin nimmt die Site jetzt sofort vom Netz.

    Ich glaube, Du hast die Problematik nicht ganz verstanden. Wenn Userdaten verloren gehen, dann ist nicht die größte Gefahr, dass die sich jemand auf der Seite anmeldet, sondern dass die Passwörter auf anderen Seiten verwendet werden. Da kannst Du Deine Seite 100 mal vom Netz nehmen. Wenn jemand “geheim” als Passwort hat, dann lohnt sich auch eine Wörterbuch Attacke und wenn er bei Paypal die gleiche Mail/Pass Kombi hat, dann nutzt es ihm auch nichts, wenn er dass Passwort auf Deiner Seite ändern muss.

    Und man könnte sogar behaupten, dass der serverweite Salt eher noch schadet, weil er den entwickelten Code unnützerweise etwas komplizierter macht, wodurch sich auch die Möglichkeiten von Programmierfehlern in einem wesentlichen Teil der Anwendung erhöht. Das gilt insbesondere auch für das Mehrfachhashen per Schleife, oder andere unnütze Befrachtung, wie z.B. die Verknüpfung mit der E-Mail-Adresse.

    Und Lebensmittelkonsum führt in vielen Fällen zu Lebensmittelvergiftung …

    Auch die Idee mit der XSS-Lücke und einer so erreichten Session-Übernahme ist ein absurdes Szenario. Das schlimmste ist hier nicht die Möglichkeit, das Passwort zu ändern (denn das würde ja wenigstens auffallen), sondern die Sessionübernahme selbst.

    Auch das ist nicht ganz richtig. Sagen wir, wir sind in einem sozialem Netzwerk und übernehme die Session. Dann kann ich im Namen meines Opfers posten. Aber ich kann den Account erst dann übernehmen, wenn ich E-Mail und Passwort ändere. Das kann ich aber nur, wenn ich auch das Passwort kenne.

    Kurz: der Workfaktor muss immer so hoch sein, dass weder Admin noch User kompromittiert werden können.

    Was aber nicht heißt, dass er nicht höher sein darf, oder?

    Der Work-Faktor ist nicht abhängig von der Hardware, die der Betreiber der Site einsetzt, sondern von der Hardware, die der Angreifer einsetzen könnte. Wenn der Server für das Hashen des PW mit einem sicheren Work-Faktor zu langsam ist, dann ist es höchste Zeit für einen Hardware-Upgrade, da auf dem Server der Work-Faktor ein weit geringeres Problem darstellt, als für den Angreifer.

    Nach Deiner Aussage muss also jedes Forum mit 100 Leuten auf ‘nem dediziertem Server laufen, weil es sonst unsicher ist. Willkommen in der echten Welt, kann ich da nur sagen.

    Aus dem selben Grund kann man auch nicht einfach nur den Work-Faktor im Anwendungscode ändern und den ursprünglichen Work-Faktor für alte Passworte in der Datenbank bestehen lassen, denn entscheidend für die Sicherheit ist letztlich, wie sicher die Passwort-Hashes der User sind.

    Natürlich sollte man die irgendwann mal updaten, aber darum ging es hier gar nicht. Ausserdem waren wir uns ja einig, dass der normale Workfaktor schon ausreichend hoch sein sollte. Wenn morgen nicht jemand mit einem Quantencomputer aus ‘nem Labor springt, wird der bei Berechnungszeiten von mehreren Mio. Jahren auch in den nächsten 50 Jahren nicht unsicherer.

    PW-Hashes, die auf SHA, MD5 usw. basieren, sollte man heute als unsicher betrachten, egal ob mit oder ohne Salt, und man sollte deswegen z.B. zu BCrypt oder anderen Verfahren, die einen Work-Faktor berücksichtigen, wechseln.

    Womit wir wieder am Anfang sind. Hast Du den Artikel gelesen, weil darum ging es ja? :-)

    Oliver

    30 Okt 12 at 17:22

  61. Hallo Oliver,

    Wie soll er denn reagieren? Die Benutzerdaten sind doch schon weg. Deshalb ist es ja essentiell, die Hürden so hoch wie möglich zu legen.

    z.B. indem der die Nutzer benachrichtigt und sie bittet, ihr Passwort zu ändern. Bei einem sicheren Hash kann er ihnen dann auch versichern, dass die Passworte so schnell nicht ermittelt werden können und sie deswegen keine Panik schieben müssen.

    Die einzige Möglichkeit, die Hürde zuverlässigerweise hochzulegen, ist ein hoher Work-Faktor. Wenn Du zusätzlich noch den geheimen Saltzusatz brauchst, hast Du ein Problem.

    Das ist natürlich nicht ganz falsch, aber es gibt durchaus Szenarien, in denen das nicht so ist, z. B. wenn nur die Datenbank geleakt wird. Die Passwörter darin sind völlig wertlos, wenn der systemweite Salt fehlt. Und aus meiner Erfahrung kann ich sagen, dass man an eine Datenbank meist viel schneller kommt als an Serverdaten.

    Wenn Du als Entwickler eine Anwendung entwickelst, kannst Du nicht allein von diesem Szenario ausgehen. Die Passworte müssen auch für den Fall geschützt sein, wenn dem Angreifer der systemweite Salt bekannt ist. Wenn Du die Passworte auch für diesen Fall absicherst, dann ist der systemweite Salt überflüssig, weil er keinen weiteren Sicherheitsgewinn mehr bringen kann. Tust Du es nicht, sind die Passworte nur noch in dem oben beschriebenen Szenario sicher.

    Ich glaube, Du hast die Problematik nicht ganz verstanden. Wenn Userdaten verloren gehen, dann ist nicht die größte Gefahr, dass die sich jemand auf der Seite anmeldet, sondern dass die Passwörter auf anderen Seiten verwendet werden. Da kannst Du Deine Seite 100 mal vom Netz nehmen. Wenn jemand “geheim” als Passwort hat, dann lohnt sich auch eine Wörterbuch Attacke und wenn er bei Paypal die gleiche Mail/Pass Kombi hat, dann nutzt es ihm auch nichts, wenn er dass Passwort auf Deiner Seite ändern muss.

    Das ist richtig. Ich habe mich der Einfachheit halber nur auf das Szenario beschränkt, dass nur Usernamen/E-Mail-Adressen und Passworthashes abhanden gekommen sind und was die Folgen sind, wenn das Passwort ermittelt wird. Die sind für alle Sites, die die selbe Username-Passwort-Kombination verwenden selbstverständlich gleich. Ich dachte, dass das nicht gesondert erwähnt werden muss sondern klar ist.

    Und Lebensmittelkonsum führt in vielen Fällen zu Lebensmittelvergiftung …

    Möglichst einfachen und gut verständlichen Code zu entwickeln ist keine schlechte Praxis, die zu mehr Sicherheit beiträgt, oder willst Du dem wirklich widersprechen? Wenn zusätzlicher Code keinen echten Nutzen bringt, dann sollte man auf ihn verzichten.

    Auch das ist nicht ganz richtig. Sagen wir, wir sind in einem sozialem Netzwerk und übernehme die Session. Dann kann ich im Namen meines Opfers posten. Aber ich kann den Account erst dann übernehmen, wenn ich E-Mail und Passwort ändere. Das kann ich aber nur, wenn ich auch das Passwort kenne.

    Ja, keine Frage. Nur beginnt der Worst-Case bereits mit der Übernahme der Session und nicht erst dann, wenn der Angreifer die Userdaten ändert. Man kann nicht pauschal sagen, solange der Angreifer die Userdaten nicht ändern kann, ist noch nicht das schlimmste passiert, denn es hängt von dem Service ab, den die Site bietet, womit man dem User am schlimmsten schaden kann. Wenn der Angreifer z.B. einmalig eine Bestellung für 1000 Euro aufgibt, dann dürfte das für viele User bereits der Super-Gau sein.

    Was aber nicht heißt, dass er nicht höher sein darf, oder?

    Richtig. Es stellt sich nur die Frage, welcher Nutzen damit erzielt werden soll. Wenn der Work-Faktor bereits für die User so hoch ist, dass die Passworte sicher sind, dann bringt ein höherer Work-Faktor keinen Nutzen mehr. Bringt er einen Nutzen, dann stellt sich die Frage, warum die User einem geringeren Schutz ausgesetzt werden, wenn alles, was es zu schützen gilt, am Ende immer die Userdaten sind.

    Nach Deiner Aussage muss also jedes Forum mit 100 Leuten auf ‘nem dediziertem Server laufen, weil es sonst unsicher ist. Willkommen in der echten Welt, kann ich da nur sagen.

    Das ist nicht dein Ernst: Wie oft sollen sich denn die 100 User am Tag ein- und ausloggen, damit die Serverlast so ansteigt, damit dieses Szenario einen Realitätsbezug hat? Wenn sich jeder 2x am Tag einloggt, dann verbraucht das Login der 100 Leute bei einem Work-Faktor, der 0,3 Sekunde Rechenzeit verursacht, 1 Minute Rechenzeit auf einem einzelnen Core. Das Cracken eines einzelnen Passworts bei der selben Rechenleistung benötigt hingegen 12 Jahre. Selbst wenn Du die tausendfache Rechenzeit einsetzen kannst, dann braucht der Angreifer zur Ermittlung eines einzigen Passworts immer noch 4 Tage für alle 100 User braucht er so mehr als 1 Jahr. Und das alles unter der unrealistischen Annahme, dass es sich um einfache kurze Passworte mit 6 Zeichen handelt.

    Ich gehe mal sehr schwer davon aus, dass die vielen andere Sachen, die die User auf dem Server machen, bei steigender Userzahl sehr viel eher einen weiteren Server notwendig machen, als der Login-Prozess.

    Womit wir wieder am Anfang sind. Hast Du den Artikel gelesen, weil darum ging es ja?

    Ich hatte hingegen am Ende den Eindruck, dass Du den Nutzen von BCrypt wieder ein wenig in Frage stellen wolltest.

    Aber noch einmal auf den serverseitigen Salt zurückkommend:

    Warum setzen wir eigentlich Hashes ein statt die Passworte einfach zu verschlüsseln? Weil wir dann ein Geheimnis in Form eines Verschlüsselungspasswortes auf dem Server hinterlegen zu müssten. Jeder, der an dieses Verschlüsselungspasswort und die Userdaten gelangt, der kann dann die Userpassworte einfach entschlüsseln, egal, wie sicher das Verschlüsselungsverfahren ist.

    Hashes entbinden uns von der Notwendigkeit, ein Geheimnis auf dem Server zum Schutz der Nutzerpassworte hinterlegen zu müssen. Deswegen ist es vollkommen absurd, in Form von geheimen serverseitigen Salts dieses Problem wiederzubeleben. Passworte müssen so gehasht werden, dass man sie möglichst nicht per Brute-force in annehmbarer Zeit ermitteln lassen. Dann sind diese Salts überflüssig. Alles andere ist Security by Obscurity und damit unsicher.

    TomW

    30 Okt 12 at 21:32

  62. Passworte müssen so gehasht werden, dass man sie möglichst nicht per Brute-force in annehmbarer Zeit ermitteln lassen. Dann sind diese Salts überflüssig. Alles andere ist Security by Obscurity und damit unsicher.

    Nee, um jetzt nicht alles noch mal zitieren zu müssen und um es auf einen Nenner zu bringen. Es ist (IMHO) nicht schädlich mehr zu tun als nötig. Grundsätzlich spricht auch nichts gegen Pepper oder gegen die Nutzung von verschiedenen Algorithmen. Teilweise sind die sogar nötig, weil bcrypt selbst ein paar Schwächen hat. Ausserdem hälst Du Scriptkiddies davon ab, mit JTR rum zu experimentieren.

    Genau so ist es bei den Sessions. Natürlich sollte der Rest nicht unsicher sein, aber ein Trapezartist lässt auch nicht das Netz weg, weil die letzten Jahre nichts passiert ist. Wenn man den Login als abgeschlossenes Modul sieht, ist selbst meine Version nicht so komplex als dass es da Probleme geben könnte.

    Wenn Du also einen konkreten Fehler findest, der meinen Vorschlag unsicher macht, ok. Aber pauschal zu sagen, dass etwas unsicher wird, weil es zusätzlichen Schutz bietet, macht in meinen Augen wenig Sinn.

    Oliver

    31 Okt 12 at 05:02

  63. Ich zitiere mal dies

    http://www.martinstoeckli.ch/hash/de/

    “Geben wir noch etwas Pfeffer dazu

    Mit Pfeffern meint man das Kombinieren einer geheimen Zeichenfolge mit dem Passwort, bevor man den Hash-Wert berechnet.

    $scharfesPasswort = $passwort + “p8empspher3ocm1yUwiq”;
    $hash = bcrypt($scharfesPasswort);

    Der Pfeffer (Pepper) ist geheim und wird nicht in der Datenbank gespeichert. Stattdessen wird er an einem möglichst sicheren Ort hinterlegt, der gleiche Pepper gilt für alle Passwörter.

    Kennt der Angreifer unseren Code (Kontrolle über den Server), so bringt der Pepper keinerlei Vorteile.

    Hat der Angreifer nur Zugriff auf die Datenbank (SQL-Injection), so erkennt er immer noch die Hash-Werte, die Hash-Werte stammen aber nicht mehr von schwachen Passwörtern. Es sind Hash-Werte von langen Kombinationen von Passwort und einem starken Pepper. Kein Wörterbuch enthält je solche Passwörter, ein Wörterbuchangriff ist darum sinnlos.

    Häufig wird empfohlen einen HMAC zu verwenden, um Passwort und Pepper zu kombinieren. Werden sie hingegen einfach aneinandergehängt, so sollte der Pepper hinter und nicht vor dem Passwort angefügt werden, da manche Hash-Funktionen Zeichen ab einer bestimmten Position ignorieren.”

    ECMA-262-3rd

    4 Feb 13 at 15:10

  64. [...] Eigenschaften (dynamischer Salt, langsamer Algorithmus gegen Brute-Force) direkt mitbringt: bcrypt. PHP 5.5 führt dazu sogar eine einfach zu nutzende Password Hashing API ein, von der [...]

  65. TomW und andere Vorposter haben es ja schon geschrieben – ich will nur nochmal für andere darauf hinweisen und betonen:
    Ich würde den obigen Artikel NICHT als Anleitung für eine Eigenimplementierung nehmen.

    1) es gibt dafür eine fertige PHP-Libraries

    2) die Generierung des Salts ist Mumpitz. Der sollte *nirgendwo* fix hinterlegt werden, um damit z.B. den Random-Salt zu erweitern oder so Späße – sondern sollte ein REINER Zufallswert sein – und zwar nicht Applikationsweit sondern für jeden einzelnen Eintrag neu. Das str_shuffle ist zwar naheliegend, aber wie schon angemerkt zu kurz gedacht – da Dopplungen unnötigerweise verhindert werden. Man erzeuge eine Zufallszahl und konvertiere die in das spezielle base64-Format von bcrypt. Auch dafür gibts fertige Funktionen.

    3) Die Vorverarbeitung des Passworts (mit whirlpool) ist unnötig, da bcrypt selbst für die Streckung des Passwortes sorgt. Ich empfinde das sogar als schädlich. BCrypt-Hashes sind über sprachgrenzen hinweg lesbar. Das, was da fabriziert wird aber nur bedingt – könnte also ggf. später nicht mal eben in ein anderes System übernommen werden.

    4) Die Rundenzahl “04″ aus dem Beispiel sollte ja wohl *nirgendwo* genommen werden

    Evtl. sollte nochmal klar sein: Der Salt sollte pro Eintrag eindeutig sein – und sollte nichts mit den anderen Einträgen gemeinsam haben.

    yves

    14 Mai 13 at 22:39

  66. es gibt dafür eine fertige PHP-Libraries

    Das ist eine Begründung? Ich kann es bei Github hochladen, wenn Du Dich dann besser fühlst.

    die Generierung des Salts ist Mumpitz.

    Nein, ist es nicht.

    sondern sollte ein REINER Zufallswert sein

    Kannst Du lesen? Es ist ein Zufallswert!

    Das str_shuffle ist zwar naheliegend, aber wie schon angemerkt zu kurz gedacht – da Dopplungen unnötigerweise verhindert werden.

    Macht genau welchen Unterschied? Richtig, keinen.

    Die Vorverarbeitung des Passworts (mit whirlpool) ist unnötig, da bcrypt selbst für die Streckung des Passwortes sorgt. Ich empfinde das sogar als schädlich.

    Hast Du überhaupt eine Ahnung, wovon Du redest? Wie lang darf ein Wert sein, dass er mit BCrypt richtig gehasht wird? Na?

    BCrypt-Hashes sind über sprachgrenzen hinweg lesbar. Das, was da fabriziert wird aber nur bedingt – könnte also ggf. später nicht mal eben in ein anderes System übernommen werden.

    Whirlpool ist genau so standardisiert wie BCrypt. Wir können auch noch ein paar Sonderfälle raus suchen, nachdem man unbedingt md5 nehmen muss.

    Die Rundenzahl “04″ aus dem Beispiel sollte ja wohl *nirgendwo* genommen werden

    Kannst ja 12 nehmen, am besten auf einem Webspace, wo es dann 10 Sekunden dauert. 04 ist u. U. aber ebenfalls ausreichend, was im Algorithmus von Bcrypt begründet ist.

    Oliver

    14 Mai 13 at 23:09

  67. Ich bin selbst Webentwickler und ich finde dieses Thema hier echt amüsant. Jedoch läuft hier einiges drunter und drüber.

    Der Salt dient nicht der Verlängerung der Berechnungszeit sondern soll nur das Vorabberechnen einer RainbowTable verhindern. Daher muss dieser für jedes Passwort unterschiedlich sein. Der Salt sollte eine zufällige Zeichenkette sein.

    Es ist natürlich Sinnvoll zu verhindern das in einer gestohlenen Session keine wichtigen Änderungen durchgeführt werden können (Passwort ändern, Email Adressen ändern, wichtiger Einstellungen ändern) jedoch ist dies nicht Aufgabe einer Authentifizierungsklasse sondern der eigentlichen Applikation.

    Der Hash Algorithmus sollte nur das Passwort und den Salt selbst hashen, dies übernimmt die php Funktion crypt().
    Das danach wird noch geprüft ob der erzeugte Hash dem gespeicherten Hash entspricht und fertig ist die Sache.

    Eine Veränderung des Hash Algorithmus im laufenden Betrieb ist eigentlich weder mit md5() shaX() bcrypt() ein Problem. Beim Login wird geprüft wie der Hash gespeichert ist. Wenn noch die alte Version gespeichert ist wird das Passwort mit dem alten Algorithmus überprüft, mit neuen neu gehasht und gespeichert, der alte Hash gelöscht. Ansonsten wird nur mit dem neuen Hash überprüft.
    Eine gute Authentifizierungsklasse sieht den Wechsel des Algorithmus bereits vor. Ob gesondert gespeichert wird oder nicht welcher Algorithmus verwendet wird ist egal. Jedoch ist es eventuell Sinnvoll alle relevanten Parameter in einer Spalte zu Speichern, da eventuell neuere Algorithmen mehr Parameter benötigen, als zum Zeitpunkt des Datenbankdesigns bekannt war.

    Zum Thema Security by Obscurity, dort gehört dein Beitrag leider hin:

    Es ist davon auszugehen, dass dem Angreifer ohnehin alle gespeicherten Parameter bekannt sind (Hash, dynamischer Salt, statischer Salt, Algorithmus, Rundenzahl etc).
    Es ist zwar richtig, dass die Datenbank ohne den festen Salt Bestandteil wertlos ist. Jedoch wird ein Angreifer der überhaupt die Ressourcen besitzt um bcrypt mit ausreichender Rundenzahl zu knacken nicht daran scheitern alle relevanten Informationen in seinen Besitz zu Bringen.
    Die Scriptkiddies mit ihren 1337 Maschienen scheitern schon an einem dynamischen Salt mit ausreichender Rundenzahl.
    Der Rest gehört einfach nicht in den Hashalgorithmus oder die Authentifizierungsklasse.

    Die Funktionen die Sie an bcrypt bemängeln sind

    1. unnötig, da unwirksam
    2. in md5() und shaX() ebenfalls nicht vorhanden
    3. auch in bcrypt können Sie an die zu hashende Zeichenkette beliebige Parameter anhängen (wie Emailadresse oder statischen Salt)

    Ich weiß, dass es Schwierig ist mit Kritik umzugehen, Sie benehmen sich jedoch wie ein kleines Kind.

    Sehen Sie doch einfach ein, dass Ihr Algorithmus Verbesserungswürdig ist, es sollen keine persönlichen Angriffe sein, sondern Verbesserungsvorschläge. Gerade im Bereich Sicherheit sollte man Versuchen das Beste zu Programmieren oder zu Verwenden, nehmen Sie sich das Gesagte zu Herzen und überlegen Sie wie sie Ihren Algorithmus noch verbessern können. Ein kleiner Hinweis von php selbst zur crypt() Funktion:
    Kurz zusammengefasst sollten Entwickler “$2y$” bevorzugt verweden, wenn sie nicht mit PHP Versionen vor PHP 5.3.7 kompatibel sein müssen.

    Ich habe mir erlaubt Sie hier noch einmal aus ihrem oberen Beitrag zu Zitiren:

    Kurz gesagt: „Das geht besser“.

    Balint

    21 Mai 13 at 19:36

  68. Jedoch wird ein Angreifer der überhaupt die Ressourcen besitzt um bcrypt mit ausreichender Rundenzahl zu knacken nicht daran scheitern alle relevanten Informationen in seinen Besitz zu Bringen.

    Beispiele? Es ist ja nun oftmals so gewesen, dass Datenbanken von Sony, Samsung, Yahoo usw. veröffentlicht wurden. Wann wurde dabei auch Applikationsquelltext offen gelegt? Oder anders: Wo schränkt die Vorgehensweise die Sicherheit der Passwörter ein?

    Sofern ein Angreifer auf meinen Server direkt zugreifen kann, sind meine Benutzerdaten verloren, denn er könnte sich auch alle eingegebenen Passwörter zuschicken lassen. Wenn aber, wie im Normalfall „nur“ die Datenbank verloren geht, dann ist das ein prima Schutz vor allem für die, die einfach zu erratende Passwörter nutzen.

    Der Rest gehört einfach nicht in den Hashalgorithmus oder die Authentifizierungsklasse.

    Sagt wer? Schränkt es die Sicherheit ein, wenn man elementare Fehler von vorne herein vermeidet?

    Sehen Sie doch einfach ein, dass Ihr Algorithmus Verbesserungswürdig ist, es sollen keine persönlichen Angriffe sein, sondern Verbesserungsvorschläge.

    Wie sehen denn die Verbesserungen aus? Bisher ist noch in keinem Kommentar ein echter Schwachpunkt genannt worden.

    Kurz zusammengefasst sollten Entwickler “$2y$” bevorzugt verweden, wenn sie nicht mit PHP Versionen vor PHP 5.3.7 kompatibel sein müssen.

    Der Beitrag ist 4 Monate vor PHP 5.3.7 erschienen. Meine Glaskugel hat mir das damals nicht verraten, sorry. Allerdings spielt es für aktuelle PHP Installationen keine Rolle, ob man 2a oder 2y verwendet. In beiden Fällen kommt das gleiche raus.

    Oliver

    21 Mai 13 at 20:35

  69. Nur weil Seitenquelltext nicht veröffentlicht wird, heißt es nicht, dass kein Zugriff darauf vorhanden war. Jedoch werden auch viele Internetseiten über FTP gehackt und die Datenbank dann mittels hochgeladener Scripte extrahiert. Es ist richtig das die Sicherheit nicht verringert wird, jedoch erhöht sie sich auch nicht.

    Ein höherer work factor ist hier immernoch das Mittel der Wahl. Zusammen mit Passwortregeln welche nach Möglichkeit sehr Konservativ gewählt werden um die Mehrzahl der Benutzer nicht zu verärgern.

    Nein es schränkt in diesem Fall die Sicherheit nicht ein, jedoch ist es einfach kein sauberes Programmieren, wenn Aufgaben von Modulen vorgenommen werden, für die sie eigentlich nicht vorgesehen sind. Dies führt zu unübersichtlichem Code welcher aufgrund von Fehlern wieder Sicherheitslücken öffnen kann.

    Nur weil sie glauben, dass es keine echten Schwachpunkte sind heißt es nicht, das es wirklich keine sind. Gemeint ist ihre Funktion welche den Salt erzeugt.

    Wie bereits von anderen erwähnt schließen Sie so effektiv einen sehr großen Teil des möglichen Schlüsselraumes aus. In der Tat ist der Sicherheitsverlust hierdurch nicht gravierend. Jedoch können bei der Vorberechnung von RainbowTables so sehr viele mögliche Salt Zeichenketten außgeschlossen werden.

    Wie Bereits erwähnt kostet eine richtiger Zufallsstringgenerator keine 5 Zeilen mehr Code noch 2 Minuten längere Arbeit.

    Ich habe in der Tat nicht geschaut von wann der Artikel ist, jedoch ist dies eine weitere mögliche Verbesserung. Dass es keine Rolle spielt kann man so nicht stehen lassen. 2a ist unter bestimmten Umständen eine fehlerhafte Hashfunktion, 2x ist die Hashfunktion welche gravierende Fehler hat und der 2a Implementierung von vor 5.3.7 entspricht. Neue Hashes sollten immer mit 2y angelegt werden. Wenn sie anderer Meinung sind sollten sie doch php.net den Vorschlag unterbreiten die offizielle Dokumentation zu ändern.

    Balint

    21 Mai 13 at 22:32

  70. Nein es schränkt in diesem Fall die Sicherheit nicht ein, jedoch ist es einfach kein sauberes Programmieren, wenn Aufgaben von Modulen vorgenommen werden, für die sie eigentlich nicht vorgesehen sind. Dies führt zu unübersichtlichem Code welcher aufgrund von Fehlern wieder Sicherheitslücken öffnen kann.

    Ich denke, der hier vorgestellte Vorschlag (mehr ist es ja nicht) ist nicht so komplex, als das er die Sicherheit beeinträchtigen könnte. Ausserdem muss jeder Entwickler selbst entscheiden, wo eine bestimmte Programmlogik hinterlegt ist. Würde ich 3 Entwickler zu dem Thema fragen, bekäme ich sicher auch 4 unterschiedliche Antworten.

    Der Pepper ist durchaus umstritten, allerdings gibt es Szenarien, in denen er sinnvoll ist. Von daher halte ich es für zu einfach, ihn als unsinnig abzutun. In meinen Augen, gibt es keinen Grund, darauf zu verzichten.

    Wie bereits von anderen erwähnt schließen Sie so effektiv einen sehr großen Teil des möglichen Schlüsselraumes aus. In der Tat ist der Sicherheitsverlust hierdurch nicht gravierend. Jedoch können bei der Vorberechnung von RainbowTables so sehr viele mögliche Salt Zeichenketten außgeschlossen werden.

    Na, die Frage ist doch, wie wirkt es sich aus? Ist die Wahrscheinlichkeit, dass ein Salt doppelt ist gegeben. Rechnerisch vielleicht, aber praktisch?

    Wie Bereits erwähnt kostet eine richtiger Zufallsstringgenerator keine 5 Zeilen mehr Code noch 2 Minuten längere Arbeit.

    Ein Befehl reicht schon:

    $salt = substr ( str_shuffle ( str_repeat ( './0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' , 22 ) ) , 0, 22 );

    Aber! Das Hinzufügen einer Konstante macht den Code unnötig komplex, aber ein 5 Zeilen langer Zufallsgenerator nicht? Interessant!

    Neue Hashes sollten immer mit 2y angelegt werden. Wenn sie anderer Meinung sind sollten sie doch php.net den Vorschlag unterbreiten die offizielle Dokumentation zu ändern.

    Nö, muss ich gar nicht, die stimmen nämlich mit mir überein:

    For practical purposes, it does not really matter if you use $2a$ or $2y$ for newly set passwords, as the countermeasure is only triggered on some obscure passwords (not even valid UTF-8) that are unlikely to be seen outside of a deliberate attack (trying to match hashes produced by buggy pre-5.3.7 code).

    http://www.php.net/security/crypt_blowfish.php

    Oliver

    22 Mai 13 at 00:09

  71. Ich denke, der hier vorgestellte Vorschlag (mehr ist es ja nicht) ist nicht so komplex, als das er die Sicherheit beeinträchtigen könnte. Ausserdem muss jeder Entwickler selbst entscheiden, wo eine bestimmte Programmlogik hinterlegt ist. Würde ich 3 Entwickler zu dem Thema fragen, bekäme ich sicher auch 4 unterschiedliche Antworten.

    Das ist überhaupt nicht der Punkt. In dem Fall ist das richtig, ändert aber nichts an der Tatsache das soetwas kein schöner Programmierstil ist und auch gegen alle gängigen Konventionen verstößt. In einer Hashfunktion hat außer dem Passwort und dem Salt nichts etwas zu Suchen und dieser Salt muss auch Zufällig sein, dies ist bei einer Emailadresse nicht gegeben.
    Anscheinend bemängelt eine Vielzahl von Entwicklern, wie dem Kommentarbereich eindeutig zu entnehmen ist, dieses Feature. Nicht die Funktion an sich ist schlecht, sondern die Implementierung.
    Prinzipiell kann ich trotzdem eine neue Emailadresse in der Datenbank speichern, es ist ja nicht so, dass dieser Algorithmus exklusiven Zugriff auf die Datenbank hat.
    Nur bei der Änderung des Passwortes wird eine Emailadresse zum hashen benötigt und der Algorithmus kann ja wohl kaum entscheiden ob dieser aus einer Benutzereingabe stammt oder aus einer Session Variabelen.

    Das ist wirklich nur, damit ich nicht vergessen kann, bei einer Änderung der Mail das Passwort abzufragen.

    Die neue Mail kann man trotzdem in der Datenbank speichern, dies hat dann zufolge, dass sich der Benutzer nicht mehr einloggen kann, da die Emailadresse mit der der Hash berechnet wurde nicht mehr mit der übereinstimmt, die er zum einloggen verwendet hat.
    Erreicht wurde nur, dass man bei der Änderung der Mail den Hash neu berechnen muss.
    Das eingegebene Passwort muss sowieso Validiert werden, sonst würde die Eingabe keinen Sinn ergeben. Wenn es valide ist wird die Mail geändert, sonst nicht.

    Na, die Frage ist doch, wie wirkt es sich aus? Ist die Wahrscheinlichkeit, dass ein Salt doppelt ist gegeben. Rechnerisch vielleicht, aber praktisch?

    Nein eine Kollision ist nicht gemeint, die ist Unwahrscheinlich und wäre auch bei einer sicheren Erzeugung möglich, um dies unnötigerweise auszuschließen müsste man in der Datenbank prüfen ob es schon Vergeben wurde.
    Gemeint ist das weniger als 2^128 RainbowTables berechnet werden müssen, da alle Schlüssel/Salt bei denen ein Zeichen doppelt vorkommt von vornherein ausgeschlossen werden kann. Somit vergibt man einen Teil des Vorteils des Salt. 2^80 ist zwar immernoch sehr sehr viel, jedoch bedeutend weniger als 2^80. Genaugenommen nicht mal ein Bruchteil. In zukunft kann man eventuell solche RainbowTables erstellen.

    Eine ordentliche Lösung wäre:

    function generateSalt($length = 22)
    {
    // Check to see if OpenSSL libraries
    if (function_exists(‘openssl_random_pseudo_bytes’)) {
    return bin2hex(openssl_random_pseudo_bytes($length));
    }
    // Use less-secure salt-generation method.
    else {
    error_log(‘php-scrypt warning: OpenSSL not installed!’);
    $salt = ”;
    $chars = ‘abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#%&*?’;
    $num = strlen($chars) – 1;
    for ($i = 0; $i kleiner $length; $i++) {
    $salt .= $chars[mt_rand(0, $num)];
    }
    return $salt;
    }
    }
    Das Hinzufügen einer Konstante macht den Code nich unnötig komplex, beim Pepper geht es um Sinn oder Unsinn.

    Nö, muss ich gar nicht, die stimmen nämlich mit mir überein:

    For practical purposes, it does not really matter if you use $2a$ or $2y$ for newly set passwords, as the countermeasure is only triggered on some obscure passwords (not even valid UTF-8) that are unlikely to be seen outside of a deliberate attack (trying to match hashes produced by buggy pre-5.3.7 code).

    Dort steht das es nicht wirklich wichtig ist aber, dass ein Unterschied vorhanden ist.

    Balint

    22 Mai 13 at 01:27

  72. In einer Hashfunktion hat außer dem Passwort und dem Salt nichts etwas zu Suchen und dieser Salt muss auch Zufällig sein, dies ist bei einer Emailadresse nicht gegeben.

    Also erst einmal muss ein Salt einmalig sein, woraus der besteht, ist zweitrangig, da der Salt nicht geheim ist.

    Anscheinend bemängelt eine Vielzahl von Entwicklern, wie dem Kommentarbereich eindeutig zu entnehmen ist, dieses Feature.

    Ich habe zwei gezählt, aber ok, die Alternative habe ich ja in die Kommentare geschrieben.

    http://www.phpgangsta.de/schoener-hashen-mit-bcrypt#comment-51878

    Die neue Mail kann man trotzdem in der Datenbank speichern, dies hat dann zufolge, dass sich der Benutzer nicht mehr einloggen kann, da die Emailadresse mit der der Hash berechnet wurde nicht mehr mit der übereinstimmt, die er zum einloggen verwendet hat.

    Genau das war ja auch der Sinn der Sache. ;-) Aber wie gesagt, das ist (m)eine Version. Die Alternative findet sich in den Kommentaren.

    Gemeint ist das weniger als 2^128 RainbowTables berechnet werden müssen, da alle Schlüssel/Salt bei denen ein Zeichen doppelt vorkommt von vornherein ausgeschlossen werden kann.

    Es sind immer mehr als 2^128 (woher kommt die Zahl?) … wie lange dauert die Berechnung einer Regenbogentabelle bis 8 Zeichen in bcrypt bei fast utopischen 10.000 Passwörtern pro Sekunde? So knapp 400 Jahre etwa, richtig? Und das mal ~10^35 als realistische Option? Echt? Aber wenn Zeichen doppelt vorkommen können und man daher 10^39 Tabellen berechnen müsste, wäre es plötzlich sicher?

    Eine ordentliche Lösung wäre

    Okay … möchten Sie da noch mal nachbessern oder soll ich die Funktion sofort in der Luft zerreissen? Übrigens die Klasse aus der das „geliehen“ ist, benutzt auch pepper. Nur mal so angemerkt. Der Pull Request bei Github zeigt auch sehr schön, dass der Verfasser (Sie?) dieser Funktion absolut keine Ahnung hat, was er da tut und warum er es tut.

    https://github.com/DomBlack/php-scrypt/pull/7

    Oliver

    22 Mai 13 at 03:48

  73. Also erst einmal muss ein Salt einmalig sein, woraus der besteht, ist zweitrangig, da der Salt nicht geheim ist.

    Okay … möchten Sie da noch mal nachbessern oder soll ich die Funktion sofort in der Luft zerreissen? Übrigens die Klasse aus der das „geliehen“ ist, benutzt auch pepper. Nur mal so angemerkt. Der Pull Request bei Github zeigt auch sehr schön, dass der Verfasser (Sie?) dieser Funktion absolut keine Ahnung hat, was er da tut und warum er es tut.

    Sie Wiedersprechen sich selbst. Sie bemängeln die Cryptografische Sicherheit von mt_rand() welche zugegeben nicht sehr hoch ist, daher wird ja auch wenn verfügbar die sichere Alternative genutzt. Nutzen selbst den Cryptografisch noch unsicheren Zufallsgenerator str_shuffle() welcher auf rand() basiert zusammen mit statischen Informationen.

    http://stackoverflow.com/questions/14079703/str-shuffle-and-randomness

    Die bessere Alternative ist Ihnen nicht Gut genug, selbst nutzen Sie jedoch eine Bedeutend schlechtere. Entscheiden Sie sich mal.

    Balint

    22 Mai 13 at 06:20

  74. Sie Wiedersprechen sich selbst. Sie bemängeln die Cryptografische Sicherheit von mt_rand() welche zugegeben nicht sehr hoch ist

    Ok, also bis vor ein paar Minuten habe ich die Kritik wirklich noch Ernst genommen. :-) Das Problem mit zufälligen und pseudozufälligen Generatoren ist folgendes:

    - Bei den (echt)zufälligen kann man den Wert nicht voraus sagen.
    - Bei den pseudozufälligen kann man die Werte mit einer gewissen Wahrscheinlichkeit voraus sagen (unter bestimmten Bedingungen)

    Pseudozufällige Generatoren sollten nicht für kryptografische Werte eingesetzt werden, soweit richtig. Aber ein Salt ist kein kryptografischer Wert. Kryptografie kommt von geheim. Ist der Salt geheim? Nein, er steht ja in der Datenbank. Es ist also völlig egal, wo der Wert her kommt, so lang er einmalig ist. Dafür reichen pseudozufällige Werte völlig aus. Wenn ein Angreifer den Salt voraus sagen kann, kann er damit das Passwort voraus bestimmen? Nee, oder? Wenn ein Angreifer das Passwort auf einen unbekannten Wert zurück setzt, der mit einem bekannten Salt gehasht ist, hat er dadurch irgendwas gewonnen? Nee, oder?

    Die bessere Alternative ist Ihnen nicht Gut genug, selbst nutzen Sie jedoch eine Bedeutend schlechtere. Entscheiden Sie sich mal.

    Ok, versuchen wir die „bessere“ Alternative mal:

    - generateSalt(22)

    Wenn ich openssl installiert habe, bekomme ich einen 44 stelligen hexadezimalen Wert, von dem die Hälfte der Zeichen weg fällt, richtig? Der Zeichenraum besteht dann nur noch von 0 bis f, statt aus dem base64 Zeichenraum. Ihre Funktion reduziert damit die Summe der Möglichkeiten von 10^35 auf ~10^26 um fast 1/3. Das ist die bessere Alternative? Die Möglichkeiten würden sicher noch ausreichen, aber es ging ja um eine „Verbesserung“. Wo ist die Verbesserung?

    Ok, sagen wir, ich habe openssl nicht installiert, dann erzeuge ich mit mt_rand einen Wert aus dem angebenem Zeichenraum, sagen wir „Y#Vix3DSa&AHeE9oJlgPrR“. Wissen Sie, was mir bcrypt zurück gibt, wenn ich das als Salt angebe? Richtig *0 → Fehler. Sobald nämlich nur ein Zeichen nicht aus dem base64 Zeichenraum besteht, gibt die crypt Funktion nur einen Fehler zurück.

    Das ist also die bessere Alternative? Eine Alternative, in der ich entweder den Zeichensatz massiv reduziere oder ca. 20 % der Werte einfach alles zulasse?

    Oliver

    22 Mai 13 at 11:58

  75. Danke für den Artikel und auch Danke für die tolle Diskussion, auch wenn es teils echt anstrengend war, sie komplett zu lesen. Einige Ansätze fand ich schon gerechtfertigt und zum nachdenken hervorragend, was mir jedoch komplett fehlt sind Fakten. Es wird immer nur gesagt, es gibt bessere Variante, es gibt Libraries und es gibt fertige/bestehende Funktionen. ABER nicht ein einziger Name fällt oder wird erwähnt. Ich erwarte wirklich keine kompletten Anleitungen oder Beschreibungen, aber wenigstens ein Name, nachdem man dann googlen kann, wäre echt was feines. Ich wollte mich die ganze Zeit nun schon mit dem Thema sicherere Passwörter in einer Applikation beschäftigen und nun muss/darf ich es endlich machen. Ich wollte weg von md5 und wusste, es gibt sha1, wusste aber auch, dass es auch nicht mehr so sicher ist und musste hier an sha512 denken. Das dies aber auch nicht gerade das gelbe vom Ei ist und der Weg im Grunde anders bestritten werden muss, habe ich in diesem Artikel gelernt und dafür danke ich!

    Wie gesagt, fand ich einige Ansätze auch gut und einige Sachen im Code hätte ich von vornherein auch etwas anders gemacht. Teils wurden diese Punkte in den Kommentaren auch angesprochen und auch Ausbesserungsvorschläge gegeben. Fand ich gut. Was mich da aber viel mehr interessiert, da es ja teils auch um die Performancefrage ging, welche der Varianten ist die bessere?

    Ich will jetzt gar nicht darüber diskutieren, welcher Salt nun sinnvoll ist oder nicht, das ist im Grunde egal, mich interessiert hier eher, wie optimiere ich die Performance zur Erzeugung dieser. Darüber hat irgendwie niemand ein Wort verloren, immer nur über den “work factor”.

    substr(str_shuffle()),
    substr(str_shuffle(str_pad())),
    for()

    oder gar eine komplett andere Funktion, die vielleicht sogar etwas fertiges zurück gibt. Warum versucht man nicht hier schon einen Optimierungsansatz? Ok der Artikel ist im Grunde ja auch nur ein Beispiel, wie man es machen kann, doch ich finde gerade die Kommentare sind dafür perfekt, hier zusammen eventuell die perfekte Lösung zu ermitteln und zu erstellen. Oder?

    Ansonsten versuche ich diese Ansätze nun erst mal selber in einigen Tests zu nutzen und sage nochmal danke für den Artikel :)

    maXus

    16 Aug 13 at 16:36

  76. Ich hoffe, dass möglichst viele Kommentare, die bestätigen, dass dies keine gute Lösung ist, helfen, zu verhindern, dass unwissende Benutzer dies so implementieren.

    Oliver wird sich sowieso nur drehen und wenden und auf stur schalten, also hoffe ich einfach, dass die Masse der Kommentare dominiert und unwissende Benutzer abschreckt:

    1. /dev/random verwendet Maus, Keyboard, Sound, Gerätetreiberrauschen, CPU-Temperatur, Spannungsschwankungen uvm. und ist damit true random, nicht pseudo wie Oliver behauptet.

    Quelle: http://en.wikipedia.org/wiki//dev/random

    2. Dein Argument “Zeige mir eine Schwäche”:

    Du bist nicht der erste, der die Email-Adresse mit hashen möchte, das ist immer die erste Frage der Studenten in den Security-Vorlesungen. Und irgendwann verstehen sie auch, warum es nichts bringt und warum sie es nie in einen Standard geschafft hat:

    Sie bringt keinen Sicherheitsvorteil.

    und:

    Alles, was keinen Sicherheitsvorteil bringt in diesem Bereich, ist Verschwendung von Ressourcen und Verschwendung von Zeit bei der Implementierung, die man dazu verwenden könnte, wirklich die Sicherheit zu erhöhen.

    3. Von 100 Leute, die selbst Kryptografie implementieren, macht es nur einer richtig. Selbst wenn du es hier also richtig erklären würdest, würden 99/100 einen Fehler einbauen, du verursachst also, dass andere unsichere Webseiten haben.

    4. Nutzt fertige Security-Libraries, die sich etabliert haben. So eine Library kommt raus und wird jahrelang überarbeitet und diskutiert. Ein einzelner Mensch kann sowas niemals schaffen. Ja, Oliver, du kannst deinen Algo auf github laden, da wird er aber dahinvegetieren und das weißt du auch, du stellst dich nur stur. Denn alleine hier hast du ja schon 5 Kommentare, die bestätigen, dass es schlecht ist, was du tust, nehme es doch einfach mal an und versuche an dir zu arbeiten!

    Greg

    19 Nov 13 at 16:59

  77. Mimimimimi! /dev/random ist pseudozufällig. Steht sogar in dem Link, den Du gepostet hast. Warum ich das mit den E-Mails mache, hab ich doch erklärt, oder? Machen muss man das nicht, Sinn ergibt es trotdem. Wenn ich immer auf das was geben würde, was irgendwer, irgendwann im Internet geschrieben hat, wäre ich wohl ständig mit Unwichtigem beschäftigt. Ansonsten mimimimimi (ohne Argumente).

    Oliver

    19 Nov 13 at 17:56

  78. zu 2) Das es keinen Vorteil bringt, hat er ja nun auch schon gesagt, jedoch auch keinen Nachteil. Weder in Zeit noch Ressourcen (ok evtl. ein wenig beim letzteren).

    zu 3) Das er hilft unsichere Websites zu generieren, halte ich für etwas sehr schnell geschossen. Immerhin regt er zum Denken an und hilft mit einem Weg weg vom md5 und sha1, den noch immer so gut wie ALLE Scripte nutzen.

    zu 4) Hast du ein Beispiel, welche sich etabliert haben, damit man sich da mal einarbeiten/lesen kann? Würde mich jetzt schon interessieren. Danach googlen kann ich zwar, ob sich die Ergebnisse dann aber etabliert haben, kann ich dann sicherlich aber nicht wissen. Du scheinst dich ja mit dem Thema auszukennen und daher würde ich jetzt mal auf einen Tipp von dir hoffen. Gibt es von dir zu dem Thema etwas zu lesen?

    Von deinen hier 5 erwähnten Kommentaren, welche die Unsicherheit bestätigen, finde ich jedoch keinen. Ich sehe nur Behauptungen. Keine Nachweise oder Quellen. Hoffe habe die Kommentare noch alle richtig im Kopf.

    Allgemein:
    Nach den letzten Meldungen von Adobe, mache ich mir auch aktuell wieder vermehrt Gedanken dazu, wie man Daten in der Datenbank am besten verschlüsselt. Eventuell hast du ja auch dafür einen Tipp.

    maXus

    19 Nov 13 at 19:04

  79. Warum substr($stored,0,30) und nicht 29?

    (unabhängig von der Tatsache, dass der Rest der Zeichen wohl eh verworfen wird und man substr() gar nicht benötigt)

    $2a = 3 Zeichen
    $08 = 3 Zeichen
    $ = 1 Zeichen + 22 Zeichen (21 1/2)
    ergibt 29 Zeichen nicht 30.

    Haben ich einen Denkfehler oder ist es egal?

    HerrZ

    29 Nov 13 at 09:00

  80. Oli du bist süß, 34 Jahre und diskutierst wie ein kleines Kind. Allerdings sind die, die vergeblich versuchen, dich eines besseren zu belehren auch nicht gerade erwachsener.

    Manu

    11 Mrz 14 at 02:36

  81. Ja, beim letzten Kommentar war ich auch etwas genervt, weil da auch wirklich alles falsch war, was mein Vorredner von sich gegeben hat. Meistens atme ich erst mal durch, bevor ich etwas schreibe. ;-)

    Oliver

    11 Mrz 14 at 02:58

  82. “Jetzt kommen wir zu dem Teil, wo bcrypt von nett, auf cool wechselt.”

    YMMD!

    RaveKev

    14 Mrz 14 at 07:59

  83. […] […]

  84. Wow, that’s what I was exploring for, what a material!
    present here at this web site, thanks admin of this web page.

    Albertha

    19 Apr 14 at 04:18

Leave a Reply

You can add images to your comment by clicking here.