PHP/SQL - Passwort wirklich sicher speichern

hoi - aaaalso ich bin der Neue, der euch in nächster zeit wohl öfter in diesem Forum belästigen wird ;)

und zwar bin ich auf der suche nach einer möglichst sicheren Methode Login-Passwörter zu verschlüsseln.
auf sha1 und md5 allein kann man sich ja nichtmehr verlassen...
mit Bruteforce und diversen Online-Datenbanken sind solche Passwörter schnell geknackt...

jetzt hab ich mir meinen "eigenen" Algorithmus gebaut, der auf Hash-keys und Salt's basiert... das ganze mehrmals hintereinander. So kommt am ende ein 128-Zeichen-Code heraus.

jetzt wollt ich mal fragen wie sicher das wirklich ist, denn bei hash's ist ja immer das Problem dass es mehrere "Lösungen" zu einem Hash gibt...
 
Hi,

Das verwenden eines eingenen Algorithmuses verschafft dir natürlich einen Vorteil, aber denke an das Kerckhoffs-Prinzip, das besagt, dass eine Verschlüsselung nicht auf Geheimhaltung des Algorithmus basieren darf sondern einzig und allein in der Geheimhaltung des Schlüssels.

Und wer sagt, das dein Algorithmus sicher ist? Ist jetzt nichts gegen dich, aber selbst in Algorithmen wie MD5 und SHA, die von sehr guten Mathematikern entwickelt wurden, wurden Schwachstellen entdeckt. OK, du hättest den Vorteil, dass weniger Leute versuchen Schwachstellen zu finden, da der Algorithmus nicht so weit verbreitet ist (...)

Schau dir doch auch mal diesen Artikel an: Salted Hash

Was im Enddeffekt auch noch Sinn macht, ist ein Teil des Hashs in einer DB zu speichern und den anderen Teil in einer Datei, das hat den Vorteil, das jemand, der z.B. einen Exploit für deinen SQL Server hat erst noch einen für dein Dateisystem suchen muss...

Gruß Chris
 
Eine eigene Verschlüsellungsmethode ist eigentlich sehr sicher, wenn du niemandem sagst wie sie funktioniert. ;)
Das mit den doppelten Hashes ist erstmal kein Problem außer wenn dein Algorithmus zu viele Kollisionen verursacht.

MFG -=HKA
 
das mit splitten des hashkeys ist eine sehr gute idee :]
werd ich sofort einbauen ^^

@blacksun: salted hash's hab ich ja implementiert ;)
 
Hi,

Du könnstest z.B. die Rundenanzahl für deinen Hash Algorithmus aus dem Benutzernamen berechenen...

Oder du kannst die Hashs nochmal mit AES verschlüsseln und den Key auf einem anderen Server speichern...

Der Paranoia beim Datenschützen sind keine genzen gesetzt :D

Gruß Chris
 
Der tollste Hash-Algorithmus bringt nix, wenn der User schwache Passworte auswählt. Die Kombination aus einem Salted Hash mit MD5 und einer Prüfroutine, die keine Passworte akzeptiert, die nicht mindestens 6 Zeichen (mit Buchstaben, mindestens einer Zahl und einem Sonderzeichen) beinhalten, halte ich da aber für sicher genug. MD5 auch deswegen, weil die Kollisionswahrscheinlichkeit hier (im Gegensatz zu einem eigenen Algorithmus nachweislich) sehr gering ist.

Den Salt kannst du ja woanders ablegen oder dynamisch berechnen lassen, in dem Fall würde es nicht mal was bringen, wenn jemand Zugriff auf deine Datenbank bekommt und den Hash auslesen könnte.
 
Zwing den Benutzer dazu ein langes Passwort zu verwenden und speicher dann sein Passwort als verschiedene Hashes ab. (MD5, SHA-1 bis 512, Whirlpool, GOST, ...)
Damit wird es so gut wie unmöglich das Passwort rauszufinden.
Bruteforce kannste wegen der Länge knicken und eine Kollision zu finden, die auf mehrere Hashes zutrifft ist sehr gering.
Aber wie gesagt:

Der tollste Hash-Algorithmus bringt nix, wenn der User schwache Passworte auswählt.
 
Hi,

Original von valenterry
speicher dann sein Passwort als verschiedene Hashes ab. (MD5, SHA-1 bis 512, Whirlpool, GOST, ...)
Damit wird es so gut wie unmöglich das Passwort rauszufinden.
Bruteforce kannste wegen der Länge knicken und eine Kollision zu finden, die auf mehrere Hashes zutrifft ist sehr gering.

