PHP Sicheres Login (Manipulation von $_SESSION)

te one

Lt. Commander
Registriert
Apr. 2009
Beiträge
1.255
Hallo,
da ich mal wieder richtig Lust auf PHP habe, habe ich gestern ein neues Projekt angefangen.
Ein Login hab ich schonmal programmiert, allerdings möchte ich diesmal alles richtig machen.
Deshalb überlege ich was ich vom Nutzer im $_SESSION-Array speichere!(Bin mir nicht sicher inwieweit sich das auch Manipulieren lässt, da es ja i-wo im Browser des Nutzers gespeichert werden muss)
Auf jeden Fall sollte ja mal die User-ID gespeichert werden. Doch welche der 5 Methoden soll ich neben der User-ID nutzen (oder ist die schon sicher genug? Wobei ich das nicht glaube)?
Hier mal meine Ideen:

PHP:
//1. Methode, einfach speichern dass er eingeloggt ist
$_SESSION['logedin']=1;

//2. Methode, Passwort im Klartext
$_SESSION['password']=$password;

//3. Methode, Passwort-Hash
$_SESSION['password']=md5($password);

//4. Methode, Zufällige Zahl, die bei jedem Login geändert wird und in der Datenbank steht
$_SESSION['number']=$Nummer_aus_DB;

//5. Methode, Zufällige Zahl, die bei jedem Login geändert wird und in der Datenbank steht
//Diesmal hat aber das Array-Element selbst diese Nummer
//Alternativ könnte natürlich das Element diese Zahl haben und der Wert des Elementes auch die Zahl sein
$_SESSION['$Nummer_aus_DB']=1);

Wäre super wenn ihr mir sagen könntet ob die normalen Varianten reichen oder ob es Methode 4 bzw. 5 sein sollte.
Möglicherweiße weiß ja auch einer noch was viel besseres!

Vielen Dank

mfg
Michael
 
Ich mache es so, dass ich allgemein einen Loginflag speichere (so wie bei dir logedin) und die UserID und wenn ich Userdaten brauche bezieh ich die über die ID aus ner DB Abfrage. Komplette Userdatensätze würde ich auch nicht unbedingt in der Session speichern, da es zwar nicht alszu wahrscheinlich ist, aber es schon vorkommen kann dass Session Daten leaken, gerade wenn mehrere Projekte auf einem Server betrieben werden.
 
Die Daten zur Weiterverarbeitung würde ich bei 1. - 5. aus der Datenbank nehmen.
Das Speichern des Passwortes (als beispiel wie in 2. und 3.) würde ich lediglich dazu nehmen, nochmal das Session-PW mit der Datenbank abzugleichen (halt dann auf jeder seite).

Jemand eine Ahnung wie leicht sich die Session-Variablen verändern lassen?? Oder liegen die garnet beim User?
Denn wenn die beim User liegen, würde ich sagen, sollte auf jeden Fall eine Prüfung per zufälliger Zeichenfolge (wird bei jedem login geändert) erfolgen.
Wenn die Zeichenfolge stimmt, dann könnte man auch nen flag setzen (wie logedin=1), welches allerdings zB nur 1 Minute gültig ist.
Surft der Benutzer länger als 1 Minuten auf der Seite wird erneut die Zeichenfolge überprüft und das Flag gesetzt.
Ich denke das wäre eine optimale Lösung.
Maximale Sicherheit durch ändernte Zeichenfolge.
Minimale Serverbelastung da nur jede Minute (in der der User auch aktiv ist) eine Anfrage an die DB gesendet wird.

Natürlich kann einer dann auch wieder das logedin manipulieren...Glaube für maximale Sicherheit kommt man um die Zahlenfolge nicht herum...
 
Session Variablen liegen am Server. Am Client wird meistens nur ein Cookie gesetzt, dass die Session ID beinhaltet (oder die Session ID wird direkt in die URLs kodiert). Der Client sendet die Session ID in jeder Anfrage an den Server und der Server verwendet die Session ID um die für den Benutzer passenden Werte in die $_SESSION Variable zu laden.

-> Der Client kann nur die Session ID manipulieren, und die ist deshalb standardmässig eine lange, zufällige Zeichenfolge, damit man nicht die Session ID eines anderen Benutzers erraten kann.
 
Zuletzt bearbeitet:
Ich benutze gar keine $_SESSION. Im Grunde speicher ich eine UniqueID in einem Cookie. Eine zweite UniqueID nutze ich als Session-ID im Cookie, diese ID wird bei JEDEM Seitenaufruf aktualisiert. Somit verhindere ich (oder erschwere es zumindest im hohen Maß), dass jemand, der zufällig oder absichtlich an die Sessiondaten gekommen ist, sich als diesen User einloggen kann. Und wenn der Angreifer des doch schafft, greift ein zweiter Sicherheitsmechanismus, der überprüft wann sich die IP und/oder Browser geändert hat und fragt erneut nach dem Passwort, wenn der User sicherheitskritische Funktionen nutzen will. Wobei ich letzteres halt noch einbauen muss...^^

