php crypt
Benz
- php
Hi,
Ich habe für meinen Chor einen internen Bereich programmiert bzw. bin dabei.
Ich hatte das erst mit htacces usw. gelöst, mit verschlüsselten (crypt) Paswörtern.
Jetzt würde ich aber gerne auf Sessions umsteigen, und frage mich, wie ich die Passwörter, die beim Login übergeben werden, mit den verschlüsselten Passwörtern aus der DB auf Gleichheit überprüfen kann.
Wenn ich den selben String zweimal crypt verschlüssele, kommen ja zwei unterschiedliche Strings raus.
Bräuchte also einen Tipp, wie ich das Serververhalten bei htaccess-Lösung in PHP nachahmen kann.
Grüsse
Jens
Moin!
Ich hatte das erst mit htacces usw. gelöst, mit verschlüsselten (crypt) Paswörtern.
Jetzt würde ich aber gerne auf Sessions umsteigen, und frage mich, wie ich die Passwörter, die beim Login übergeben werden, mit den verschlüsselten Passwörtern aus der DB auf Gleichheit überprüfen kann.
Wenn ich den selben String zweimal crypt verschlüssele, kommen ja zwei unterschiedliche Strings raus.
crypt() ist nicht wirklich eine ideale Lösung. Es ist systemabhängig, welcher Mechanismus dadurch zum Einsatz kommt, dein System wäre also nicht beliebig portabel, und der standardmäßige Mechanismus erlaubt nur Passworte mit 8 Zeichen Länge - das ist durch heutige Computer durchaus knackbar.
Das "Salz" des jeweiligen Crypt-Vorgangs steckt übrigens in den ersten Zeichen des Crypt-Resultats, und die übliche Vorgehensweise ist, zur Herstellung des neuen Crypt-Wertes einfach das Klartextpasswort und den gespeicherten Crypt-Wert der Funktion zu übergeben.
Bräuchte also einen Tipp, wie ich das Serververhalten bei htaccess-Lösung in PHP nachahmen kann.
Der wichtigste Tipp wäre, von crypt wegzugehen. MD5 oder SHA1 sind schönere Alternativen, die durch Hinzufügen einer Salz-Komponente noch viel schöner werden.
- Sven Rautenberg
Hi,
ich möchte jetzt eigentlich verhindern, dass alle bereits angemeldeten User sich nochmals neu anmelden müssen.
Das "Salz" des jeweiligen Crypt-Vorgangs steckt übrigens in den ersten Zeichen des Crypt-Resultats, und die übliche Vorgehensweise ist, zur Herstellung des neuen Crypt-Wertes einfach das Klartextpasswort und den gespeicherten Crypt-Wert der Funktion zu übergeben.
Welche "Funktion" meinst du?
Also ich habe hier Passworte, die allesamt deutlich länger als 8 Zeichen sind, z.B folgendes:
$1$2C6Q0Qkx$BHWpm2Yrv/qQId405EcpZ.
Wie kann ich daraus jetzt das "Salz" erkennen bzw. wie kann ich es auf Gleichheit mit Klartext-String xy prüfen?
Grüße,
Jens
Hallo Jens,
Also ich habe hier Passworte, die allesamt deutlich länger als 8 Zeichen sind, z.B folgendes:
$1$2C6Q0Qkx$BHWpm2Yrv/qQId405EcpZ.
Wie kann ich daraus jetzt das "Salz" erkennen bzw. wie kann ich es auf Gleichheit mit Klartext-String xy prüfen?
1. Ändere das Passwort bitte mal, jetzt wo's hier veröffentlicht wird. Man kann es sicher nicht SOFORT knacken, aber wenn es nicht sicher genug ist: Brute Force ist bei hinreichend kurzen Passwörtern bei der heutigen Rechenleistung _verdammt_ schnell. ;-)
2. crypt() ist eine Standard-POSIX-Funktion zum hashen von Passwörtern. crypt() arbeitet immer mit "Salts", d.h. einer zufälligen Zeichenkette. Dies wird gemacht, um zu verhindern, dass Leute einfach für alle möglichen Passwörter Hashes vorherberechnen können. Wenn man z.B. nur eine normale Hash-Funktion wie MD5() nimmt und dann gehashtes_passwort = MD5(passwort) macht, dann ist das nicht sehr sicher, weil viele Leute sich inzwischen sogenannte "Rainbow-Tabellen" angelegt haben, die MD5-hashes für eine MENGE Passwörter vorberechnet erhalten. Man muss dann nur sehen, ob der Hash in der eigenen Tabelle enthalten ist und wenn ja, kennt man das Passwort.
Salts arbeiten so:
gehashtes_password = salt + HASH(salt + passwort)
Das heißt: Vor dem Verwenden der Hashfunktion (hier HASH genannt), wird vor das Passwort der Inhalt von salt gehängt, salt muss zufällig gewählt sein (für jedes Passwort neu). Um das ganze wiederherstellen zu können, wird das salt zusätzlich noch VOR das gehashte Passwort gehängt.
Es gibt nun 2 mögliche Implementierungen von crypt() in aktuellen UNIX-artigen Betriebsystemen:
1. Triple DES
Das ist der klassische crypt()-Algorithmus, der i.A. nur unter UNIX-
artigen Betriebssystemen zur Verfügung steht. Er benutzt einen Salt,
der 2 Zeichen lang ist und das Passwort darf maximal 8 Zeichen lang
sein (der Rest ist abgeschnitten). Dafür kann jedes Passwort einein-
deutig auf einen Hash abgebildet werden (bei gegebenem Salt), d.h.
zwei unterschiedliche Passwörter bei gleichem Salt verursachen zwei
unterschiedliche Hashes (das KANN bei MD5 schon gar nicht garantiert
werden weil man in MD5 ja beliebig lange Daten reinstopfen kann,
hinterher aber nur Daten einer festen Länge wieder herauskommen).
Es gibt auch ANSI-C-Implementierungen des Algorithmus und ich hatte
vor Ewigkeiten irgendwann sogar mal ein kleines Windows-Programm
geschrieben gehabt, womit man auch unter Windows das traditionelle
crypt() verwenden kann. Großer Nachteil: Die ganze andere Software
(Apache, PHP, ...) baut den Algorithmus unter Windows nicht nach und
kann den dagegen nicht verwenden (unter POSIX verwenden sie halt die
Betriebssystemfunktion).
Der Salt ist idR. 2 Zeichen lang, es gibt auch eine Variation, bei der
der Salt 9 Zeichen lang ist.
2. MD5-basiert
Hier wird als Hash-Algorithmus MD5 verwendet und der Salt ist 8
Zeichen lang. In dem Fall (zur Unterscheidung) fängt der Hash dann
mit $1$ an, gefolgt von dem Salt, gefolgt von einem weiteren $, gefolgt
von dem eigentlichen Hash. Dies ist der Fall des obigen Passworts.
[diverse andere Software hat das noch irgendwie erweitert; PHP bietet auf einigen Systemen noch $2a$ an für Blowfish als Hash-Algo, was aber m.W. nicht standardisiert ist, Apache verwendet nicht $1$ sondern $apr1$ für MD5, $apr2$ für SHA1]
Da Dein obiges Passwort ein MD5-basierter crypt-Hash ist, kannst Du in PHP einfach folgendes machen:
if ($gehashtes_passwort == crypt ($eingegebenes_passwort, $gehashtes_passwort)) {
# Zugang gewährt
} else {
# Zugang verweigert
}
Wie funktioniert das? Man muss crypt() ja sagen können, welchen Salt man beim vorherigen Passwort verwendet hat, damit man den Hash rekonstruieren kann. Daher müsste man strenggenommen '$1$altersalt$' als 2. Parameter an crypt übergeben (oder halt die 2 Zeichen für 3DES). Da das aber unpraktisch wäre, akzeptiert crypt() einfach das gesamte alte Passwort und verwendet automatisch nur den Salt-Teil wieder.
Wenn Du dieses Verfahren weiter verwenden willst (MD5 + Salt ist nicht schlecht, SHA1 oder etwas anderes wäre natürlich besser, das geht aber mit crypt() nicht [1]), dann musst Du bei _neuen_ (!) Passwörtern dafür sorgen, dass immer MD5 als Hash-Algorithmus genommen wird, d.h.
$neues_gehashtes_passwort = crypt ($eingegebenes_neues_passwort, $salt);
Wobei $salt gerade sowas wäre wie
$salt = '$1$'.$acht_zufaellige_zeichen.'$';
Wobei die acht zufälligen Zeichen aus [a-zA-Z0-9./] sein sollten und vor allem zufällig sein müssen, aber ruhig berechenbar sein dürfen. Wenn Du also einen brauchbaren Salt mit PHP erzeugen willst, kannst Du z.B. nutzen:
function generate_salt ($len = 8) {
static $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./';
$salt = '';
for ($i = 0; $i < $len; $i++) {
$salt .= $chars[mt_rand (0, 63)];
}
return $salt;
}
(Alternativ auch rand() statt mt_rand() - aber da die Vorhersagbarkeit des Zufallszahlgenerators für diese Problemstellung keine Rolle spielt, ist das eigentlich relativ egal, was man nimmt (MT ist kryptographisch nicht sicher, rand ist aber auf einigen älteren Systemen noch sehr viel schlechter als MT, dafür auf neueren Systemen _deutlich_ besser))
Zusammengefasst nochmal:
Altes Passwort überprüfen:
if ($gehashtes_passwort == crypt ($eingegebenes_passwort, $gehashtes_passwort)) {
# Zugang gewährt
} else {
# Zugang verweigert
}
Neues Passwort hashen:
$neues_gehashtes_passwort = crypt ($eingegebenes_neues_passwort, '$1$' . generate_salt () . '$');
Übrigens, Du kannst Dir evtl. auch mal http://www.openwall.com/phpass/ anschauen, wenn Du Dich für Passwort Hashing in PHP interessierst.
Viele Grüße,
Christian
[1] Gut, Blowfish ginge noch, wenn das auf Deinem System verfügbar wäre. Wenn es das ist, würde ich das MD5 vorziehen, allerdings ist das Verfahren etwas komplizierter...
wow,
der absolute Hammer, dein Beitrag. Ich will nicht behaupten schon durch zu sein, aber muss mich mal im Vorraus bedanken. Total interessant!
Ach, das mit dem Passwort spielt keine Rolle, das darf ruhig jeder knacken wie er will ;)
Jetzt ist aber folgendes ein wenig komisch:
crypt ($korrektes_passwort_klartext,$hash_aus_DB) =
$1$mX3VG01c$xJ2J3g2gB81/By8fNZQJV.
Also nicht das obige Passwort. Woran könnte das liegen? Ich habe noch festgestellt, dass du gemeint hast, Apache würde bei MD5 nicht $1$, sondern $apr1$ voranstellen. Nun ja, die Passwörter WERDEN unter Apache gehasht.
Komisch ne? Hoffe du kannst mir noch einen kurzen Tipp geben :-)
Okay,
ich hab die Lösung, das mit crypt($passwort, $kompletter_hash) hat (zumindest bei mir) einfach nicht geklappt. Ich habe jetzt
[CODE]
$salt = explode("$", $hash);
$salt = "$".$salt[1]."$".$salt[2]."$";
if (crypt($password,$salt) == $hash) {
session_start;
echo "geklappt";
}
else {
echo "nicht geklappt";
}
[/CODE]
und das funktioniert wunderbar
Hallo Sven,
Der wichtigste Tipp wäre, von crypt wegzugehen. MD5 oder SHA1 sind schönere Alternativen, die durch Hinzufügen einer Salz-Komponente noch viel schöner werden.
MD5 und SHA1 OHNE Salt sind auf Grund von Rainbow Tables keine bessere Idee als 3DES-crypt. Mit Salt bieten sie den Vorteil, dass sie längere Passwörter zulassen. Im Gegensatz zu MD5 gibt es jedoch AUSSER Brute Force auf 3DES keinen bekannten Angriff - sprich: 3DES ist gar nicht SO schlecht, wie die Leute immer tun.
Daher:
MD5 + Salt ist (noch) brauchbar heutzutage.
SHA1 + Salt ist eine bessere Idee.
Blowfish + Salt (wie OpenBSD es vormacht) ist eine noch bessere Idee.
(MD5 ist halt eher verfügbar, als SHA1 oder gar Blowfish...)
Viele Grüße,
Christian