Stimmt, ABER wenn jemand nun einen Exploit hat, mit dem er die Daten seines SQL Servers auslesen kann, kann sich der Cracker raussuchen, welchen Hash er entschlüsseln will. X(

Gruß Chris
 
Hallo,
selber einen Hash Algorithmus schreiben, die Runden-Anzahl zu variieren und ähnliche Expertiemente schaden mehr als dass sie nutzen.

Was wirklich hilft, ist eine Salt zu verwenden, z.B. 10 Bytes. Denn dann funktionieren die Datenbanken/Rainbow-Tables nicht mehr und wenn man ein gutes Passwort verwendet (>8 Zeichen), dann dauert es tausende von Jahren bis man das Passwort per Brute Force geknackt hat.

Du kannst z.B. die PHP Funktion crypt(); mit MD5 als Algorithmus verwenden, oder wenn es möglichst kompatibel sein soll, generierst du eine zufällige 8 Byte Salt und in der DB speicherst du dann:
$pw = $Salt.'$'.md5($Salt.$UserPW);

Außerdem solltest auch noch überprüfen, ob das Passwort sicher ist. Denn gegen ein schwaches PW helfen nicht die besten Maßnahmen.


Was evt. auch noch intressant sein könnte, wäre eine HMAC zu verwenden.
Der Angreifer müsste dann Zugriff auf deine DB und Zugriff auf deinen PHP Script mit dem Key bekommen. Eine normale SQL Injection (in älteren phpBB Versionen konnte man per SQL Injection an die Passwort-Hashs gelangen) würde dann nicht mehr reichen.

Optimal wäre es, wenn du auf dem Server ein Programm laufen hast, was dir die HMAC's berechnet, und welches normale User nur ausführen können (inkl. Brute-Force schutz). So müsste ein Angreifer root Rechte auf dem Server bekommen, um an das Programm und somit an den Key zu gelangen.
Aber solch eine Möglichkeit ist eher selten.
 
hmmm...
Elderan
selber einen Hash Algorithmus schreiben, die Runden-Anzahl zu variieren und ähnliche Expertiemente schaden mehr als dass sie nutzen.
Was wirklich hilft, ist eine Salt zu verwenden, z.B. 10 Bytes. Denn dann funktionieren die Datenbanken/Rainbow-Tables nicht mehr und wenn man ein gutes Passwort verwendet (>8 Zeichen), dann dauert es tausende von Jahren bis man das Passwort per Brute Force geknackt hat.
Salts hab ich ja eingebaut, das Passwort wird vor dem ersten Hashen mit 2 Salts versehn, und danach abhängig von einem variablen Schlüssel in jeder Stufe wieder mit diversen Salts vesehn, wobei das orignale Passwort in Verbindung mit einem Schlüssel mehr Gewichtung in dem einzelnen Hashs bekomt als die Salts, um die Kollisionswahrscheinlichkeit zu minimieren...
Zu erklären wie das bei bis jetzt genau funktioniert ist ja auch erst mal egal^^

Minnimale Passwortlänge hatt ich eigentlich 7 Zeichen mit mindestens einem/einer Sonderzeichen/Ziffer, allerdings nicht am Anfang/ende, vorgesehn um Wörterbuchattacken auszuschalten.
Und Bruteforce wird sich wohl an den diversen variablen Hash's die Zähne ausbeißen...

Algorithmen verwende ich md5, sha1 und zur Berechnung der variablen Hash's crc32.

Elderan
Was evt. auch noch intressant sein könnte, wäre eine HMAC zu verwenden.
Der Angreifer müsste dann Zugriff auf deine DB und Zugriff auf deinen PHP Script mit dem Key bekommen. Eine normale SQL Injection (in älteren phpBB Versionen konnte man per SQL Injection an die Passwort-Hashs gelangen) würde dann nicht mehr reichen.
okay, was genau ist/macht HMAC denn genau? ich hab mir den Artikel mal durchgelesen, aber bin nicht wirklich schlau daraus geworden^^ soweit ich das verstanden hab generiert HMAC einen umkehrbaren Hash aus einer "Nachricht" - sprich Passwort - und einem Schlüssel.
Also ähnlich meinem Algorithmus... nur was genau bringt mir das dann im Endeffekt an Sicherheit?
 
Hallo,
Original von keksinat0r
okay, was genau ist/macht HMAC denn genau?
Etwas spät die Antwort ^^

Also HMAC arbeitet so ähnlich wie normale Hashverfahren, allerdings nur die Person die im Besitzt des Schlüssels ist, kann gültige Hashwerte erzeugen, bzw. diese überprüfen.

Wenn du dein PW als MD5 in der DB hinterlegst, und ein Angreifer per SQL Injection an den Hashwert kommt, kann er per BruteForce diesen Hashwert knacken, indem er für jede Zeichenfolge den Hashwert bildet und schaut, ob der gesuchte dabei ist.

Bei HMAC wäre dies nicht mehr möglich, er bräuchte zusätzlich noch den geheimen Schlüssel um überhaupt gültige Hashwerte zu generieren.

In PHP würde eine sichere HMAC Implementierung mit Salt so aussehen:
PHP:
<?php


function HMAC($pw, $salt=null)
{
   //Eine interne Variable damit die nicht auf anderen Seiten ungewollt ausgegeben werden kann
   $key = "geheimer Key";

   if($salt == null)
      $salt = substr(base64_encode(pack('H*',crc32(microtime()))),0,6); //6 Zeichen Salt
   else if(strpos($salt, "|") !== false)
       $salt = substr($salt, 0, strpos($salt, "|"));

   return $salt.'|'.sha1($key.sha1($salt.$pw));
}

//Usage:
//Hash generieren
$hash = HMAC("UserPasswort");

//Hash überprüfen
if(HMAC($usereingabe, $hash_aus_db) == $hash_aus_db)
   echo "Richtiges PW";
?>

Nur wer den Wert für KEY besitzt, ist in der Lage gültige Hashwerte zu generieren.
Für einen Angreifer würde es nicht mehr reichen per SQL Injection an die Hashwerte zu gelangen, er müsste zusätzlich noch eine Lücke im Server finden, um PHP Dateien auszulesen.

Ein weiteres häufiges Problem ist das Datenbank-Backup, worüber ein Angreifer alle Hashwerte bekommen kann.

In einer älteren phpBB Version konnte man durch einfache Cookie Manipulation Adminrechte erlangen. Man musste dazu die Userid von einem Admin kennen (meistens 2) und dann für den PW Hash einen speziellen, manipulierten Wert eintragen und schon konnte man sich mit dem Cookie als Admin einloggen (ohne einen Hashwert zu kennen).
Im AdminCP könnte man sich dann das Datenbank Backup herunterladen und per BruteForce die Passwörter knacken, um damit weiteren Schaden anzurichten.
Mit HMAC hätte man wie gesagt, noch eine Möglichkeit suchen müssen, PHP Scripts auszulesen.
 
Ich glaube beim letzten if() fehlen die {} - klammern ;)
edit: die fehlen fast überall. kurzschreibweise oder fehler?