In der Datenbank verknüpfe ich diese beiden UniqueIDs mit den benötigten Sessiondaten, unter Anderem die gehashte IP und die Browserkennung, sodass ich die Möglichkeit hab eine Session sicherer, und extrem sicher zu machen (binden der Session an die IP, z.B. für Adminaccounts).

Ganz wichtig: Ich speichere _überhaupt_ keine Userdaten beim User selbst (ganz fatal sind beim User abgelegte Loginnamen/User-ID UND Passwörter, egal ob gehasht oder nicht! Kommste an die Daten ran biste potentiell recht fix eingeloggt!) und auch nicht in einer $_SESSION-Variable, da das wirklich potentielle Lücken sind, die man schwer kontrollieren kann. Und die Daten, die ich brauche, stehen nur bei der aktuellen Laufzeit des Scripts zur Verfügung und werden grundsätzlich bei jedem Seitenaufruf aus der Datenbank gelesen, allerdings stehen die häufig benötigten Daten als serialisierte Info bereit, sodass ich keine komplizierten Queries dafür brauche. Für den Fall, dass in dieser serialisierten Info irgendwas geändert werden muss, wird die Session schlicht und ergreifend vollständig neu erstellt (der User bekommt davon aber nix mit).

Mein Weg mag zwar kompliziert sein und etwas mehr Ressourcen benötigen als die simple $_SESSION-Methode, aber ich denke dass des eine recht sichere Methode ist, die recht schwer zu knacken sein wird...
 
@TchiboMann:
Na die Lösung klingt mal sicher.

@firesnow:
Naja wenn das schon so sicher implemetiert ist, wird es wohl kaum nötig sein, soetwas wie TchiboMann zu basteln.

Ich denke ich werde dann einfach nur ein logedin-Flag setzen.

Zusätzlich werde ich wahrscheinlich in der DB fehlgeschlagene Login-Versuche und falsche, unmögliche SESSION-Variablen mitloggen und bei zu vielen Fehlern den User sperren.
Dann bekommt er einfach ne Mail und kann sich wieder entsperren (und da der Hacker keinen Zugriff auf die Mail hat, hat er verloren).

Für den Fall dass sich einer mehrmals sperrt, werde ich eine IP-Blacklist einbauen, spätestens dann ist Schluss mit lustig.

Ich hoffe mal das bring ich alles hin, aber ich denke das klappt schon (Blacklist hab ich zB schonmal gemacht).

Vielen Dank für die vielen Antworten bis hierher, falls einer noch mehr weiß, bitte posten.

Ps: Auch hier sei gesagt: Wer nen gutes Tutorial für objektorientiertes PHP hat, bitte Link schicken. Hab mir schon so viele Tuts durchgelesen, aber bring komplizierte Sachen nur prozedural hin :rolleyes:


edit: Achja, weils mir gerade einfällt: Ich habe noch nie in MySQL Felder verknüpft. Ich hab die Funktion gerade eben in phpmyadmin gefunden.
Ist es dann so dass ich nicht erst durch ne DB-Abfrage die User-ID und dann durch ne neue Abfrage den Namen (der Beispielsweise in ner eigenen Tabelle steht) zur ID abfragen muss? Kann ich dann direkt aus der Tabelle mit der User-ID auch den Namen ausgeben lassen, obwohl der in ner eigenen Tabelle ist?

zB:
Code:
SELECT level,name FROM levels WHERE id=1
wobei level in der levels-Tabelle und der name in der users-Tabelle steht (IDs sind verknüpft)
 
Zuletzt bearbeitet:
Du meinst einen JOIN? Wenn ich das richtig verstanden habe?
Code:
SELECT levels.level, user.name FROM levels levels LEFT JOIN user user ON (levels.id = user.id)

Zum anderen Thema: Session Hijacking

Zum hashen von Variablen: Generell finde ich, wenn man schon so großartig auf Sicherheit achtet, das reine verwenden von md5 als zu schwach, da schon extrem große Rainbow Tables existieren. Ich verwende zB immer md5(sha1(md5())), da dies Voraussetzen würde, das sowohl der md5 als auch der sha1 Algorithmus schon komplett geknackt wurde (was wohl in absehbarer Zeit nicht passieren wird)

Generell sollte man sich immer überlegen, wieviel Sicherheit man in einer privaten Applikation wirklich braucht. Es geht hier ja nicht um Online Banking oder sonstige Sicherheitskritische Anwendungen. Das setzen von einem Session Hash ist idR schon sehr sicher, wenn nicht eben andere Sicherheitslücken (zB XSS) existieren. Der Ansatz sollte also eher dahin gehen, Sicherheitslücken, die Session Hijacking (und meist noch ne Menge anderer Dinge ermöglichen) zu entfernen.


so long
Renegade
 
md5(sha1(md5())).
Du bist gut...
md5 gibt 2^128 verschiedene Möglichkeiten zurück, sha1 2^256. Lies dir mal die Comments zu den Funktionen auf php.net durch.
Wenn encrypten, dann nur eine einzige Methode verwenden, und einen Salt direkt einbauen.
Ich machs oft so
Code:
$hash = md5($username.$password.time().microtime());
setcookie("hash",$hash);
$db->query("insert $hash")
Für alle wirklich wichtige Dinge (um z.B. mehr als die Session zu übernehmen wie Email oder Passwort ändern). Frag ich jedes mal das Passwort noch ab.
 
TchiboMann schrieb:
Ganz wichtig: Ich speichere _überhaupt_ keine Userdaten beim User selbst (ganz fatal sind beim User abgelegte Loginnamen/User-ID UND Passwörter, egal ob gehasht oder nicht! Kommste an die Daten ran biste potentiell recht fix eingeloggt!)

Die Frage ist dann was du machst, wenn du eine permanente Loginfunktion a la Computerbase (bzw. dem Bulletinboard), Facebook etc. realisieren willst.
Bei meinem Login speichere ich auch nur die Session-ID in einem Cookie beim User ab, überprüfe bei jedem Seitenaufruf aber noch dessen IP-Adresse mit der zuletzt bekannten.

Einen permanenten Login kann ich so aber nicht realisieren. Ist die Session abgelaufen oder aber der Benutzer hat eine neue IP (z.B. wegen Zwangstrennung alle 24h), so ist er ausgeloggt. Zwar kann man PHP-Sessions so konfigurieren, dass sie eine Mindestlebensdauer haben, aber das ist auch keine wirkliche Lösung.

Irgendwas muss man also wohl oder übel beim Benutzer abspeichern, oder nicht? also username und password sind natürlich zu unsicher, auch gehasht würde ich das nicht machen. Ich habe mir überlegt, dass ich für jeden User in der Datenbank noch 2 weitere Spalten habe mit 2 hashartigen Zeichenfolgen, z.B. 2 GUIDs. Die eine soll für den usernamen bzw. userid stehen, die andere für das Passwort. Allerdings gibt es zwischen dem tatsächlichen Usernamen und Passwort keinerlei Verbindung zu diesen 2 Zeichenfolgen, diese sind zufällig generiert und natürlich unique. Diese beiden Zeichenfolgen würde ich nun im Cookie speichern und als Logininformation nutzen, um den Benutzer jedesmal auf der Seite einzuloggen.
Klar, wenn ein Fremder an das Cookie kommen sollte, so kann er sich nach wie vor einloggen. Allerdings wäre aus den Cookiedaten nicht das tatsächliche Passwort des Benutzers ersichtlich, welches er eventuell auch noch auf 500 anderen Seiten verwendet.

Was besseres ist mir noch nicht eingefallen. Weiß jemand, wie das die ganzen PHP-Boards wie vbulletin machen?
 
Zuletzt bearbeitet:
Blitzmerker schrieb:
md5(sha1(md5())).
Du bist gut...
md5 gibt 2^128 verschiedene Möglichkeiten zurück, sha1 2^256. Lies dir mal die Comments zu den Funktionen auf php.net durch.
Wenn encrypten, dann nur eine einzige Methode verwenden, und einen Salt direkt einbauen.
Ich machs oft so
Code:
$hash = md5($username.$password.time().microtime());
setcookie("hash",$hash);
$db->query("insert $hash")
Für alle wirklich wichtige Dinge (um z.B. mehr als die Session zu übernehmen wie Email oder Passwort ändern). Frag ich jedes mal das Passwort noch ab.

Du hörst mir aber auch gar nicht zu oder? :)