Was ich mir überlegt habe, und ich habe keine ahnung davon, was wäre wenn man den MD5 Hash, nochmal durch die md5() laufen lassen würde?

Dann hätte man für den Endhash minimum ein 32-bytiges zeichen, also mit sicherheit lange genug. Darauf müsste man dann erstmal kommen, so als häcker.

also md5("1234") -> [hash] -> md5("[hash]") -> Endpasswort

So als Idee.
 
Die Klammern kann man bei PHP weglassen, dann wird nur der nachfolgende Befehl als zugehörig betrachtet.

Wenn man den Hash 2 mal durch die MD5-Routine laufen lassen würde, brächte das kaum größere Sicherheit. Der Cracker lässt das Passwort, dass er ausprobiert dann einfach auch 2mal durch die Routine laufen. Die Rechenzeiten würden zwar etwas erhöht, aber die MD5-Summe braucht er trotzdem nicht erraten.
 
@Elderan sehr interessant - werde das gleich (in einer leicht abgewandelten Form) in meine Session Class implementieren. =)

@starfoxx - gegen Bruteforce bringt das wenig, wenn der Angreifer weiß, dass die md5 Funktion 2 mal verwendet wurde. Gegen Rainbowtables hingegen schon :D


Aber selbst der beste Algoryhtmus bringt wenig, wenn der Benutzer den Namen seiner Freundin oder etwas ähnlich banales als Passwort nutzt :P

Deshalb immer ne Prüfroutine einbauen, die nur Passwörter ab 8 Zeichen + mindestens 1 Zahl & ein Sonderzeichen zulässt. :D
 