Ich hab nicht an der generellen Sicherheit von den Hashalgorithmen gezweifelt (wobei zum Thema Sha1 und md5), sondern nur am Reverse Engineering. Es gibt riesige Tabellen im Internet, in denen ein md5 Hash mit seinem zugehörigen Klartext Ergebnis abgespeichert wurde. Wenn ich jetzt die User rein md5 hashe, hab ich eine sehr hohe Chance, das die generell schwachen Passwörter, die von nichtsahnenden Standardusern verwendet werden, schon in einer md5 Tabelle existieren, ergo hab ich mit dem Hash schon eine sehr hohe Chance auf ein Klartextpasswort.
Im Grunde machst du ja nichts anderes, nur das du aufgrund der Time Variante bei jedem mal erneut hashen musst. mMn ergeben sich daraus zwei generelle Probleme: Erstens, wie speicherst du das Passwort dann in der Datenbank ab? Klartext?!?!?! (siehe Daten auslesen per SQL Injection, was wieder zu "einfachen" Klartextpasswörtern führt), zweitens dürfte es performancetechnisch gesehen aufwändiger sein, bei jedem Vergleich erneut hashen zu müssen (und zwar im Skript wie auch in der Datenbank) als nur ein einziges Mal (abhängig davon wie oft du den Vergleich durchführst)

Zudem, wie die oben verlinkten Links zur Sicherheit der Algorithmen zeigen, sieht man, das fleißig an den Schwächen gesucht wird (und auch schon welche existieren), ergo kann man nicht genau sagen, wann einmal ein Algorithmus komplett geknackt wird. Sollte in deinem Fall zB überraschenderweise md5 geknackt werden, ist es deine Verschlüsselung ebenso, während ich ansonsten zumindest noch den sha1 Polster habe.

Soviel also dazu, einen generellen Nachteil seh ich daher nicht :)


PS: Das abspeichern des Hashes in einem Cookie ist zudem eine weitere Sicherheitslücke. Falls du nur eine PHP Session abspeicherst, ist eine Session Hijacking Attacke möglich, was das temporäre Einsteigen ermöglicht (das tut der Hash auch), mit dem Hash direkt aber kann ich versuchen, das Passwort zu knacken. Diese Möglichkeit hab ich ansonsten bei XSS (und das sind sehr gängige Sicherheitslücken) nicht.

Zusammenfassend bleibt zu sagen: Sicherheit ist ein sehr komplexes und weitschweifiges Thema, also nur Ratschläge geben, wenn man sich dabei wirklich sicher ist was man sagt ;) Und dann bitte auch begründen :)


so long
Renegade
 
Zuletzt bearbeitet:
Wenn du einen entsprechenden Salt benutzt, helfen dir md5-Tabellen auch nicht wieter. Zudem die Passwörter immer in gehashter From in der Relation speichern.

Wie -=Renegade=- schon sagt, versteifft euch nicht auf das Thema Login. Größere Risiken stellen da Injection-Angriffe dar. Daher mein eindringlicher Rat, benutzt Preparred Statements zur Kommunikation mit der Datenbank.
Thema Cross-Site-Scripting. Alle Userdaten maskieren, escapen und validieren.
Cross-Site Request Forgery. Unique-Ids zur Formularauthentifizierung vergeben.

Dies sind nur ein paar Beispiele.

Gruss
 
renegade:
Hast du die Kommentare durchgelesen? (Dort wird klar ein md5|sha1 mit Salt empfohlen).
mal wird schneller zu md5(sha1(md5())) ne Rainbowtable machen als zu md5() alleine.

Gedankenexpreiment:
md5 macht aus dem string ein Kommawert zwischen 0 und 1 (also wie zahl/max_number).
Bei meiner Methode müsste man nun als Angreifer von 0 bis 0.5 im Schnitt testen, bei dir nur von 0 bis 0.125.
Dadurch würde es bei deiner Methode mit einer rainbowtable deutlich schneller gehen als bei mir.

Zum Thema Sicherheit: ein Restrisiko bleibt immer.
Wenns total sicher sein muss, dann mach per Javascript ein RSA Handshake und verschlüssle alle Daten :) . (nur für Freaks).
Wichtig ist nur, dass du auf der Serverseite keine Fehler hast (SQL-Injection, offene Verzeichnisse) und per Definition die wichtigen Bereiche zusätzlich abgesichert sind. (Bspw. eine Bestätigungsemail für Passwortänderungen).
 
Ich glaube du hast das Konzept nicht ganz verstanden?

Erstens, hast du schon jemals eine Rainbow Table für md5(sha1(md5())) gesehen? Ich nicht. Für md5 und sha1 alleine existieren unmengen.

Zweitens: Benutzt du einen Algorithmus alleine, bist du auf die Sicherheit dieses einen Algorithmus angewiesen. Nehmen wir mal an, md5 wird soweit geknackt, das die Berechnung eines Passwortes aus einem beliebigen Hashschlüssel innerhalb annehmbarer Zeit möglich wird (eine Kollision kann immerhin schon innerhalb von 35 Minuten gefunden werden). Dann bringt es dir auch nichts, einen Schlüssel mit Timesalt zu verwenden, denn dann extrahiere ich halt den kompletten String von dir aus dem Hash, sobald ich einmal in Besitz eines Hashes bin und habe dann das Klartextpassword (samt Username und eben deinem Timestring)

Bsp:
PHP:
$Username = Administrator
$Password = Test
$Timestring = 12345678
md5($Username.$Password.$Timestring)

Output: 809c87e0f9f758f5cd983167e76cfca4

Mit der Rednoize Engine erhalte ich den Klartext Benutzernamen + Passwort + Timestring:
http://md5.rednoize.com/?q=809c87e0f9f758f5cd983167e76cfca4&s=md5&go.x=9&go.y=4&go=Search liefert AdministratorTest12345678 (erfolgreich)

Tritt dieser Fall bei meiner Methode ein, bekommt man lediglich das sha1 gehashte Passwort zurück (und beide Verfahren werden wohl kaum zeitgleich geknackt)

Das verwenden von Salts macht ein Passwort lediglich gegen Rainbow Tables und Reverse Engineering sicherer.

Drittens: Wenn du das Passwort jedes Mal erneut gegen einen Timesalt hasht und danach vergleichst, so wie du es in deinem Beispiel machst, heißt das, dass du das Passwort im Klartext in der DB abspeicherst (ansonsten könntest du ja nicht jedes Mal einen neuen vergleichbaren Hash erzeugen). Klartextpasswörter in der DB entbehren sowieso einmal jeder Sicherheitgrundlage (SQL Injection gehört noch immer zu den häufigsten Sicherheitslücken). Dieses Problem hast du nicht, wenn du meine Methode verwendest, da du hier einmal das Passwort so verschlüsselt in die DB speicherst und nur einmal Skriptseitig einen Vergleich durchführen musst. Der Aufwand ist hier also auch relativ gering.

Dein Rechenbeispiel, warum die Verwendungen mehrere Hashes die Berechnungszeit verkürzen sollte verstehe ich ehrlich gesagt nicht, eigentlich sehe ich es genau anders rum.

Ich hab dir deshalb auch mal ein kurzes Beispiel zusammengestellt, was da eigentlich passiert, anhand des einfachen Passwortes 'test';

PHP:
$password = 'test';
$password_hashed = md5(sha1(md5($password)));
echo 'Standard Password: ', $password;
echo '<br /> Password hashed (md5(sha1(med5())): ', $password_hashed;
$password = md5($password);
echo '<br />md5: ', $password;
$password = sha1($password);
echo '<br />sha1: ', $password;
$password = md5($password);
echo '<br />md5: ', $password;

Output:
Code:
Standard Password: test
Password hashed (md5(sha1(med5())): 54640093f5e4052d9aeda7f90b0b6324
md5: 098f6bcd4621d373cade4e832627b4f6
sha1: 4028a0e356acc947fcd2bfbf00cef11e128d484a
md5: 54640093f5e4052d9aeda7f90b0b6324

Praktisches Beispiel anhand des Rednoize Reverse Engineering:
http://md5.rednoize.com/?q=098f6bcd4621d373cade4e832627b4f6&s=md5&go.x=14&go.y=9&go=Search liefert test zurück (erfolgreich)
http://md5.rednoize.com/?q=4028a0e356acc947fcd2bfbf00cef11e128d484a&s=sha1&go.x=16&go.y=11&go=Search liefert 098f6bcd4621d373cade4e832627b4f6 zurück (erfolgreich)
http://md5.rednoize.com/?q=54640093f5e4052d9aeda7f90b0b6324&s=md5&go.x=14&go.y=2&go=Search liefert kein Ergebnis mehr zurück (nicht erfolgreich)

In diesem besonderen Fall kommt sogar noch zu gute, das sha1 einen 40 Zeichen Hash zurückliefert, während md5 nur 32 Zeichen Hashes codiert. Hashed man also einen 40 Zeichen String erneut mit md5, gibt es auch keine eindeutige Lösung mehr (und die Rednoize Reverse Engineering Engine scheitert daran)

Im Regelfall sollte es jedoch bei einem etwas sichereren Passwort gar nicht dazu kommen, das der sha1 Vergleich sogar erfolgreich verläuft. Beim dritten Vergleich ist es wie eben gezeigt schon nahezu unmöglich.

Ehrlich gesagt sehe ich hier jetzt keinen Nachteil gegenüber deinem Verfahren. Ich habe keine Klartextpasswörter (auch nicht in der DB), ich speichere nicht in Cookies, ich muss nicht ständig neu berechnen und ich bin sehr sicher gegen irgendwelche Tables :)

Aber vllt übersehe ich ja auch den entscheidenden Punkt :rolleyes:


so long
Renegade
 