Ich meinte das, von der Annahme aus gehen dass derjenige nur an den Hash gekommen ist. Wenn man Sql-Injected hat man ja nicht grundsätzlich zugriff auf den PHP Code. Soweit ich das verstanden habe.

Dann brächt's was. irgendwie.
 
Original von [starfoxx]
Ich meinte das, von der Annahme aus gehen dass derjenige nur an den Hash gekommen ist. Wenn man Sql-Injected hat man ja nicht grundsätzlich zugriff auf den PHP Code. Soweit ich das verstanden habe.

Dann brächt's was. irgendwie.

wie auch?

wenn er nur zugang zu der datenbank hat bringt ihm das noch net wirklich viel, da er dort ja nur die hash's findet...

wenn er allerdings zugang zu den scripten bekommt kommt er logischereise auch an die sql-logindaten und somit auch an die datenbank...


ich setze im moment folgendes verfahren ein:

aus dem pw wird mit hilfe von hash-algoritmen, diversen berechnungen und individuell berechneten salts ein 128bit code erstellt... dieser wird in 2 teile zerlegt -
ein teil in der datenbank, der andre in einer datei auf dem server.

so müsste der angreifer schon zugriff auf den server selbst haben um einen kompletten hash zu erlangen, wobei ihm der dann wiederum auch egal sein kann, da er ja dann sowieso vollzugriff hätte^^
 
Hallo,
Original von keksinat0r
ein teil in der datenbank, der andre in einer datei auf dem server.

so müsste der angreifer schon zugriff auf den server selbst haben um einen kompletten hash zu erlangen
Naja die Speicherung von Hashwerten in 2 Orten kann schnell zu Synchronisationsproblemen führen, außerdem muss sichergestellt sein, dass man die Datei nicht einfach per URL Aufruf abfragen kann.

Allerdings wie bereits mehrfach geschrieben, bei einem HMAC bräuchte der User auch Zugriff auf die Datenbank und auf den PHP-Source-Script (bzw. auf den Key).


Aber anstatt sich komplizierte Rechenverfahren auszudenken, wäre es deutlich sinnvoller, seinen Code gegen SQL, Code und XSS Injections abzusichern.
 
die passwort-dateien können nur serverseitig aufgerufen werden.

gegen injections kann man nie 100%ig sicher sein da man garantiert irgendwo eine lücke übersehn hat, desswegen geh ich so auf nummer sicher :)


bin auch am überlegen wie ich das alles noch besser machen könnte... mal sehn vielleicht fällt meiner paranioa noch etwas fieses für die bösen H4ck0r ein :P
 
Achtung
in die HMAC-Funktion hat sich ein Fehler eingeschlichen.

Ich habe die Funktion gerade mal ausprobiert und festgestellt, dass JEDES Passwort als richtig erkannt wurde.

Der Fehler liegt in der Variablen-Benennung:

Als Parameter der Funktion wird $pw übergeben, beim return dagegen will er den Wert der Variable $pw_eingabe hashen --> also bitte auf einen der zwei Variablennamen einigen und schon klappt's!

EDIT:
Ich hab meine Paranoja mal noch ein wenig weiter spinnen lassen.
  1. statt dem 6-stelligem base64-Salt hab ich nun 12 stellen eines md5's genommen
  2. den "|", der Salt von Hash getrennt hatte, hab ich raus genommen, um keine visuelle Abgrenzung mehr zu haben
  3. den 12-Zeichen-Salt hab ich wild im Hash verteilt

Das Ergebnis sieht dann so aus:
PHP:
function HMAC($pw_eingabe, $testhash=null)
{
   //Eine interne Variable damit die nicht auf anderen Seiten ungewollt ausgegeben werden kann

   $key = "dasistmeingeheimerkey";

   if($testhash == null)
      $salt = substr(md5(pack('N',crc32(microtime()))),0,12); //12 Zeichen Salt
   else
       $salt = substr($testhash, 0,2).substr($testhash,5,1).substr($testhash,21,2).substr($testhash,8,2).substr($testhash,strlen($testhash)-3,3).substr($testhash,15,2);
   $pwhash=sha1($key.sha1($salt.$pw_eingabe));
   return substr($salt,0,2).substr($pwhash,0,3).substr($salt,2,1).substr($pwhash,3,2).substr($salt,5,2).substr($pwhash,5,5).substr($salt,10,2).substr($pwhash,10,4).substr($salt,3,2).substr($pwhash,14,strlen($pwhash)-14).substr($salt,7,3);
}

mfg
BeAvIsBeE
 
Zurück
Oben