Zuletzt bearbeitet:
1. Das PW muss nicht in der DB gespeichert werden, man kann auch ein Hash erzeugen, der dann abgespeichert wird, allerdings natürlich nur entweder mit einem statischen "streng geheimen" Salt oder die Zeit gleich mit abspeichern (nicht so gut, da ein Teil der Nachricht verfügbar ist). Den Hash den ich verwende, wird natürlich nur beim Einloggen verwendet, und dann in der DB und beim Nutzer als Cookie gespeichert. (Ein Cookie kann zwar geklaut werden, aber nicht allein durch Dummheit (GET Vars und dann ein link per Email versenden...)

Dein Problem: md5 hat eine Komplexität von 2^128 sha1 eine von 2^160.
Wenn du jetzt diese hintereinanderschaltest, dann nimmt die Komplexität langsam aber stetig ab. (Wie die Idee, ein ZIP-Ordner nocheinmal zu zippen), das passiert immer, wenn man Daten komprimiert. (Hashen ist wie die schlimmste Komprimierung)
Dadurch kann man zwar das PW nicht knacken aber man kann zum Brute-Forcen einen großen Teil der 2^128 Kombinationen ausschließen und deutlich schneller echte und passende Kollisionen finden...

BTW: Dein rednoiz findet nicht mal mein nick und 60 Mio. (ca. 2^28) Hashes ist wenig, (bei 2^128 möglichen)

to topic:
Sicherheit ist immer eine Frage der Statistik (Vericherungen lassen Grüßen). Wenn man dem Server deutlich mehr Vertrauen schenkt (sollte man als Programmierer) als dem Benutzer, dann solle man seine "gefährlichen" Variablen dorthin verlegen, wenn man nur einen kleinen Kundenkreis hat, der z.B. sonst gar nirgends ins Netz darf, dann kann auch mal ein haufen Zeugs beim Kunden aufm Rechner liegen.

$_SESSION ist für den kleinen (und großen) Programmierer eine Erleichterung, er braucht sich nicht mehr um die Hashes/Zufallsvariablen kümmern und hat die Komfortfunktionen wie $_SESSION und so. Wer ein Sicherheitsfreak ist, dem bleibt es natürlich offen, irgendetwas anderes zu entwickeln und zu verwenden.
 
Also anfangs dachte ich auch darüber nach, ob das "Gedankenexperiment" von Blitzmerker stimmt. Allerdings muss ich sagen, dass es schon so scheint als hat Renegade recht.
Der gespeicherte Hash-Wert aus der DB ist unknackbar (nahezu) und der rechenaufwand ist eher gering (man muss zwar 3mal verschlüsseln, aber dafür nicht großartig mit session usw. arbeiten).

Wie funktioniert denn bitte md5.rednoize.com?? Also bei mir erscheint nie ein Ergebnis mit den Hashes, die bei dir angeblich ein Ergebnis geliefert haben.

Was man allerdings noch kombinieren könnte:
Man könnte beim ersten md5 außer dem Passwort noch die Registrierungszeit (mktime() als sich der User registriert hat, die ändert sich ja nie)
md5.mktime -> sha1 -> md5
Somite würde der User nichtmal selbst irgendwie auf seinen in der DB-gespeicherten Hash-Wert kommen. (außer eben wieder SQL Injection oder so, aber dann kennt er ja immer noch nicht meinen Weg den Hash zu ermitteln)



Hm und wegen dem Verknüpfen von DB-Feldern, da muss ich nochmal nachlesen. Das versteh ich noch net wie das auslesen dann funktioniert...
Erleichtert das irgendwie die Abfrage per PHP?? Ich kann ja einfach so noch extra nen ID-Feld erstellen wobei ich die "Verknüpfung" durch mein PHP-Skript realisiere! Oder ändert diese phpmyadmin-Funktion doch irgendetwas?

edit: Hm Brute Force Attacken werden dadurch wirklich erleichtert, das stimmt.
Vielleicht sollte man erst mit einem kürzeren Verfahren verschlüsseln, und dann mit einem längeren.
Wobei hierdurch eigentlich die Verschlüsselung nicht besser wird... Es bleiben ja nur so viele mögliche End-Hash-Werte übrig (also nach beiden verschlüsselungen), wie es nach der ersten verschlüsselung schon gab...
Hm alles nicht so einfach...

edit2: ah jetzt geht sogar md5.rednoize.com
Vorhin hat er irgendwie immer nicht gesucht
 
Zuletzt bearbeitet:
-=Renegade=- schrieb:
Zweitens: Benutzt du einen Algorithmus alleine, bist du auf die Sicherheit dieses einen Algorithmus angewiesen. Nehmen wir mal an, md5 wird soweit geknackt, das die Berechnung eines Passwortes aus einem beliebigen Hashschlüssel innerhalb annehmbarer Zeit möglich wird (eine Kollision kann immerhin schon innerhalb von 35 Minuten gefunden werden). Dann bringt es dir auch nichts, einen Schlüssel mit Timesalt zu verwenden, denn dann extrahiere ich halt den kompletten String von dir aus dem Hash, sobald ich einmal in Besitz eines Hashes bin und habe dann das Klartextpassword (samt Username und eben deinem Timestring)

Wie soll das gehen? Ein einziger Hash hat in der Theorie unendlich viele Strings, die du extrahieren kannst, warum solltest du da so schnell ausgerechnet auf genau das stoßen, was du haben willst?

-=Renegade=- schrieb:
Bsp:
PHP:
$Username = Administrator
$Password = Test
$Timestring = 12345678
md5($Username.$Password.$Timestring)

Output: 809c87e0f9f758f5cd983167e76cfca4

Mit der Rednoize Engine erhalte ich den Klartext Benutzernamen + Passwort + Timestring:
http://md5.rednoize.com/?q=809c87e0f9f758f5cd983167e76cfca4&s=md5&go.x=9&go.y=4&go=Search liefert AdministratorTest12345678 (erfolgreich)

Verstehe ich schon wieder nicht, warum benutzt du als Beispiel jetzt eine Rainbowtable? Also mal ehrlich, es ist utopisch, dass es für jede Art von salts rainbow tables gibt.
Benutzer+ Passwort + Timestring ist ja auch arg kreativ..

-=Renegade=- schrieb:
Das verwenden von Salts macht ein Passwort lediglich gegen Rainbow Tables und Reverse Engineering sicherer.

Hm? in deinem Beispiel hast du doch gerade zeigen wollen, dass das nicht so ist?


-=Renegade=- schrieb:
In diesem besonderen Fall kommt sogar noch zu gute, das sha1 einen 40 Zeichen Hash zurückliefert, während md5 nur 32 Zeichen Hashes codiert. Hashed man also einen 40 Zeichen String erneut mit md5, gibt es auch keine eindeutige Lösung mehr (und die Rednoize Reverse Engineering Engine scheitert daran)

Glückwunsch, jetzt hilfst du auch noch selbst dabei, Kollisionen zu erzeugen, ist das dein Ernst?
 
Ich denke, ich lass einfach mal ein paar andere Sprechen:
php.net Einfach die Kommentare lesen (gutes beispiel ist der, der in die Hashkette ein crc32 einbaut (4 Mrd. Kombinationen)).
http://de.wikipedia.org/wiki/Message-Digest_Algorithm_5
Die Verwendung eines Salt, also eines zufälligen nicht vorhersehbaren Wertes, welcher an den Klartext angefügt wird, kann die Effektivität von vorberechneten Regenbogentabellen jedoch zunichte machen.

Zur DB:
Wenn du folgendes machst
Code:
select user.name,user.signatur,beitraege.text from beitraege join users on user.id=beitraege.id
Dann bekommst du user.name,user.sigantur,beitraege.text. Der (normale) Join ist wie wenn du 2 Tabellen zusammenpappst und die zweite Tabelle bekommt die Daten durch die Angaben in der ersten Tabelle.

EDIT: carom: genau, das mein ich (aber ich kanns nicht richtig formulieren :) )
 
So ich hab nun mal nen kleinen Test gemacht, und hab mich dafür entschieden:
PHP:
//Wobei $reg_time die registrierungs-mktime() ist (als sich der User registriert hat)
$md5=md5($password.$reg_time);

Es scheint so als kann man ganz einfach auf das JOIN in der Datenbankabfragen verzichten:

Code:
SELECT X.a, X.b, Y.a, Y.b, Y.c
FROM X, Y
WHERE X.a = Y.a AND X.b = Y.b
Das finde ich super einfach und erfüllt seinen Zweck ohne Join benutzen zu müssen.
 
Blitzmerker schrieb:
1. Das PW muss nicht in der DB gespeichert werden, man kann auch ein Hash erzeugen, der dann abgespeichert wird, allerdings natürlich nur entweder mit einem statischen "streng geheimen" Salt oder die Zeit gleich mit abspeichern (nicht so gut, da ein Teil der Nachricht verfügbar ist). Den Hash den ich verwende, wird natürlich nur beim Einloggen verwendet, und dann in der DB und beim Nutzer als Cookie gespeichert. (Ein Cookie kann zwar geklaut werden, aber nicht allein durch Dummheit (GET Vars und dann ein link per Email versenden...)
Also ich glaube wir reden da wirklich aneinander vorbei. Irgendwo musst du ja dein Password in der Datenbank abspeichern. Und wenn du zum hashen das Klartext Passwort verwendest, dann musst du es ergo auch Klartext in der Datenbank abgespeichert haben. Und Klartext Passwörter in der Datenbank sind ein weitaus größeres Risiko als das Verwenden mehrerer Algos oder eines Salts.

Dein Problem: md5 hat eine Komplexität von 2^128 sha1 eine von 2^160.
Wenn du jetzt diese hintereinanderschaltest, dann nimmt die Komplexität langsam aber stetig ab. (Wie die Idee, ein ZIP-Ordner nocheinmal zu zippen), das passiert immer, wenn man Daten komprimiert. (Hashen ist wie die schlimmste Komprimierung)
Dadurch kann man zwar das PW nicht knacken aber man kann zum Brute-Forcen einen großen Teil der 2^128 Kombinationen ausschließen und deutlich schneller echte und passende Kollisionen finden...

BTW: Dein rednoiz findet nicht mal mein nick und 60 Mio. (ca. 2^28) Hashes ist wenig, (bei 2^128 möglichen)
Zum Thema rednoize: Das war rein die erste die ich bei Google gefunden habe und da sie über GET Variablen arbeitet war es einfach gut, ein Beispiel zu liefern. Es gibt weit bessere Engines dafür, das auf jeden Fall. Das diente lediglich zur Verdeutlichung.

Ich hab mich auch ehrlich gesagt nich soweit mit der Mathematik beschäftigt, in wie weit die Komplexität tatsächlich abnimmt, aber wenn wir schon ins Thema Brute & Force gehen halte ich es schon für realistischer, ein durchschnittliches Standarduserpasswort mit 7-8 Zeichen zu knacken als einen reduzierten Hash.

carom schrieb:
Wie soll das gehen? Ein einziger Hash hat in der Theorie unendlich viele Strings, die du extrahieren kannst, warum solltest du da so schnell ausgerechnet auf genau das stoßen, was du haben willst?

Verstehe ich schon wieder nicht, warum benutzt du als Beispiel jetzt eine Rainbowtable? Also mal ehrlich, es ist utopisch, dass es für jede Art von salts rainbow tables gibt.
Benutzer+ Passwort + Timestring ist ja auch arg kreativ..
Lesen hilft ;) Dieses Beispiel war unter der Annahme, das ein Verfahren komplett geknackt wurde und die Berechnung jedes Hashes innerhalb kurzer Zeit möglich ist.


Hm? in deinem Beispiel hast du doch gerade zeigen wollen, dass das nicht so ist?
Und das gilt für dieses Beispiel nicht mehr. Denn Rainbow Tables heißt nicht, das der Algo vollständig geknackt wurde ;)
 
te one schrieb:
Es scheint so als kann man ganz einfach auf das JOIN in der Datenbankabfragen verzichten:

Code:
SELECT X.a, X.b, Y.a, Y.b, Y.c
FROM X, Y
WHERE X.a = Y.a AND X.b = Y.b
Das finde ich super einfach und erfüllt seinen Zweck ohne Join benutzen zu müssen.
Funktioniert zwar, das Problem ist aber, das intern eine Ergebnismenge generiert wird, die aus der Anzahl der Einträge in Tabelle X multipliziert mit der Anzahl der Einträge in Tabelle Y besteht und anschließend durch WHERE eingegrenzt wird.
Wenn also in Tabelle X zehn Einträge vorhanden sind und in Tabelle Y 14 dann wird ein Ergebnis erzeugt, das 140 Einträge hat, die dann durch WHERE eingegrenzt werden.

Bei Join
Code:
SELECT X.a, X.b, Y.a, Y.b, Y.c
FROM X LEFT JOIN Y ON X.a = Y.a
WHERE X.b = Y.b
wird eine Ergebnismenge generiert, die maximal so viele Einträge hat wie Tabelle X und über die Verknüpfung hinter ON bestimmt und anschließend durch WHERE eingegrenzt wird.
Wenn also in Tabelle X zehn Einträge vorhanden sind und in Tabelle Y 14 dann wird ein Ergebnis erzeugt, das maximal zehn Einträge hat, die dann durch WHERE eingegrenzt werden.

JOIN ist also viel effizienter.

EDIT: Man kann sich JOIN quasi so vorstellen, dass
Code:
X LEFT JOIN Y
alle Spalten der Tabelle Y nimmt und sie der Tabelle X hinzufügt unter der Bedingung ON,
Code:
ON X.a = Y.a
dass die Zeile bzw. der Eintrag in der Spalte X.a gleich der Zeile bzw. dem Eintrag Y.a ist.
 
Zuletzt bearbeitet:
Zurück
Oben