Klaus: Unterschied zwischen Firefox und anderen Browsern?

0 55

Unterschied zwischen Firefox und anderen Browsern?

Klaus
  • browser
  1. 0
    Matze
    1. 0
      molily
      1. 0
        Matze
        1. 0
          Klaus
          1. 0
            Matze
            1. 0
              Klaus
              1. 0
                Matze
  2. 0
    Felix Riesterer
    1. 0

      Zustimmung: Einfach MD5-gehaschte Passwörter sind unsicher

      Jörg Reinholz
      • programmiertechnik
      1. 0
        Klaus
        1. 0
          Matze
          1. 0
            Klaus
          2. 0
            MudGuard
        2. 2
          molily
          1. 0
            Klaus
            1. 0

              Statt md5(str) - Das, was heise vorschlug in einf. Code gegossen

              Jörg Reinholz
              1. 0
                molily
                1. 0
                  Jörg Reinholz
                  1. 0
                    Jörg Reinholz
                  2. 2
                    molily
                    1. 0
                      molily
                    2. 0
                      Jörg Reinholz
                      1. 0
                        molily
                        1. 0
                          Jörg Reinholz
                          1. 1
                            dedlfix
                            1. 0
                              Jörg Reinholz
                              1. 0
                                molily
                                1. 0
                                  molily
                                  1. 0
                                    Jörg Reinholz
                                2. 0
                                  Jörg Reinholz
                              2. 1
                                Sven Rautenberg
                                1. 0
                                  Jörg Reinholz
                        2. 0
                          Jörg Reinholz
                          1. 2
                            molily
                            1. 0
                              Jörg Reinholz
                              1. 0

                                Kostenloses Bildungsangebot

                                molily
                                1. 0
                                  molily
                                  1. 0

                                    Unbrauchbares Bildungsangebot

                                    Jörg Reinholz
                                  2. 0

                                    Kostenloses Bildungsangebot - jetzt für Dich

                                    Jörg Reinholz
                                2. 0

                                  Kostenloser Hinweis auf Deinen Denkfehler

                                  Jörg Reinholz
              2. 0
                Der Martin
              3. 1
                Sven Rautenberg
                1. 0
                  Matthias Apsel
    2. 0

      Verschlüsselung clientseitig. Kann mir das mal einer erklären ?

      Siri
      1. 0
        misterunknown
        1. 0
          Siri
      2. 0
        Jörg Reinholz
        1. 0
          Siri
      3. 0

        Noch ne Frage: Wo wird eigentlich der Schlüssel hinterlegt?

        Siri
  3. 0
    Jörg Reinholz
    1. 0
      Klaus
  4. 0
    molily
    1. 1

      Session-Konfiguration

      Matze
    2. 0
      Klaus

Hallo,

mir ist gestern ein ziemliches Problem aufgefallen. Von mir entwickelte Seiten funktionieren einwandfrei mit dem Firefox, aber mit keinem anderen Browser (weder IE, noch Chrome, noch Safari)

So funktioniert bereits der Login auf der Startseite nicht.
Hier wird geprüft, ob eine Session existiert und wenn nicht, die Login-Form angezeigt.
Beim Abschicken wird über Ajax die Daten geprüft und das Ergebnis zurückgegeben.
Dieses wird testweise dann in einem Div angezeigt. Aber selbst das funktioniert auch schon nicht.
Weder IE, noch Chrome, noch Safari deuten irgendeinen Fehler an, was die Fehlersuche für mich deutlich erschwert.
Für mich sieht es so aus, als ob der ganze Ajax-Aufruf erst gar nicht ausgeführt wird.
Denn ein Alert(resulttext) direkt nach der resulttext-Zuweisung wird auch nicht ausgeführt.

Kann mir jemand einen Tip geben, was ich ändern muss, damit die anderen Browser den Code auch korrekt ausführen?

Die Login-Form sieht so aus:

  
<script type="text/javascript" language="javascript">  
function doLogin() {  
	var formdaten = serialize(document.loginform);  
  
	var req = createXMLHttpRequest();  
	req.onreadystatechange = function() {  
		switch(req.readyState) {  
			case 0:  
			case 1:  
			case 2:  
			case 3: return;  
			case 4: break;  
		}  
		resulttext = req.responseText;  
		if (resulttext.search("Success") >= 0) {  
//			window.location="index.php";  
			document.getElementById('loginresult').innerHTML = resulttext;  
			document.getElementById('loginresult').style.display = "block";  
			document.getElementById('logininfo').style.display = "none";  
		} else {  
			document.getElementById('loginresult').innerHTML = resulttext;  
			document.getElementById('loginresult').style.display = "block";  
			document.getElementById('logininfo').style.display = "none";  
		}  
	}  
	url = "<?=$pfadhtml;?>/templates/do_login.php";  
	req.open('post', url, true);  
	req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");  
	req.setRequestHeader("Content-length", formdaten.length);  
	req.setRequestHeader("Connection", "close");  
	req.send(formdaten);  
  
	return false;  
}  
</script>  
<div id="loginresult" class="error center" style="width:250px; display:none;"><span class="sonderzeichen error-symbol"></span>Fehler</div>  
  
<div id="logininfo" class="info shadow width_3">  
	Einige Inhalte stehen nur authentifizierten Benutzern zur Verfügung. Bitte melden Sie sich hier an.<br/>  
	Sollten Sie noch über keine Zugangsdaten verfügen, setzen Sie sich mit Ihrer IT-Technik in Verbindung.  
</div>  
<div id="form-wrapper" class="width_3">  
    <form name="loginform" method="post" action="#" onsubmit="return doLogin();">  
        <label for="loginname">Login:</label>  
        <input type="text" id="loginname" name="loginname" value="" required="required" />  
        <label for="password">Passwort:</label>  
        <input type="password" id="password" name="password" value="" required="required" />  
		<br/>  
		eingeloggt bleiben?  
		<span class="custom-checkbox">  
		  <input type="checkbox" name="stayloggedin" id="stayloggedin" />  
		  <span class="box"><span class="tick"></span></span>  
		</span>  
		<div class="clear"></div>  
		<input class="button" type="submit" value="Einloggen" />  
    </form>  
</div>  

Das Ajax-Script sieht so aus:

  
header("Content-Type: text/html; charset=iso-8859-1");  
require('/grab_globals.lib.php');  
include "/config.php";  
include ("$pfadphp/templates/toolbox.php");  
  
session_start();  
  
$verbindung = @mysql_connect($server,$login,$pass);  
$test_pw = md5($password);  
$abfrage = "select anzeigename,loginname from userlogins where loginname='$loginname' AND password='$test_pw'";  
$erg = mysql_db_query($dbname,$abfrage,$verbindung);  
$anz = mysql_num_rows($erg);  
if ($anz > 0) {  
	$row = mysql_fetch_array($erg);  
	extract($row);  
	$_SESSION['Benutzername'] = $loginname;  
  
	if ($stayloggedin == "on") {  
		$md5name = md5($loginname);  
		$cookievar = $loginname.",".$md5name;  
		setcookie("cookievar",$cookievar,time()+(3600*24*100));  
  
		$abfrage = "update userlogins set cookievar = '$md5name' where loginname = '$loginname'";  
		$erg = mysql_db_query($dbname,$abfrage,$verbindung);  
		echo $abfrage;  
	}  
	echo "--Success--";  
} else {  
	@session_destroy();  
	echo "Anmeldung fehlgeschlagen.";  
}  
mysql_close($verbindung);  

  1. Hallo!

    Ich hatte eigentlich angefangen ein Script von mir für dich zurecht zu biegen und habe gemerkt, dass es eigentlich wesentlich einfacher ist wenn du für die AJAX-Geschichte einfach jQuery benutzt.
    Du sagst, dass dein Request nicht ausgeführt wird, also tippe ich darauf, dass sowas in der Art bei dir fehlt:

    var request = null;  
    try{  
    	request = new XMLHttpRequest();  
    }catch (ms){  
    	try{  
    		request = new ActiveXObject("Msxml2.XMLHTTP");  
    	}catch (nonms){  
    		try{  
    			request = new ActiveXObject("Microsoft.XMLHTTP");  
    		}catch (failed){  
    			request = null;  
    		}  
    	}  
    }
    

    jQuery nimmt dir das ganze Gedöns mit den verschiedenen Browsern ab.
    Den HTTP-Status fragt dein Script übrigens auch nicht ab. Aber das erstmal am Rande.
    Zum testen des AJAX-Requests reicht es auch erstmal aus wenn du einfach einen Teststring zurück gibst. Solange der nicht in deinem JS landet, interessiert der PHP-Teil erstmal nicht.

    Grüße, Matze

    1. Hallo,

      Ich hatte eigentlich angefangen ein Script von mir für dich zurecht zu biegen und habe gemerkt, dass es eigentlich wesentlich einfacher ist wenn du für die AJAX-Geschichte einfach jQuery benutzt.

      jQuery einzubinden für einen simplen XHR-Request ist beileibe nicht nötig.

      var request = null;

      try{
      request = new XMLHttpRequest();
      }catch (ms){
      try{
      request = new ActiveXObject("Msxml2.XMLHTTP");
      }catch (nonms){
      try{
      request = new ActiveXObject("Microsoft.XMLHTTP");
      }catch (failed){
      request = null;
      }
      }
      }

        
      Die Nutzung von new ActiveXObject ist nur für Internet Explorer < 7 nötig (ActiveX ist eine proprietäre Microsoft-Technik). Alle anderen Browser unterstützen `new XMLHttpRequest()`{:.language-javascript}.  
        
      Mathias
      
      1. Hallo!

        jQuery einzubinden für einen simplen XHR-Request ist beileibe nicht nötig.

        Sicher, es ist mit jQuery aber unkomplizierter.

        Die Nutzung von new ActiveXObject ist nur für Internet Explorer < 7 nötig (ActiveX ist eine proprietäre Microsoft-Technik). Alle anderen Browser unterstützen new XMLHttpRequest().

        Ja das ist mir klar. Aber weil Klaus keine Versionsangaben gemacht hat, habe ich das mal mit berücksichtigt. Aber auch das würde jQuery einem abnehmen :)

        Grüße, Matze

        1. jQuery einzubinden für einen simplen XHR-Request ist beileibe nicht nötig.

          Sicher, es ist mit jQuery aber unkomplizierter.

          Noch unkomplizierter wäre es, es gar nicht selber zu programmieren. Aber Spaß beiseite, ich bin kein Freund von großen Bibliotheken für simple Aufgaben, auch wenn es viel erschlägt.

          Zudem sehe ich es als Herausforderung zu verstehen, warum etwas funktioniert oder eben auch nicht. JQuery einzusetzen ist für mich so wie nur noch im Restaurant zu essen, weil man nicht weiß, wie die Herdplatte angeht.

          1. Hallo!

            Ok, ich sehe das mit jQuery auch so wie du.
            Und was sagt bei dir der Hinweis auf

            var req = new XMLHttpRequest();

            ? Hast du das mittlerweile getestet?

            Grüße, Matze

            1. var req = new XMLHttpRequest();

              Ich hätte es nicht vermutet, aber das war tatsächlich die Ursache.

              Ich hatte erwartet, dass
              var req = createXMLHttpRequest();
              genau das selbe macht wie
              var req = new XMLHttpRequest();

              Werde ich mir mal genauer anschauen, worin der Unterschied besteht, bzw. warum der Firefox das versteht, aber der Rest nicht.

              Ansonsten werde ich wohl bei allen (zahlreichen) Ajax-Requests diese Zeile umbauen (müssen).

              Vielen Dank !!

              1. Hallo!

                Ich hätte es nicht vermutet, aber das war tatsächlich die Ursache.

                Ich hab es schließlich nicht aus Spaß geschrieben ;)
                Zum Thema Sicherheit solltest du allerdings wirklich nochmal deine Einstellung überdenken. Mag gut sein, dass dich das in einem thread, in dem du ein anderes Problem lösen willst, nervt aber die gesamten Einwände und Hinweise auf deinen Code waren durchaus mehr als berechtigt.
                Ich fände das irgendwie wichtiger als 1 Zeile JS.

                Grüße, Matze

  2. Lieber Klaus,

    $test_pw = md5($password);

      
    sehe ich das richtig, dass Du Passwörter als md5-Hashes speicherst? Ist das auch sinnvoll? Dabei hatte ich immer gedacht, dass man PW gesalzen und verschlüsselt in der DB speichern sollte...  
      
    Liebe Grüße,  
      
    Felix Riesterer.
    
    -- 
    ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
    
    1. Tach!

      $test_pw = md5($password);

      
      >   
      > sehe ich das richtig, dass Du Passwörter als md5-Hashes speicherst? Ist das auch sinnvoll? Dabei hatte ich immer gedacht, dass man PW gesalzen und verschlüsselt in der DB speichern sollte...  
        
      Felix hat recht! [Einfach MD5 oder auch SHA1 ist schon länger keine gute Idee mehr...](http://www.heise.de/security/artikel/Passwoerter-unknackbar-speichern-1253931.html?artikelseite=1)  
        
      Der Link gibt auch eine Hilfestellung.  
        
        
      Jörg Reinholz
      
      1. $test_pw = md5($password);

        
        > >   
        > > sehe ich das richtig, dass Du Passwörter als md5-Hashes speicherst? Ist das auch sinnvoll? Dabei hatte ich immer gedacht, dass man PW gesalzen und verschlüsselt in der DB speichern sollte...  
        >   
          
        Da die Seite keinen Anspruch auf besondere Sicherheit hat (ist nicht über das Internet erreichbar) und über keine hochsensiblen Daten verfügt, sondern lediglich zur Unterscheidung der angemeldeten Person dienen soll, reicht die einfache MD5-Verschlüsselung vorerst aus.  
          
        Dennoch danke für den Hinweis.
        
        1. Hallo!

          Da die Seite keinen Anspruch auf besondere Sicherheit hat (ist nicht über das Internet erreichbar) und über keine hochsensiblen Daten verfügt, sondern lediglich zur Unterscheidung der angemeldeten Person dienen soll, reicht die einfache MD5-Verschlüsselung vorerst aus.

          Da frage ich mich doch warum überhaupt ein Hash? Also entweder vernünftig oder du kannst es ganz weg lassen. Wobei du auch bei internen Netzwerken nicht darauf vertrauen solltest, dass sich alle immer so verhalten wie du es gern hättest und ein sicherer Hash ist auch kein Hexenwerk.

          Grüße, Matze

          1. ...  und ein sicherer Hash ist auch kein Hexenwerk.

            Und daher auch kein A-Thema im Moment. Eine bei allen Browsern funktionierende Anmeldung aber schon.

          2. Hi,

            Da frage ich mich doch warum überhaupt ein Hash?

            Was sonst?

            Willst Du die Paßwörter direkt als Klartext in die Datenbank legen?
            So daß jeder, der Zugriff auf die DB hat, die Paßwörter kennt?

            Auch eine Verschlüsselung hilft nicht, da sie ja reversibel ist. Damit sind die Paßwörter nicht wesentlich sicherer als wenn sie direkt in der Datenbank liegen. Denn wer an die DB rankommt, wird wohl kaum am Zugriff auf die Scripts scheitern - und damit weiß er, wie er vom verschlüsselten Paßwort zum Klartext-Paßwort kommt.

            (ob MD5 ohne Salt die geeignete Hash-Funktion ist, steht nochmal auf einem anderen Blatt Papier)

            cu,
            Andreas

            --
            Warum nennt sich Andreas hier MudGuard?
            O o ostern ...
            Fachfragen per Mail sind frech, werden ignoriert. Das Forum existiert.
        2. Hallo,

          Da die Seite keinen Anspruch auf besondere Sicherheit hat (ist nicht über das Internet erreichbar) und über keine hochsensiblen Daten verfügt, sondern lediglich zur Unterscheidung der angemeldeten Person dienen soll, reicht die einfache MD5-Verschlüsselung vorerst aus.

          Zur Erklärung: MD5 ist keine Verschlüsselung, sondern eine kryptografische Hashfunktion, die mittlerweile als unsicher gilt.

          Das Hashen von Passwörtern dient dazu, das Passwort zu schützen, nicht die Daten, die mit diesem Passwort zugänglich sind. Wenn ein Angreifer an die gehashten Passwörter kommt, kommt er wahrscheinlich ohnehin an die zugehörigen Daten.

          Warum muss das Passwort geschützt werden? Weil Leute Passwörter wiederverwenden. Selbst wenn deine Seite nebensächlich ist, so kann es sein, dass Nutzer das Passwort z.B. für ihr E-Mail-Konto und andere wichtigere Webdienste wiederverwenden.

          Ob die Seite übers Internet zugänglich ist oder nicht, Trojaner infizieren auch Intranets und schicken die gesammelten Daten ins Netz. Das ist nicht unwahrscheinlich, sondern ganz normaler Alltag.

          Sicherheit ist nicht nebensächlich; und diese Problematik zeigt, dass man Sicherheit nirgendwo vernachlässigen kann.

          Grüße,
          Mathias

          1. Warum muss das Passwort geschützt werden? Weil Leute Passwörter wiederverwenden. Selbst wenn deine Seite nebensächlich ist, so kann es sein, dass Nutzer das Passwort z.B. für ihr E-Mail-Konto und andere wichtigere Webdienste wiederverwenden.

            Wer Kennwörter auf verschiedenen Internetseiten wiederverwendet ist selber schuld, denn wer legt seine Hand dafür ins Feuer, dass der Internetseiten-Anbieter bei der Eingabe des Kennworts nicht eine unverschlüsselte Version woanders speichert, zur persönlichen Verwendung, weil's lustig ist, auf Facebook-Seiten anderer nach peinlichen Fotos zu suchen oder lukrativ, die Zugänge an zwielichte Personen zu verkaufen.

            Und wie bereits gesagt wurde, derjenige der bereits unberechtigter Weise Zugriff auf die MD5-Hashs hat, hat warscheinlich auch Zugriff auf die übrigen Inhalte.

            1. Wer Kennwörter auf verschiedenen Internetseiten wiederverwendet ist selber schuld,

              Mag sein. Aber haben wir nicht eine gewisse Mitverantwortung? Man kann auch sagen, wer bei Überqueren der Straße nicht aufpasst sei selber schuld. Deiner Theorie folgend müsste man Geschwindigkeitsbeschränkungen vollständig aufheben. Auch die zwei Tasten an der Presse - weg damit! Es weiß doch ein jeder, dass man den Pressvorgang nicht auslöst wenn man eine Hand drunter hat.

              Tatsächlich ist jeder, der eine Anlage erreichtet und/oder betreibt, dazu verpflichtet diese so gefahrlos wie nur möglich zu erreichten oder zu betreiben - weil eben bekannt ist, dass die Menschen oft fahrlässig handeln. Ich ersuche Dich also, Deinen Algorithmus zu verbessern.

              Das was heise vorschlug, läuft übrigens auf etwas wie das hier heraus:
              Die Konstante PASSWD_ITERATIONS sollte noch angepasst werden, sinnvoll sind Werte ab 5000. Mit 1 Mio Iterationen braucht das Skript bei mir je 1 Sekunde zum Ermitteln des Vielfach-Hashes und zum Prüfen des Passwortes. Je nach Server und Last kann 1 Mio zu viel sein. Aber je höher, so sicherer.

              <?php  
              ## Speicherbedarf des mehrfach gehaschten Passwortes mit Salt: 42 Byte + PASSWD_SALT_LENGHT  
              ## (aus Zeile 10)  
                
              if (! defined('PASSWD_ITERATIONS')) {  
                  define('PASSWD_ITERATIONS', 1000000);  
              }  
                
              if (! defined('PASSWD_SALT_LENGHT')) {  
                  define('PASSWD_SALT_LENGHT', 32);  
              }  
                
                
              #### TEST #####  
              #/* Zum Testen am anfang dieser Zeile Raute (#) vor SlashAsterix(/*) setzen -> #/*  
              $test = craeteSaltedHash('Hallo');  
              print "gehaschtes Passwort: $test\n";  
                
              if (checkSaltedHash('Hallo' , $test) ) {  
                echo "Passt!\n";  
              } else {  
                echo "Nö!!\n";  
              }  
              #*/  
              #### /TEST #####  
                
                
              function craeteSaltedHash($passwd) {  
                if ('' == $passwd) { # oder bessere Prüfung!  
                  return false;  
                }  
                $salt='';  
                for ($i=0; $i<PASSWD_SALT_LENGHT; $i++) {  
                  $salt .= chr( rand( 33, 125 ) );            # ascii von '!' bis '}' - OHNE '~'!  
                }  
                for($i=0; $i<PASSWD_ITERATIONS; $i++) {  
                   $passwd=sha1($salt.$passwd);  
                }  
                return ($salt.'~'.$passwd);  
              }  
                
              function checkSaltedHash($passwd, $saved_string) {  
                list($salt,$rest)=explode('~', $saved_string, 2);  
                # print "salt: '$salt'\n"; #### DEBUG  
                # print "rest: '$rest'\n";   #### DEBUG  
                for($i=0; $i<PASSWD_ITERATIONS; $i++) {  
                   $passwd=sha1($salt.$passwd);  
                }  
                if ( "$passwd" == "$rest" ) {  
                  return true;  
                } else {  
                  return false;  
                }  
              }  
                
              ?>
              

              Jörg Reinholz

              1. Hallo,

                Ich ersuche Dich also, Deinen Algorithmus zu verbessern.

                Es ist i.d.R. nicht sinnvoll, sich so etwas selbst zu bauen.

                Das was heise vorschlug, läuft übrigens auf etwas wie das hier heraus: (…)

                Der Heise-Artikel empfiehlt die fertige Bibliothek phpass, welche bcrypt verwendet, sofern verfügbar. Die nativ ab PHP 5.5 implementierten Funktionen password_hash und hash_pbkdf2 hatte ich schon erwähnt.

                Der Artikel weist auch darauf hin, dass Key-Derivation-Algorithmen wie PBKDF2 und bcrypt besser geeignet sind als Hash-Algorithmen wie MD5 oder SHA, die für Geschwindigkeit optimiert sind. (Davon abgesehen ist SHA-1 mittlerweile veraltet.) Wenn man von Krypto wenig Ahnung hat, sollte man besser Bibliotheken und Funktionen von Leuten verwenden, die wissen, was sie tun.

                Mathias

                1. Die nativ ab PHP 5.5 implementierten Funktionen password_hash und hash_pbkdf2 hatte ich schon erwähnt.

                  Na, wenn Du schon hash_pbkdf2 erwähnst, dann solltest Du aber auch erwähnen, dass man keinen statischen (für alle gehashed zu speichernden Passwörter gleichen) salt verwenden sollte.

                  Damit wären wir dann aber wieder - abgesehen vom verwendeten Hash-Algorithmus - bei meiner Beispiel-Implementation, bei der JEDES Passwort einen EIGENEN, ZUFÄLLIGEN Salt erhält, der mit dem hash zusammen gespeichert wird. Hier jetzt also das BEISPIEL mit hash() oder pbkdf2().

                  <?php  
                  # Warnung bei einer Umstellung auf PHP 5.5. werden alle Passwörter ungültig!  
                  # Und das ist ein Beispiel...  
                    
                  if (! defined('PASSWD_ITERATIONS')) {  
                      define('PASSWD_ITERATIONS', 1000000);  
                  }  
                    
                  if (! defined('PASSWD_SALT_LENGHT')) {  
                      define('PASSWD_SALT_LENGHT', 32);  
                  }  
                    
                  # ggf. anpassen, gültige Werte aus hash_algos(); Alternativen wären: sha512,tiger192,4, gost, haval256,5  
                  # http://en.wikipedia.org/wiki/List_of_hash_functions  
                  # http://en.wikipedia.org/wiki/Comparison_of_cryptographic_hash_functions  
                  if (! defined('PASSWD_ALGO')) {  
                      define('PASSWD_ALGO', 'sha512');  
                  }  
                    
                  # für PHP > 5.5 ggf. anpassen, gültige werte aus hash_algos()  
                  if (! defined('PASSWD_ALGO_BBKDF2')) {  
                      define('PASSWD_ALGO_BBKDF2', 'sha512');  
                  }  
                    
                    
                  #### TEST #####  
                  #/* Zum Testen am anfang dieser Zeile Raute (#) vor SlashAsterix(/*) setzen -> #/*  
                  $test = craeteSaltedHash('Hallo');  
                  print "gehaschtes Passwort: $test\n";  
                  echo "(", strlen($test), " Zeichen)\n";  
                  echo "Algos: ", implode( ', ', hash_algos() ), "\n";  
                    
                  if (checkSaltedHash('Hallo' , $test) ) {  
                    echo "Passt!\n";  
                  } else {  
                    echo "Nö!!\n";  
                  }  
                  #*/  
                  #### /TEST #####  
                    
                    
                  function craeteSaltedHash($passwd) {  
                    if ('' == $passwd) { # oder bessere Prüfung!  
                      return false;  
                    }  
                    
                    $salt='';  
                    for ($i=0; $i<PASSWD_SALT_LENGHT; $i++) {  
                      $salt .= chr( rand( 33, 125 ) );            # ascii von '!' bis '}' # OHNE '~'!  
                    }  
                    return ( $salt.'~'.my_hash($passwd, $salt) );  
                  }  
                    
                  function checkSaltedHash($passwd, $saved_string) {  
                    list($salt,$rest)=explode('~', $saved_string, 2);  
                    # print "salt: '$salt'\n"; #### DEBUG  
                    # print "rest: '$rest'\n";   #### DEBUG  
                    
                    if ( my_hash($passwd, $salt) == $rest ) {  
                      return true;  
                    } else {  
                      return false;  
                    }  
                  }  
                    
                  function my_hash($passwd, $salt) {  
                    
                    if ( function_exists('hash_pbkdf2') ) {  
                       echo "funktion 'hash_pbkdf2'wird benutzt:\n";  
                       return hash_pbkdf2(PASSWD_ALGO_BBKDF2 , $passwd , $salt , PASSWD_ITERATIONS);  
                    } else { # PHP-Version < 5.5  
                       $passwd_original=$passwd;  
                       for($i=0; $i<PASSWD_ITERATIONS; $i++) {  
                         if (0==$i%2) {  
                  	 $passwd=hash(PASSWD_ALGO, $salt.$passwd);  
                         } else {  
                          $passwd=hash(PASSWD_ALGO, $passwd_original.$passwd);  
                         }  
                       }  
                       return $passwd;  
                    }  
                  }  
                    
                  ?>
                  

                  Wenn man von Krypto wenig Ahnung hat, sollte man besser Bibliotheken und Funktionen von Leuten verwenden, die wissen, was sie tun.

                  Wenn man aber von Kryptographie so fürchterlich viel Ahnung hat, dass man anderen "so locker vom hohen Hocker"  Ahnungslosigkeit vorwerfen kann, dann sollte man die sinntragende Verwendung der selbst empfohlenen Funktionen auch wenigstens kurz erklären. Die Dokumentation sagt nämlich wenig zur sinnvollen Implementierung - also zu der Frage, warum wie viele Iterationen zu verwenden sind und wie ein salt denn nun sinnvoll verwendet wird... Du knallst den Lesern ein paar Begriffe und Funktionen um die Ohren und was dann rauskommt ist mit brauchbarer Wahrscheinlichkeit gleich mal um Größenordnungen unsicherer als mein Beispiel, welches ich mit "etwas wie das hier" ganz genau richtig beschrieben habe.

                  Zum Beispiel dürfte bei Befolgen Deines Vorschlages ein $hash=hash_pbkdf2 ('md5' , $passwd , '' , 1)  nämlich nur wenig bringen, wäre sogar kontraproduktiv, weil der Verwender jetzt irrt - "He! Ich habe doch die empfohlene Funktion hash_pbkdf2() verwendet! Das ist also sicher!"

                  Daneben gibt es noch ein Problem. Die Funktionen sind so ziemlich schwarze Boxen. Was, wenn ich eines Tages von PHP auf eine andere Sprache umstellen will oder muss? In der es hash_pbkdf2() nicht gibt? - Richtig. Dann darf ich mir den Quelltext von PHP anschauen und versuchen herauszubekommen, was diese Funktions-Mäntel (das genau sind diese) eigentlich genau treiben und das nachbauen. Oder meine Benutzer dazu anhalten, über die "Passwort vergessen - Funktion" neue Passwörter einzugeben... - das wäre übrigens auch das, was passiert, wenn das obige Beipspiel verwendet und dann irgendwann z.B. von PHP 5.3 auf PHP 5.5 umgestellt wird. Im aktuellen Ubuntu-Server 12.04 (LTS) ist die PHP-Version (aus dem REPO) übrigens aktuell 5.4.9.

                  Jörg Reinholz

                  1. Die Dokumentation sagt nämlich wenig zur sinnvollen Implementierung - also zu der Frage, warum wie viele Iterationen zu verwenden sind und wie ein salt denn nun sinnvoll verwendet wird... Du knallst den Lesern ein paar Begriffe und Funktionen um die Ohren und was dann rauskommt ist mit brauchbarer Wahrscheinlichkeit gleich mal um Größenordnungen unsicherer als mein Beispiel, welches ich mit "etwas wie das hier" ganz genau richtig beschrieben habe.

                    Beispiel:

                    <?php echo hash("md5" , "hallo" , ""); ?>
                    gibt 598d4c200461b81522a3328565c25f7c aus.

                    <?php echo md5("hallo"); ?>
                    gibt 598d4c200461b81522a3328565c25f7c aus.

                    Das ist natürlich das gleiche Ergebnis. Der Sicherheitsgewinn ist also exakt Null. Du hättest, wenn Du mir schon Ahnungslosigkeit vorhältst, darauf eingehen sollen wie man die von genannten Funktionen benutzt um überhaupt einen Sicherheitsgewinn gegenüber meinem Beispiel erzielen zu können.

                    Jörg Reinholz

                  2. Hallo,

                    $salt='';
                      for ($i=0; $i<PASSWD_SALT_LENGHT; $i++) {
                        $salt .= chr( rand( 33, 125 ) );            # ascii von '!' bis '}' # OHNE '~'!

                    rand() ist meines Wissens keine zuverlässige Quelle für Zufallszahlen zur Verwendung in kryptografischen Verfahren. Dafür gibt es m.W. mycrpt_create_iv oder openssl_random_pseudo_bytes. Aber darauf würde ich nicht vertrauen, sondern eine fertige Bibliothek verwenden. Wenn man die falsch verwendet, reduziert man unter Umständen die Entropie dramatisch.

                    Wenn man von Krypto wenig Ahnung hat, sollte man besser Bibliotheken und Funktionen von Leuten verwenden, die wissen, was sie tun.

                    Wenn man aber von Kryptographie so fürchterlich viel Ahnung hat, dass man anderen "so locker vom hohen Hocker"  Ahnungslosigkeit vorwerfen kann

                    Lieber Jörg, ich werfe hier niemandem etwas vor. Ich habe wenig Ahnung von Kryptographie und anscheinend hast du etwa gleich wenig Ahnung. Hier bewegt sich i.d.R. niemand, der wirklich Ahnung von Krypto hat. Deshalb halte ich es für kontraproduktiv, hier selbstgebastelte Funktionen zu posten.

                    Als Entwickler von Webanwendungen versuchen ich die Verfahren anzuwenden, die Krypto-Experten empfehlen, und die Bibliotheken zu verwenden, die Krypto-Experten entwickelt haben. Nicht mehr und nicht weniger. Ich verstehe nicht, warum man sich nicht eingestehen kann, dass man von Kryptografie Marke Eigenbau besser die Finger lassen sollte, wenn man kein wirklich fundiertes Wissen hat. Das ist nicht speziell an dich gerichtet, sondern an alle Beteiligten.

                    Du knallst den Lesern ein paar Begriffe und Funktionen um die Ohren

                    Nein. Ich habe dazu geraten, ausgereifte Bibliotheken und fertige Funktionen zu verwenden. Das kannst du in meinem Post nachlesen. Sven hat bereits auf eine Bibliothek hingewiesen, die password_hash auch für ältere PHPs bereitstellt. Darauf will ich hinaus.

                    Zum Beispiel dürfte bei Befolgen Deines Vorschlages ein $hash=hash_pbkdf2 ('md5' , $passwd , '' , 1)  nämlich nur wenig bringen, wäre sogar kontraproduktiv

                    Bitte was? Ich habe diesen Vorschlag nicht gemacht, ich habe lediglich auf den Algorithmus und seine Implementierung in PHP hingewiesen.

                    Ja, hash_pbkdf2 ist keine einfach verwendbare Funktion, weil sie im Gegensatz zu password_hash einen Salt erwartet. Das bringt einen in die Verlegenheit, selbst einen Zufallswert berechnen zu müssen – das sollte man besser nicht tun (siehe oben).

                    Die Funktionen sind so ziemlich schwarze Boxen. Was, wenn ich eines Tages von PHP auf eine andere Sprache umstellen will oder muss? In der es hash_pbkdf2() nicht gibt?

                    Jetzt ist aber gut. PBKDF2 ist ein Internetstandard und Implementierungen gibt es in jeder großen Programmiersprache. Dasselbe gilt für bcrypt.

                    Mathias

                    1. rand() ist meines Wissens keine zuverlässige Quelle für Zufallszahlen zur Verwendung in kryptografischen Verfahren.

                      Wen es interessiert, wie der Salt bei password_compat berechnet wird:
                      1. Wenn mcrypt_create_iv() verfügbar ist, wird das verwendet mit /dev/urandom als Quelle
                      2. Alternativ openssl_random_pseudo_bytes()
                      3. Alternativ wird /dev/urandom direkt ausgelesen
                      4. Alternativ wird mt_rand() verwendet (nicht nur ASCII, sondern 0-255)

                      Dieser Bytestream wird dann base64-enkodiert.

                      Wenn ich das richtig sehe, verwendet die C-Implementierung von password_hash auf unixoiden Systemen direkt /dev/urandom.

                      Mathias

                    2. Hallo,

                      $salt='';
                        for ($i=0; $i<PASSWD_SALT_LENGHT; $i++) {
                          $salt .= chr( rand( 33, 125 ) );            # ascii von '!' bis '}' # OHNE '~'!

                      rand() ist meines Wissens keine zuverlässige Quelle für Zufallszahlen zur Verwendung in kryptografischen Verfahren.

                      Das ist hier ziemlich egal. Bei der von mir vorgeschlagenen Länge von 32 Zeichen gibt es 92^32 verschiedene Kombinationen (Als Zahl: 693761947078590541087517725927563750525223139271569629234855936; Als 10er Potenz: ~ 6.9 * 10^62).

                      Selbst wenn rand() unzuverlässig wäre, so dürfte man (mit gegenwärtiger Rechentechnik)

                      for ($i=0; $i<PASSWD_SALT_LENGHT; $i++) {
                          $salt .= chr( rand( 33, 125 ) );
                      }

                      so oft aufrufen müssen, dass der Eigentümer des Passwortes schon _sehr_ lange tot ist, bis ein vorher ermittelter, beliebiger Salt sich wiederholt. Und wie viele Passwörter werden wohl auf diese Weise "gesalzen"?

                      Jörg Reinholz

                      1. rand() ist meines Wissens keine zuverlässige Quelle für Zufallszahlen zur Verwendung in kryptografischen Verfahren.

                        Das ist hier ziemlich egal.

                        Das denke ich nicht. rand() liefert m.W. vorhersehbare Zufallszahlen, wenn der Seed bekannt ist. Deshalb steht in der PHP-Doku auch groß und breit dran, dass man es nicht für Krypto verwenden soll. Jeder Artikel, den ich finde, rät dazu, in PHP nicht rand() oder mt_rand() für Initialisierungsvektoren oder Hash-Salts zu verwenden.

                        Noch einmal: Ich weiß nicht, warum man hier sämtliche Ratschläge von Experten ignorieren sollte. Warum man dezidierte Krypto-Bibliotheken wie OpenSSL und mcrypt ignorieren sollte. Warum man dezidierte Quellen für Pseudozufallszahlen wie /dev/urandom ignorieren sollte. Warum man ignorieren sollte, was in der PHP-Doku steht. Warum man ignorieren sollte, wie PHP es intern implementiert. Warum man ignorieren sollte, wie fertige PHP-Bibliotheken es implementieren. Warum man sich selbst etwas basteln sollte.

                        Mathias

                        1. Noch einmal: Ich weiß nicht, warum man hier sämtliche Ratschläge von Experten ignorieren sollte.

                          NIST macht MIST.

                          Warum man dezidierte Krypto-Bibliotheken wie OpenSSL und mcrypt ignorieren sollte.
                          Warum man dezidierte Quellen für Pseudozufallszahlen wie /dev/urandom ignorieren sollte.

                          weil bei einem

                          $salt=dd if=/dev/urandom/ bs=32 count=1;

                          der dedlfix geschrieben hätte, man solle doch bitte, um Windows-Hoster nicht zu verärgern, in PHP bleiben statt Systemaufrufe zu tätigen?

                          Warum man ignorieren sollte, was in der PHP-Doku steht.

                          Weil da auch rand(), dann srand() mal als toller Zufallsgenerator gepriesen wurde? Es gibt, wenn er so besch... ist, wie Du behauptest, für die Macher von PHP keinen Grund den nicht zu reparieren.

                          Warum man ignorieren sollte, wie PHP es intern implementiert.

                          siehe rand();

                          Warum man ignorieren sollte, wie fertige PHP-Bibliotheken es implementieren.

                          siehe rand();

                          Warum man sich selbst etwas basteln sollte.

                          siehe das hier
                          Und weil man dann weiß, was das Ding macht, es also in anderen Sprachen ebenso bauen und bisherige Daten verwenden kann.

                          Und es gibt noch einen Grund. Wenn ich zeigen will man ein Brot bäckt, dann sage ich auch nicht:

                          • Man nehme die tolle Backmischung von Dr. Oh. und Wasser
                          • Man forme den Teig und stelle ihn 60 Minuten in den tollen Brotbackofen von S.

                          (Nachricht am nächsten Tag:  Backmischung von Dr. Oh. enthält krebserregende Stoffe)
                          (Nachricht am übernächsten Tag:  Brotbackofen von S. vergiftet Brot mit Schwermetallen)

                          Jeder Artikel, den ich finde, rät dazu, in PHP nicht rand() oder mt_rand() für Initialisierungsvektoren
                          oder Hash-Salts zu verwenden.

                          Soso. Du hast was übersehen. Ich verwende rand() nicht einmal, sondern 32 mal nacheinander und baue dann an einem String herum. Dazwischen passiert viel "Entropie". Und zwar andere "Entropie" als beim vorherigen und nächsten 32-maligen verwenden von rand(). Für Hashes mag das genügen, bei Initialierungsvektoren sehe ich das durchaus kritischer.

                          Es bleibt dabei. Wenn Du 100 Mio Salts mit meiner Methode erzeugst, dann erzeugst Du mit einer enormen Wahrscheinlichkeit 100 Mio verschiedene salts. Insgesamt gibt es (bei meiner Methode) theoretisch 6.9*10^32 verschiedene Salts - aller 10^26 (geschätzt, nicht gerechnet) Versuche hast Du also in jeweils 10 Mio Salts zwei gleiche. Und nur auf diese Diversität kommt es an! Denn die Salts sind ja sowieso bekannt - die stehen in den Daten!

                          Nochmal: Wichtig ist nur, in den 10 Mio (die Zahl ist nur ein Beispiel) Salts möglichst viele verschiedene sind. Auch 99.999.999 (und noch einer, der schon mal da war) reicht wohl um einem, der sich an das Ermitteln der Klartextpasswörter macht, das Leben sauer zu machen, denn er müsste für jeden Salt gigantische Rainbow-Tables errechnen- weisst Du was das kostet?

                          1. Tach!

                            Warum man dezidierte Quellen für Pseudozufallszahlen wie /dev/urandom ignorieren sollte.
                            weil bei einem
                            $salt=dd if=/dev/urandom/ bs=32 count=1;
                            der dedlfix geschrieben hätte, man solle doch bitte, um Windows-Hoster nicht zu verärgern, in PHP bleiben statt Systemaufrufe zu tätigen?

                            Der Hoster ist mir egal. Es geht nur darum, PHP-Funktionen zu verwenden, wenn diese vorhanden sind, und nicht aufgrund von deren Unkenntnis Shellprozesse zu starten. Der Aufwand ist nur gerechtfertigt, wenn es begründet ist. Das wäre hier der Fall, wenn openssl_random_pseudo_bytes() nicht zur Verfügung steht.

                            Warum man ignorieren sollte, was in der PHP-Doku steht.
                            Weil da auch rand(), dann srand() mal als toller Zufallsgenerator gepriesen wurde? Es gibt, wenn er so besch... ist, wie Du behauptest, für die Macher von PHP keinen Grund den nicht zu reparieren.

                            srand() ist kein Zufallsgenerator. rand() muss nicht repariert werden, weil es nicht kaputt ist. Es ist nur nicht für kryptografische Zwecke vorgesehen. Für weniger wichtige Zufälle ist er weiterhin geeignet.

                            Soso. Du hast was übersehen. Ich verwende rand() nicht einmal, sondern 32 mal nacheinander und baue dann an einem String herum. Dazwischen passiert viel "Entropie". Und zwar andere "Entropie" als beim vorherigen und nächsten 32-maligen verwenden von rand().

                            Für diese Entropie interessiert sich rand() nicht. Wenn man mit srand() seedet, erzeugt rand() immer die gleiche Zufallszahlenreihenfolge. Und das ist auch nicht verkehrt, wenn man nachvollziehbaren Zufall braucht. mt_(s)rand() ist in der Hinsicht auch nicht anders. Beide sind deshalb nicht für den hier vorligenden Zweck geeignet.

                            Nochmal: Wichtig ist nur, in den 10 Mio (die Zahl ist nur ein Beispiel) Salts möglichst viele verschiedene sind. Auch 99.999.999 (und noch einer, der schon mal da war) reicht wohl um einem, der sich an das Ermitteln der Klartextpasswörter macht, das Leben sauer zu machen, denn er müsste für jeden Salt gigantische Rainbow-Tables errechnen- weisst Du was das kostet?

                            Das neue NSA-Rechenzentrum kostet sicher auch eine ganze Menge. Aber wer sagt denn, dass man Rainbow-Tabellen erzeugen muss, wenn man durch unsicheren Zufall das Brute-Force einfacher haben kann?

                            dedlfix.

                            1. Wenn man mit srand() seedet, erzeugt rand() immer die gleiche Zufallszahlenreihenfolge.

                              Ich sehe ganz deutlich das Wort "wenn". Ich sah in meinem Beispiel kein srand(). Was also, wenn nicht? Was also wenn nicht einmal bekannt ist, wann und wie oft rand() aufgerufen wurde um wann den salt für welchen Hash zu bilden? Richtig. Dann mischt der echte, software- und maschinenunabhängige Zufall genügend mit.

                              selbst ETWAS wie ein simples

                                
                              srand(microtime(true) - intval(str_replace(' ','', str_replace('.','', file_get_contents('/proc/uptime')))));  
                              
                              

                              dürfte einen Angreifer vor erhebliche Probleme stellen.

                              Aber wer sagt denn, dass man Rainbow-Tabellen erzeugen muss, wenn man durch unsicheren Zufall das Brute-Force einfacher haben kann?

                              Weil ja immer noch das Passwort aus einem Hash erraten werden muss und Rainbow-Tabellem für "lange" Strings wenig praktikabel sind?

                              1. Hallo,

                                Wenn man mit srand() seedet, erzeugt rand() immer die gleiche Zufallszahlenreihenfolge.

                                Ich sehe ganz deutlich das Wort "wenn". Ich sah in meinem Beispiel kein srand().

                                »The random number generator is seeded automatically.« (http://de2.php.net/rand)

                                Im PHP-Code:

                                if (!BG(rand_is_seeded)) {  
                                    php_srand(GENERATE_SEED() TSRMLS_CC);  
                                }
                                

                                #define GENERATE_SEED() (((long) (time(0) * getpid())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))

                                php_combined_lcg

                                selbst ETWAS wie ein simples

                                srand(microtime(true) - intval(str_replace(' ','', str_replace('.','', file_get_contents('/proc/uptime')))));

                                
                                > dürfte einen Angreifer vor erhebliche Probleme stellen.  
                                  
                                Etwas ähnliches macht PHP intern, siehe oben.  
                                  
                                Trotzdem ist es relativ einfach, den Seed des Zufallszahlengenerators herauszubekommen, siehe bspw. <http://crypto.di.uoa.gr/CRYPTO.SEC/Randomness_Attacks_files/paper.pdf> und <http://blog.ptsecurity.com/2012/08/not-so-random-numbers-take-two.html>. Von solchen Attacken habe ich immer wieder gehört.  
                                  
                                Und wieder ist die Moral von der Geschicht’: Verwende (mt\_)rand() für Krypto nicht.  
                                  
                                Mathias
                                
                                  1. siehe bspw. http://crypto.di.uoa.gr/CRYPTO.SEC/Randomness_Attacks_files/paper.pdf und http://blog.ptsecurity.com/2012/08/not-so-random-numbers-take-two.html.

                                    Gibt’s auch als einstündiges Video:
                                    http://www.youtube.com/watch?v=PQiL1Pwco5A
                                    Hochinteressant!

                                    Ja. Meinetwegen. Ist aber von anderen Voraussetzungen abhängig. Gneua genommen ein Bypass-Angriff, bei dem weitere Informationen z.B. aus http-headern verarbeitet werden.

                                1. Trotzdem ist es relativ einfach, den Seed des Zufallszahlengenerators herauszubekommen, siehe bspw. http://crypto.di.uoa.gr/CRYPTO.SEC/Randomness_Attacks_files/paper.pdf und http://blog.ptsecurity.com/2012/08/not-so-random-numbers-take-two.html. Von solchen Attacken habe ich immer wieder gehört.

                                  Beide Beispiele beruhen aber darauf, dass der Angreifer wichtige Informationen über einen Bypass bekommt. So wird z.B. eine session_id  mit md5 aus IP, dem Zeitpunkt und einem sehr eingeschränktem Zufallswert ermittelt. Das ist relativ einfach angreifbar.

                                  Und wieder ist die Moral von der Geschicht’: Verwende (mt_)rand() für Krypto nicht.

                                  Nochmal: Ich erzeuge den Salt mit einer Funktion, die rand() verwendet.
                                  Den Salt speichere ich sogar, denn das geht auch gar nicht anders. Den Salt kann der Angreifer also immer dann haben, wenn er das gehaschte Passwort haben kann. Es ist also scheiß-egal ob rand ggf, erratbare Zufallswerte liefert, Hauptsache ist: Ich habe 10000 verschiedene Hashes! Er muss (im Beispiel) nur am ersten "~" trennen oder darauf vertrauen, dass der immer gleich lang ist (oder halt darauf, dass die hash-Funktion immer einen Hash gleicher Länge produziert).

                                  Das Problem für den Angreifer ist bei meiner Funktion außerdem, dass die Hashes und ergo zuvor die Salts zu völlig unterschiedlichen Zeitpunkten berechnet werden - von denen der Angreifer eben nichts weiß. Er kennt auch die Reihenfolge nicht in der die Salts ermittelt wurden.

                                  Anders als in den von Dir verlinkten Beispielen weiß er also gar nichts. Ich wüsste jetzt nichts, was seinen Aufwand so verringert, wenn er 10.000 solche Passwörter mit je einem bekanntem - aber unterschiedlichem - Salt bekommt und die knacken soll, dass er jenseits eines Wörterbuchangriffs, eines Angriffs auf schlechte hash-Methoden, eines Rainbow-Table-Angriffs oder gar eines brutalen, zeitfressenden brut-force-Angriffs auf jedes einzelne Passwort (das geht bei genug Zeit und Rechenpower theoretisch immer) eine reale Chance hat, alle 10.000 Passwörter innerhalb eines brauchbaren Zeitrahmens und mit (auch von der NSA) bezahlbarer Hardware zu knacken - was ja seine Aufgabe ist.

                                  Rainbow-Tables sind bei hinreichend langen Salts kaum noch zu händeln.

                                  Zitat:

                                  "Die Größe einer Rainbow Table steigt mit der Länge der Kennwörter, für die sie generiert werden soll. Je nach Hashtyp ist ab einer gewissen Kennwortlänge die Berechnung einer Rainbow Table nicht mehr wirtschaftlich, sowohl was die Dauer der Generierung als auch den Platzbedarf der Tabellen angeht."

                                  Im Fall von gehaschten Kennwörtern ist diese Länge: Länge des Passwortes + die Länge des Hashes.

                                  Und weiter:

                                  "Der Aufwand kann weiter erhöht werden, wenn ein Passwort nicht einfach, sondern mehrfach gehasht wird - üblich sind mehrere tausend Iterationen. Erst beide Methoden kombiniert ergeben ein Hashing-Verfahren, welches eine gewisse Resistenz gegen typische Angriffsmethoden aufweist. Das Salt macht das Erstellen von Tabellen unwirtschaftlich oder gar unmöglich, zusammen mit den Iterationen werden Brute-Force-Angriffe erheblich verlangsamt."

                              2. Moin!

                                Wenn man mit srand() seedet, erzeugt rand() immer die gleiche Zufallszahlenreihenfolge.

                                Ich sehe ganz deutlich das Wort "wenn". Ich sah in meinem Beispiel kein srand(). Was also, wenn nicht? Was also wenn nicht einmal bekannt ist, wann und wie oft rand() aufgerufen wurde um wann den salt für welchen Hash zu bilden? Richtig. Dann mischt der echte, software- und maschinenunabhängige Zufall genügend mit.

                                Es gibt wohl den dokumentierten Fall, dass der Einsatz der Software A (nennen wir sie zum Beispiel Wordpress) und die damit verbundene Nutzung eines ungeseedeten rand()-Zufallszahlengenerators zur Passworterzeugung (also so, wie man es machen sollte) kompromittiert wurde durch den parallelen Einsatz der Software B (beispielsweise nehmen wir PHPBB an), die den Zufallszahlengenerator für unwesentliche Dinge wie zufällige Bild- und Farbdarstellung hernahm, und dummerweise einen schlechten, vorhersagbaren Seed anwendete.

                                Details müsste ich aufwendig googlen, das ist im Moment nicht die richtige Zeit - allein diese Konstruktion, auch nur theoretisch durchdacht, ist in meinen Augen nachvollziehbar, da der Seed der Zufallszahlen für alle PHP-Prozesse eine globale Ressource ist.

                                Mit bekanntem Seed und nur einer unbekannten Anzahl von rand()-Aufrufen ist die einzige "Sicherheit", dass der Angreifer zu doof ist, die diversen Möglichkeiten durchzuspielen.

                                selbst ETWAS wie ein simples

                                srand(microtime(true) - intval(str_replace(' ','', str_replace('.','', file_get_contents('/proc/uptime')))));

                                
                                > dürfte einen Angreifer vor erhebliche Probleme stellen.  
                                  
                                Microtime ist grob bekannt. Wenn man's möchte, ist /proc/uptime ein Wert nahe Null (Maschine durch Überlast zum Neustart zwingen, oder Neustart durch Dauer-Ping beobachten). Oder er ist PHP\_INT\_MAX groß - das ist bei 32-Bit-Linux-Systemen, sowie derzeit auch bei 64-Bit-Windows-PHP 2147483647.  
                                  
                                Man zieht also eine bekannte Konstante von der aktuellen Zeit ab. FAIL!  
                                  
                                
                                > > Aber wer sagt denn, dass man Rainbow-Tabellen erzeugen muss, wenn man durch unsicheren Zufall das Brute-Force einfacher haben kann?  
                                >   
                                > Weil ja immer noch das Passwort aus einem Hash erraten werden muss und Rainbow-Tabellem für "lange" Strings wenig praktikabel sind?  
                                  
                                Rainbow-Tabellen werden kaum noch genutzt. Sie bringen keinen wirklichen Gewinn mehr. Der Aufwand der Erzeugung und Speicherung steht in keinem Verhältnis zur schlichten Bearbeitung mit Brute Force und relativ günstiger Hardware.  
                                  
                                 - Sven Rautenberg
                                
                                1. Microtime ist grob bekannt.

                                  Woher denn? Wer will woher (und wozu) wissen, wann das Passort gesalzen, gehasht und gespeichert wurde?

                                  Wenn man's möchte, ist /proc/uptime ein Wert nahe Null (Maschine durch Überlast zum Neustart zwingen, oder Neustart durch Dauer-Ping beobachten).

                                  ja klar. Aber die Passwörter sind da bereits gehashed.

                                  Man zieht also eine bekannte Konstante von der aktuellen Zeit ab. FAIL!

                                  Fail?

                                  Nochmal:

                                  (1)
                                  Es kommt nur darauf an, dass jedes Passwort zusammen mit einem ANDEREN (und für dieses Passwort konstantem Salt) mehrfach gehasht wurde.

                                  (2)
                                  Ob der Salt vorhersehbar ist, das ist absolut SCHEISSEGAL. Und zwar weil der Salt zusammen mit dem Passwort gespeichert wird. Der Salt muss nämlich auch bekannt sein um das Passwort zu prüfen. Üblich ist es, den Salt zusammen mit dem Hasch zu speichern. In meiner Funktion 'craeteSaltedHash($passwd)' wird er auch so zurück gegeben:

                                  return ( $salt.'~'.my_hash($passwd, $salt) );

                                  Also:

                                  Wenn ein Angreifer aus den Salt 0  bis x-1  vorhersagen kann, dass der Salt für das Passwort x "foobar" ist, dann ist das egal, weil er das auch DIREKT LESEN kann. Die Mühe, die Vorhersage zu treffen, ist schlicht und einfach völlig sinnlos.

                                  heise schreibt im bezogenen Artikel:
                                  ---------------------
                                  Bcrypt nutzt einen aufwändigen Schlüsselinitialisierungsalgorithmus und verschlüsselt das resultierende Chiffrat immer wieder abwechselnd mit dem Salt und dem Passwort. Die Zahl der Runden ist eine Potenz von 2, der benutzte Exponent wird der erzeugten Zeichenkette vorangestellt. Diese hat üblicherweise folgendes Format:

                                  $2a$08$Ra4upKLreqDA18E/OtFSIu/ED6iTmorUKyNJF6aVwbpO9AIBS/j7u

                                  Die am Anfang zwischen die Dollarzeichen eingeklemmte Zeichenkette 2a steht für den bcrypt-Algorithmus, die darauffolgende Zahl 08 signalisiert den Exponten zur Potenz 2 – 2 hoch 8 ergibt die Anzahl der Runden: 256. Die sich anschließende Zeichenfolge ist das 16-stellige Salt und das verschlüsselte Passwort.
                                  ---------------------

                                  Noch mal, weil es so schön ist:

                                  "Die sich anschließende Zeichenfolge ist das 16-stellige Salt."
                                  Im Beispiel von heise "Ra4upKLreqDA18E".
                                  Und nochmal: Es entspricht voll und ganz der Idee des Salzenz, dass der Salt bekannt ist.

                                  Damit ist aber auch jede Kritik an meiner Methode, den Salt "quasizufällig" zu bilden, glatt für den Arsch.

                                  Rainbow-Tabellen werden kaum noch genutzt. Sie bringen keinen wirklichen Gewinn mehr. Der Aufwand der
                                  Erzeugung und Speicherung steht in keinem Verhältnis zur schlichten Bearbeitung mit Brute Force und
                                  relativ günstiger Hardware.

                                  Wenn es denn so wäre könnte man auf das Salzen ganz verzichten.

                        2. Warum man ignorieren sollte, was in der PHP-Doku steht.

                          Dafür, und den will ich noch mal herausstellen, gibt es einen Grund:

                          (1)
                          Ich bin nicht zu doof um als Themenbereich "PHP" auszuwählen. Ich habe aber "Programmiertechnik" ausgewählt. Hat mal jemand darüber nachgedacht, warum?

                          (2)
                          Die Überschrift lautet: "Das, was heise vorschlug in einf. Code gegossen"
                          Es geht also um einfachen und verständlichen Code ohne zu viele "schwarze Boxen". Das "einf." musste ich abkürzen. Kann natürlich sein, dass es zu schwer war zu erraten, was ich damit meine. Nämlich einfachen also verständlichen Code, bei dem nicht jeder die Dokumentation und reichlich Drittquellen bemühen muss.

                          (3)
                          Ich schrieb über dem Code: "Das was heise vorschlug, läuft übrigens auf etwas wie das hier heraus:"
                          Noch mal: "ETWAS WIE DAS HIER"

                          (4)
                          Meine Fragen an Dich:

                          Habe ich damit irgend jemanden gesagt, er soll es so und nicht anders machen?

                          • Meine Ansicht: Das habe ich nicht.

                          Habe ich damit irgend jemanden gesagt, das Skript soll jemand so verwenden?

                          • Meine Ansicht: Das habe ich nicht. Das geht auch gar nicht daraus einiges zu löschen.

                          Habe ich gesagt, dieses sei die einzige, richtige und wahre Lösung?

                          • Meine Ansicht: Das habe ich nicht.

                          Habe ich dargestellt, dass es darum geht, WIE (sic!) man Passwörter zum Zweck des Speicherns salzt und mehrfach hashed?

                          • Meine Ansicht: Das genau habe ich sehr wohl getan!

                          Habe ich was falsch gemacht, also ich funktionierenden PHP-Code präsentierte?

                          • Meine Ansicht:  Nein. Die Menschen lernen am besten an praktischen Beispielen. Man kann damit nämlich herumspielen, sich Ergebnisse ansehen etc. pp.

                          Habe ich was falsch gemacht, also ich auf sehr spezielle und bei vielen noch nicht einmal verfügbaren Funktionen verzichtete?

                          • Meine Ansicht:  Nein. Ich wollte ja zeigen, WIE es PRINZIPIELL geht.

                          (5)
                          Das die von euch benannten, speziellen und teils noch gar nicht auf den Webservern verfügbaren PHP-Funktionen (eigentlich: Funktionsmäntel) wie hasch() und hash_pbkdf2() letztendlich auch noch der gleichen oder einer ähnlichen Implementierung bedürfen und bei denen zudem ohne die weiteren von mir gezeigtes Informationen die Gefahr besteht, dass diese falsch angewendet werden und deshalb zu unsicheren Ergebnissen führen zeigt mir, dass auch die Kritiker der Elche selber welche sind.

                          Das mit dem Salt halte ich für ausdiskutiert. In meinem Beispiel sind es 32 Zeichen aus einer Auswahl von 92. Einem so schlechten Zufallsgenerator, der hier binnen etlicher Billionen Versuche einen salt zweimal erzeugt, den gibt es auch in PHP - jenseits auf die Version beschränkter Bugs - nicht.

                          Jörg Reinholz

                          1. Hallo,

                            Nämlich einfachen also verständlichen Code, bei dem nicht jeder die Dokumentation und reichlich Drittquellen bemühen muss.

                            Einfacher Code ist nicht notwendig produktionsreifer Code und schon gar nicht kryptographisch sicherer Code.

                            Habe ich dargestellt, dass es darum geht, WIE (sic!) man Passwörter zum Zweck des Speicherns salzt und mehrfach hashed?

                            • Meine Ansicht: Das genau habe ich sehr wohl getan!

                            Ein grundlegendes Verfahren in Pseudocode zu beschreiben ist natürlich in Ordnung. Das hat der von dir verlinkte Heise-Artikel ja auch getan, das tut auch jeder Grundlagenartikel zu bcrypt, PBKDF2 oder Key Stretching im Allgemeinen.

                            Ich schrieb über dem Code: "Das was heise vorschlug, läuft übrigens auf etwas wie das hier heraus:"
                            Noch mal: "ETWAS WIE DAS HIER"

                            Wenn hier Code gepostet wird, muss man damit rechnen, dass ihn jemand kopiert. Er ist für die »Ewigkeit« konserviert und in Suchmaschinen auffindbar. Was meinst du, warum ich hier die Sicherheitslücken im Ausgangsposting moniert habe. Nicht, um irgendwen zu ärgern, vorzuführen oder weil ich zuverlässig weiß, dass die Lücken in der Anwendung des Fragenden exploitbar sind. Sondern ganz allein weil der Code hier gepostet wurde und hier viele Leute voneinander lernen.

                            Habe ich damit irgend jemanden gesagt, er soll es so und nicht anders machen?

                            Die Vehemenz, mit der du deine selbstgebaute Funktion verteidigt hast und gültige Kritik heruntergespielt hast, ist schon erstaunlich.

                            Habe ich was falsch gemacht, also ich funktionierenden PHP-Code präsentierte?

                            • Meine Ansicht:  Nein. Die Menschen lernen am besten an praktischen Beispielen.

                            Ein sinnvolles *praktisches* Beispiel wäre Code, der nach aktuellen Stand sicher, produktionsreif und direkt kopierbar ist.

                            Habe ich was falsch gemacht, also ich auf sehr spezielle und bei vielen noch nicht einmal verfügbaren Funktionen verzichtete?

                            • Meine Ansicht:  Nein. Ich wollte ja zeigen, WIE es PRINZIPIELL geht.

                            Es geht sowohl prinzipiell als auch praktisch mit password_hash() + password_compat ab der PHP-Version 5.3.7. Warum nicht das zeigen?

                            Einem so schlechten Zufallsgenerator, der hier binnen etlicher Billionen Versuche einen salt zweimal erzeugt, den gibt es auch in PHP - jenseits auf die Version beschränkter Bugs - nicht.

                            Es geht nicht um das mehrmalige Erzeugen von Salts. rand() nutzt einen Kongruenzgenerator, um aus einem gegebenen Seed deterministisch Folgezahlen zu erzeugen. Ist der Seed bekannt, sind sämtliche folgenden Zahlen herleitbar. Das ist etwas anderes als ein nicht-deterministischer, kryptografisch sicherer Zufallsgenerator, der z.B. im Linux-Kernel hinter /dev/(u)random steckt.

                            Du hast hier schon ein Posting eines Linux-Kernel-Maintainers verlinkt, indem er sich glücklich schätzt, dass der Linux-Kernel verschiedene Entropiequellen nutzt, nicht nur einen Hardware-Pseudozufallszahlen-Generator, der nicht unabhängig untersucht werden kann. Das ist schön, jetzt sollte man /dev/random auch nutzen. Am besten über plattformübergreifende und ausgereifte Bibliotheken bzw. Funktionen im PHP-Kern.

                            Mathias

                            1. Hallo,

                              Nämlich einfachen also verständlichen Code, bei dem nicht jeder die Dokumentation und reichlich Drittquellen bemühen muss.

                              Einfacher Code ist nicht notwendig produktionsreifer Code und schon gar nicht kryptographisch sicherer Code.

                              Nochmal. Seit wann bedeutet "Das was heise vorschlug, läuft übrigens auf etwas wie das hier heraus:", dass es sich um produktionsreifen Code handelt?

                              Habe ich dargestellt, dass es darum geht, WIE (sic!) man Passwörter zum Zweck des Speicherns salzt und mehrfach hashed?

                              • Meine Ansicht: Das genau habe ich sehr wohl getan!

                              Ein grundlegendes Verfahren in Pseudocode zu beschreiben ist natürlich in Ordnung. Das hat der von dir verlinkte Heise-Artikel ja auch getan, das tut auch jeder Grundlagenartikel zu bcrypt, PBKDF2 oder Key Stretching im Allgemeinen.

                              Ich schrieb über dem Code: "Das was heise vorschlug, läuft übrigens auf etwas wie das hier heraus:"
                              Noch mal: "ETWAS WIE DAS HIER"

                              Wenn hier Code gepostet wird, muss man damit rechnen, dass ihn jemand kopiert. Er ist für die »Ewigkeit« konserviert und in Suchmaschinen auffindbar. Was meinst du, warum ich hier die Sicherheitslücken im Ausgangsposting moniert habe.

                              Die sind in offenbar zur späteren Produktivität enthalten.

                              Nicht, um irgendwen zu ärgern, vorzuführen

                              Aber Worte wie "Wenn man von Krypto wenig Ahnung hat, sollte man besser Bibliotheken und Funktionen von Leuten verwenden, die wissen, was sie tun." verwenden ...

                              Habe ich damit irgend jemanden gesagt, er soll es so und nicht anders machen?

                              Die Vehemenz, mit der du deine selbstgebaute Funktion verteidigt hast und gültige Kritik heruntergespielt hast, ist schon erstaunlich.

                              Das mache ich gar nicht. Ich wehre mich dagegen, dass und in welchen Ton mein Beispiel - ohne ernst zu nehmende Konstruktive Kritik - runter gemacht wird.

                              Wie gesagt, einfach mal nur Funktionsnamen und bestenfalls den Link zum englischem(!) Handbuch  in die Runde zu werfen und - ohne jegliche wichtige Ergänzungen - auszusagen, diese zu verwenden sei sicher - mein Skript aber von jemanden, der wenig Ahnung habe das finde ich nicht nur "Scheiße", das ist es auch.

                              Habe ich was falsch gemacht, also ich funktionierenden PHP-Code präsentierte?

                              • Meine Ansicht:  Nein. Die Menschen lernen am besten an praktischen Beispielen.

                              Ein sinnvolles *praktisches* Beispiel wäre Code, der nach aktuellen Stand sicher, produktionsreif und direkt kopierbar ist.

                              Nochmal: Ich habe den Code nie als praktisches Beispiel bezeichnet. Es steht sogar deutlich drin, dass Teile fehlen, z.B. zur Überprüfung des Passwortes. Ich habe ihn als einfachen Code bezeichnet.

                              Habe ich was falsch gemacht, also ich auf sehr spezielle und bei vielen noch nicht einmal verfügbaren Funktionen verzichtete?

                              • Meine Ansicht:  Nein. Ich wollte ja zeigen, WIE es PRINZIPIELL geht.

                              Es geht sowohl prinzipiell als auch praktisch mit password_hash() + password_compat ab der PHP-Version 5.3.7. Warum nicht das zeigen?

                              Aus dem Handbuch:
                              string password_hash ( string $password , integer $algo [, array $options ] )

                              Fein, den Algorithmus darf man wieder selbst angeben, als options['salt'] darf man dann den hash wieder selbst übermitteln - das läuft also auf das selbe hinaus wie hash_pbkdf2() - und ist eine Blackbox, weil wieder keiner weiß, was das Ding treibt. Was $options['cost'] bewirkt erschliest sich aus der Dokumenattion auch nach dem Folgen von einem halben Dutzend Links nicht. Nur dass es zwischen 4 und 31 liegen soll. und das in PHP 5.3.7 was repariert wurde. Soweit zu "ausgereifte Bibliotheken bzw. Funktionen".

                              Und ergo ist password_hash() insoweit dann auch unbrauchbar, weil das Ergebnis deshalb nicht ohne weiteres in anderen Sprachen zu erzeugen oder zu überprüfen ist.

                              Einem so schlechten Zufallsgenerator, der hier binnen etlicher Billionen Versuche einen salt zweimal erzeugt, den gibt es auch in PHP - jenseits auf die Version beschränkter Bugs - nicht.

                              Es geht nicht um das mehrmalige Erzeugen von Salts. rand() nutzt einen Kongruenzgenerator, um aus einem gegebenen Seed deterministisch Folgezahlen zu erzeugen. Ist der Seed bekannt, sind sämtliche folgenden Zahlen herleitbar. Das ist etwas anderes als ein nicht-deterministischer, kryptografisch sicherer Zufallsgenerator, der z.B. im Linux-Kernel hinter /dev/(u)ranpassword_hashdom steckt.

                              Und? Ist der seed bekannt? Ist er es - oder ist er es nicht? Er ist es nicht. Alles hinter "Ist der Seed bekannt" ist damit unzutreffend, also sind sämtliche folgenden Zahlen NICHT herleitbar.

                              jetzt sollte man /dev/random auch nutzen. Am besten über plattformübergreifende und ausgereifte Bibliotheken bzw. Funktionen im PHP-Kern.

                              So lange mir keiner erklärt wo der PHP-Kern "/dev/random" unter Windows findet habe ich viel Mühe dieser Funktionen als "plattformübergreifend" anerkennen. Zumal sich mir nicht erschließt, wieso ich dann nicht gleich das Original ( file_get_contents('/dev/random', false, NULL, 0, 32) ) sondern so ziemlich neue Funktionen (aus PHP 5.3 - das auch noch nicht auf jedem Webserver läuft...) wie "openssl_random_pseudo_bytes()" nehmen soll.

                              Und wenn password_hash() vor 5.3.7 Probleme hatte, woher soll ich dann wissen, ob openssl_random_pseudo_bytes() nicht auch Probleme macht? Zumal die "Bytes" ja auch pseudo-zufällig sind. (Handbuch: "Generates a string of pseudo-random bytes") Wovon die Pseudo-Zufälligkeit hier abhängt weiß ich nicht. Bei rand() weiß ich es und ich finde es besser, das zu wissen - weil ich dem dann begegnen kann.

                              und etwas wie "$salt=dd if=/dev/random bs=32 count=1{:.language-php} oder `$salt=`dd if=/dev/urandom bs=32 count=1 (blockiert nicht, ist aber unsicherer, weil er nach dem Ausschöpfen der "Entropiequellen" kurzerhand Pseudozufall ausliefert)" darf ich hier ja nicht mehr schreiben.

                              In einem richtigen Programm würde ich es aber ohne jede Hemmung machen.

                              1. Hallo,

                                Ich wehre mich dagegen, dass und in welchen Ton mein Beispiel - ohne ernst zu nehmende Konstruktive Kritik - runter gemacht wird.

                                Dein Algorithmus hatte verschiedene ernstzunehmende technische Schwächen. Diese wurden ganz sachlich aufgezeigt. Nicht mehr, nicht weniger.

                                Niemand hier hat hinreichend Ahnung von Kryptographie, um einen eigenen Hashalgorithmus zu implementieren, der in punkto Sicherheit an bewährte Verfahren heranreicht. Nicht du, nicht ich. Daher meine Aussage: Wenn man keine Ahnung von Krypto hat, sollte man die Finger davon lassen.

                                Das ist technische Professionalität. Wenn ein Kunde anruft und nach Krypto fragt, verweise ich ihn weiter an jemanden, der sich damit auskennt – und seien es meine Kollegen, die IT-Sicherheit studiert haben. Natürlich könnte ich etwas basteln mit meinem zusammengegoogelten StackOverflow- und Heise-Halbwissen, aber das wäre grob fahrlässig.

                                Wie gesagt, einfach mal nur Funktionsnamen und bestenfalls den Link zum englischem(!) Handbuch  in die Runde zu werfen und - ohne jegliche wichtige Ergänzungen - auszusagen, diese zu verwenden sei sicher - mein Skript aber von jemanden, der wenig Ahnung habe das finde ich nicht nur "Scheiße", das ist es auch.

                                Hör endlich auf mit diesen haltlosen Unterstellungen. Ich habe schon in meinem ersten Posting auf password_hash() hingewiesen. Das ist eine einfach verwendbare Funktion, die bcrypt verwendet und automatisch einen sicheren Salt erzeugt. Diese zu verwenden ist sicher, den mir bekannten Fakten nach zu urteilen. Für ältere PHPs gibt es einen »Polyfill«, wie wir Frontend-Entwickler sagen würden. Gute Alternativen sind wie gesagt scrypt und PBKDF2.

                                Dass es die betreffende Seite des Handbuchs noch nicht in deutscher Übersetzung gibt, kannst du mir nicht vorwerfen.

                                Was du mit deinem verlinkten Posting sagen möchtest, weiß ich nicht. Du bist der einzige, der hash() ins Spiel gebracht hat; ich habe nie davon geredet.

                                string password_hash ( string $password , integer $algo [, array $options ] )

                                Fein, den Algorithmus darf man wieder selbst angeben

                                Der Standardalgorithmus ist bereits eine gute Wahl.

                                als options['salt'] darf man dann den hash wieder selbst übermitteln

                                Man muss es nicht. Man sollte es auch nicht. Steht ganz groß im Handbuch.

                                das läuft also auf das selbe hinaus wie hash_pbkdf2()

                                Nein, das ist Quatsch. Bei password_hash() muss man den Salt nicht selbst berechnen.

                                und ist eine Blackbox, weil wieder keiner weiß, was das Ding treibt.

                                Zum letzten Mal, bcrypt/Blowfish ist keine Blackbox, sondern ein öffentlich spezifizierter und hundertfach implementierter Algorithmus. Blowfish ist seit 1994 bekannt.

                                PHP ist Open Source, du kannst den C-Quellcode von password_hash lesen. Die Funktion verwendet intern crypt mit Blowfish. Die PHP-Bibliothek password_compat macht dasselbe.

                                Die crypt-Implementierung ruft php_crypt_blowfish_rn auf, das ist die C-Implementierung von Blowfish.

                                Was $options['cost'] bewirkt erschliest sich aus der Dokumenattion auch nach dem Folgen von einem halben Dutzend Links nicht.

                                Es ist ein Parameter für bcrypt. 2^cost ist die Anzahl der Wiederholungen. Diese Seite habe ich bereits verlinkt.

                                An diesem Parameter braucht man nichts verändern. Sollte man nichts verändern, wenn man nicht weiß, was man tut.

                                Und ergo ist password_hash() insoweit dann auch unbrauchbar, weil das Ergebnis deshalb nicht ohne weiteres in anderen Sprachen zu erzeugen oder zu überprüfen ist.

                                Das ist nachweislich falsch. Es wird nicht richtiger, wenn du es immer wieder behauptest.

                                bcrypt und Blowfish sind nicht PHP-spezifisch, sondern in vielen Sprachen implementiert. Viele Interpreter von dynamischen Sprachen sind in C implementiert, sie können also auf vorhandenen C-Implementierungen aufbauen.

                                So lange mir keiner erklärt wo der PHP-Kern "/dev/random" unter Windows findet habe ich viel Mühe dieser Funktionen als "plattformübergreifend" anerkennen.

                                Falls du es nicht glaubst, schau im PHP-Quellcode nach:

                                https://github.com/php/php-src/blob/php-5.5.4/ext/standard/password.c#L127-L153
                                https://github.com/php/php-src/blob/php-5.5.4/win32/winutil.c#L80-L126

                                Und wenn password_hash() vor 5.3.7 Probleme hatte, woher soll ich dann wissen, ob openssl_random_pseudo_bytes() nicht auch Probleme macht? Zumal die "Bytes" ja auch pseudo-zufällig sind. (Handbuch: "Generates a string of pseudo-random bytes")

                                Die meisten softwarebasierten Zufallsgeneratoren – so ziemlich alle, über die wir hier gesprochen haben – erzeugen nur Pseudo-Zufallszahlen. openssl_random_pseudo_bytes() ist die einzige Funktion, die dies in ihrem Namen zugibt. Quanten-Zufallsgeneratoren sind vielleicht weniger »pseudo«.

                                Wovon die Pseudo-Zufälligkeit hier abhängt weiß ich nicht.

                                Dann informiere dich halt. Über /dev/random gibt es einen Haufen an Literatur, und der Quelltext von Linux ist bekanntlich ebenfalls Open Source.

                                Bei rand() weiß ich es und ich finde es besser, das zu wissen

                                Das ändert nichts daran, dass die meisten Entropiequellen angreifbar sind.

                                Mathias

                                1. Über /dev/random gibt es einen Haufen an Literatur, und der Quelltext von Linux ist bekanntlich ebenfalls Open Source.

                                  drivers/char/random.c ist letztlich verantwortlich, sehe ich gerade. Ist sehr gut kommentiert. Die Aufrufe von add_device_randomness, add_input_randomness usw. befinden sich in verschiedenen Hardware-Treibern.

                                  1. Über /dev/random gibt es einen Haufen an Literatur, und der Quelltext von Linux ist bekanntlich ebenfalls Open Source.

                                    Melde Dich, wenn Du Neuigkeiten hast.

                                    Jörg Reinholz

                                  2. Ich wiederhole es ungern. Aber da Du wenig konkretes beigetragen hast und weil ich deshalb vermute, dass Du die Grundlagen noch nicht erfasst, weil Du die von mir gezeigten Quellen nicht wirklich gelesen hast, zeige ich Dir was es mit einem deutlich konkreteren Vorwurf der angeblichen Unsicherheit auf sich hat.

                                    Nämlich nichts.

                                    Jörg Reinholz

                                2. Dein Algorithmus hatte verschiedene ernstzunehmende technische Schwächen. Diese wurden ganz sachlich aufgezeigt. Nicht mehr, nicht weniger.

                                  Noch mal: Die Art wie ich vorliegend den Salt bilde führt im konkreten Anwendungsfall NICHT zu einer "ernstzunehmende technische Schwäche".

                                  Niemand hier hat hinreichend Ahnung von Kryptographie, um einen eigenen Hashalgorithmus zu implementieren, der in punkto Sicherheit an bewährte Verfahren heranreicht.

                                  Ich bin die ganze Zeit höflich geblieben, jetzt reicht es mir aber! Ich habe keinen Hashalgorithmus erfunden, auch nicht programmiert, sondern allenfalls benutzt und gezeigt wie das sinnvoll geht.

                                  Wenn Du wissen willt, was Hashalgorithmen sind, dann sieh Dir die Rückgaben von hash_algos(); an und suche dann bei Wikipedia nach den gefundenen Begriffen.

                                  Und so lange Du DAS nicht unterscheiden kannst bist DU auch definitiv nicht derjenige, der MIR ein Bildungsangebot unterbreitet.

                                  Jedenfalls nicht auf dem Gebiet.

                                  Jörg Reinholz

              2. Hallo,

                Man kann auch sagen, wer bei Überqueren der Straße nicht aufpasst sei selber schuld.

                ja, und bis zu einem gewissen Grad unterstütze ich diese Sichtweise. Ebenso finde ich es hanebüchen, wenn jemand bei Glatteis auf dem (öffentlichen!) Gehweg ausrutscht, sich einen Haxen bricht und dann der Besitzer des angrenzenden Grundstücks verantwortlich sein soll. Sorry, aber a) wäre das Räumen und Streuen öffentlicher Wege IMO Aufgabe der Gemeinde, und b) sind geräumte Wege meiner Ansicht nach zwar eine nette Geste[*], aber wer im Winter unterwegs ist, soll bitteschön aufpassen wo er/sie hintritt.
                Ich käme auch nicht auf die Idee, den Ladeninhaber zur Verantwortung zu ziehen, wenn ich auf den nassen Fliesen in der Kundentoilette ausrutsche, nur weil ich nicht aufgepasst habe oder zu blöd zum Laufen bin.

                Deiner Theorie folgend müsste man Geschwindigkeitsbeschränkungen vollständig aufheben. Auch die zwei Tasten an der Presse - weg damit! Es weiß doch ein jeder, dass man den Pressvorgang nicht auslöst wenn man eine Hand drunter hat.

                Bin ich prinzipiell sofort dabei - wäre da nicht ein kleiner, aber wesentlicher Unterschied zu den beiden Beispielen, die ich dagegengehalten habe: Bei deinen Szenarien hängt die Sicherheit oder Unfallfreiheit nicht nur von einem selbst ab, sondern zusätzlich von unbekannten Dritten. Abgesehen davon: Wenn mich beim Überqueren der Straße jemand mit dem Auto anfährt, würde ich -nachdem ich den Krankenhausaufenthalt hinter mir habe- auch nicht die Gemeinde auf Schadenersatz oder Schmerzensgeld verklagen, weil sie an der Stelle keine Fußgängerampel aufgestellt hat, sonden den Autofahrer, der mich erwischt hat.

                Tatsächlich ist jeder, der eine Anlage erreichtet und/oder betreibt, dazu verpflichtet diese so gefahrlos wie nur möglich zu erreichten oder zu betreiben - weil eben bekannt ist, dass die Menschen oft fahrlässig handeln.

                Das sollte deren eigenes Problem sein. Sie sollten nur vor der Fahrlässigkeit anderer geschützt werden.

                So long,
                 Martin

                [*] Hinzu kommt, dass schlecht geräumte Wege, auf denen ein dünner Schnee- oder Eisfilm übrigbleibt, gefährlicher sind als festgetretener Schnee oder buckliges Eis.

                --
                Ich bin im Prüfungsstress, ich darf Scheiße sagen.
                  (Hopsel)
                Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
              3. Moin!

                Ich ersuche Dich also, Deinen Algorithmus zu verbessern.

                Das ist ohne weitere Bedingung unterstützenswert.

                Deine Umsetzung allerdings nicht. SHA1 und MD5 sind keine für Passwort-Hashing geeigneten Algorithmen. Auch dann nicht, wenn man sie eine Million mal anwendet. Und erst recht nicht, wenn man "nur" 5000 Runden konfiguriert.

                Der aktuelle Stand der Forschung ist derzeit schon etwas weiter, als dieser über zwei Jahre alte Artikel. Die Grundtendenz ist richtig: Wenn man kein unauslesbares Sicherheits-Hardware-Modul hat (mit dessen Zerstörung dann auch alle Hashes nicht mehr wiederherstellbar sind), hilft nur möglichst aufwendige CPU- und RAM-Power.

                Aktuell wird "scrypt" als geeignet bewertet (es ist aber noch in der Erprobungsphase, und in PHP nicht implementiert), "bcrypt" kommt auf Platz 2 (und ist freundlicherweise in PHP verfügbar).

                Die Implementierung ist auch recht simpel: Ab PHP 5.5 gibts dafür Funktionen, die man im einfachsten Fall so simpel wie damals "md5()" benutzen kann - und wer ältere Versionen von PHP einsetzt: Ab PHP 5.3.7 ist diese Kompatibilitäts-Bibliothek einsetzbar (frühere PHP-Versionen haben vermutlich einen Security-Bug in der bcrypt-Implementierung, der den sicheren Einsatz verhindert).

                - Sven Rautenberg

                1. Om nah hoo pez nyeetz, Sven Rautenberg!

                  Der aktuelle Stand der Forschung ist derzeit schon etwas weiter, […]

                  Aktuell wird "scrypt" als geeignet bewertet (es ist aber noch in der Erprobungsphase, und in PHP nicht implementiert), "bcrypt" kommt auf Platz 2 (und ist freundlicherweise in PHP verfügbar).

                  Die Implementierung ist auch recht simpel: […]

                  Könntest du dir vorstellen, zu diesem Thema einen Blog-Beitrag zu verfassen?

                  Matthias

                  --
                  Der Unterschied zwischen Java und JavaScript ist größer als der zwischen Sonde und Sonderzeichen.

    2. Hallo,

      mal unabhängig von dem MD5/SHA1-Thema. Generell muss ich doch bei dem Verfahren serverseitig einen Schlüssel ausliefern, mit dem clientseitig dass Passwort (per JavaScript) verschlüsselt werden kann. Sehe ich das richtig? Und der Schlüssel muss dan serverseitig für die Identifizierung vorliegen und kann nciht mehr geändert werden. Auch richtig?

      So, jetzt die eigentliche Frage: Im JS ist das Verschlüsselungsverfahren ja ermittelbar und der Schlüssel wird übertragen und kann dann ja auch abgefangen werden (men in the middle heißt das, oder?). So: Verfahren bekannt, Schlüssel abgefangen, wo liegt also die Sicherheit?

      Viele Grüße
      Siri

      1. Moin,

        So: Verfahren bekannt, Schlüssel abgefangen, wo liegt also die Sicherheit?

        Das sind doch nur öffentliche Schlüssel, wie da abgefangen werden können... Oder meinst du etwas anderes?
        Prinzipiell geht es so:
        Der Server teilt seinen öffentlichen Schlüssel auf Anfrage mit. Der/die Clients verschlüsseln ihre Daten damit und schicken sie zum Server zurück. Der Server kann die verschlüsselten Daten mit seinem privaten Schlüssel entschlüsseln.
        Man muss also wissen, dass bei dieser Art, der sogenannten asymmetrischen Verschlüsselung ein Schlüssel_paar_ zum Einsatz kommt.

        Asymmetrisches Kryptosystem

        Grüße Marco

        --
        Ich spreche Spaghetticode - fließend.
        1. Hallo,

          Der Server teilt seinen öffentlichen Schlüssel auf Anfrage mit. Der/die Clients verschlüsseln ihre Daten damit und schicken sie zum Server zurück. Der Server kann die verschlüsselten Daten mit seinem privaten Schlüssel entschlüsseln.
          Man muss also wissen, dass bei dieser Art, der sogenannten asymmetrischen Verschlüsselung ein Schlüssel_paar_ zum Einsatz kommt.

          Das hatte ich bisher noch nicht verstanden, dass der öffentliche Schlüssel nicht zum entschlüsselnt taugt! Danke!

          Viele Grüße
          Siri

      2. Hallo,

        mal unabhängig von dem MD5/SHA1-Thema. Generell muss ich doch bei dem Verfahren serverseitig einen Schlüssel ausliefern, mit dem clientseitig dass Passwort (per JavaScript) verschlüsselt werden kann. Sehe ich das richtig?

        Es geht nicht um eine Verschlüsselung, sondern um das Bilden eines Hashes. Eine Art "Einwegverschlüsselung".

        So, jetzt die eigentliche Frage: Im JS ist das Verschlüsselungsverfahren ja ermittelbar und der Schlüssel wird übertragen und kann dann ja auch abgefangen werden (men in the middle heißt das, oder?).

        Nein, es geht hier nur um die Speicherung der Passwörter - die eben nicht selbst gesopeichert werden sollen, sondern deren Hash gespeichert werden soll. Damit ein Angreifer eben nicht eine nette Liste mit Nutzernamen, Mailadressen und Passwörtern herunterladen kann. Im Falle einer einfachen Methode wie md5 oder sha1 gibt es aber inzwischen Angriffe die - um es ganz(sic!) grob(sic! sic! sic!) zu sagen - darauf beruhen, dass md5('hallo') immer gleich ist, also riesige Datensammlungen, welche es ermöglichen aus dem geklautem hash das oder ein anderes funktionierendes Passwort [1] zu ermitteln.

        Aus dem Grund werden die Passwörter vor dem hashen gesalzen ("salted"). Das bedeutet, jedes Passwort bekommt einen String hinzugefügt und dann wird "gehashed". Damit steigt der Aufwand, weil selbst wenn der Salt (String) bekannt ist müssen die Rainbow-Tables [1] neu gebaut werden. Der Salt muss aber bekannt sein, also verwendet man für jedes Passwort einen eigenen zufälligen Salt und speichert den Salt und das Passwort in geeigneter Weise zusammen ab. Dann müssen für jedes zu knackende Passwort die Rainbow-Tabellen angeglichen werden - was den Rechenaufwand eines Angreifers zur Ermittlung der Passwörter wieder hochtreibt. Zusätzlich kann man das hashen mehrfach durchführen.

        Die von  molily unter dem gegen mich gerichteten - und nicht gerechtfertigtem - Vorwurf der "Ahnungslosigkeit" genannten Funktionen phpass, password_hash und hash_pbkdf2 tun letztendlich genau das (oder erlauben sogar die Übergabe eines statischen salt - was nicht zur Erhöhung der Sicherheit beiträgt) und verwenden dabei auch (nicht aber zwangsläufig, wie z.B. ein Blick in die Dokumentation auf die Ausgaben von hash_algos() ergibt) auch andere, stärkere hash-algorithmen als md5/sha1.

        Für die Übertragung gibt es ssl/https. Das ist aber noch mal ein anderes Thema. Merke Dir aber, dass die verschlüsselte Übertragung ggf. nutzlos wäre, wenn die Daten dann im Klartext gespeichert werden. Ebenso macht die Speicherung nur eingeschränkt Sinn, wenn die Daten dann im Klartext von der NSA oder anderen Angreifern abgegriffen werden.

        So: Verfahren bekannt, Schlüssel abgefangen, wo liegt also die Sicherheit?

        Freilich kannst Du versuchen, z.B. mit JS (auf dem Client) und PHP (Server) sowas wie die übliche asymmetrische ssl-Verschlüsselung zu implementieren, bei der nicht öffentliche Schlüssel ausgetauscht werden. Grob gesagt geht das so: der Sender VERschlüsselt mit einem Schlüssel, der aus seinem geheimen und dem öffentlichem Schlüssel des Empfängers gebaut wird - der Empfänger ENTschlüsselt mit einem Schlüssel, der seinem geheimen Schlüssel und dem öffentlichen Schlüssel des Senders gebaut wird. Deshalb asymmetrisch. Um die Sicherheit zu verbessern gäbe dann noch diffi-hellmann zum Wechsel der Schlüssel e.t.c. - das wieder, weil z.B. wenn genug Klartext bekannt ist auf die verwendeten Schlüssel geschlossen werden kann. "diffi-hellmann" sorgt dafür, dass die Schlüssel sich verändern.

        [1] gigantische Rainbow-Tables helfen inzwischen dabei, extrem viel Rechenzeit zu sparen.

        1. Danke für die ausführlichen Informationen! Alles ein bischen klarer jetzt insbesondere die Salt-Methode!

      3. Hallo,

        wo wird eigentlich der Schlüssel der verschlüsselt gespeicherten Passwörter am besten hinterlegt? Da muss es ja auch eine Zuordnung von User zu seinem Schlüssel geben (so ne Art Mapping).

        Danke und viele Grüße
        Siri

  3. Moin!

    Bist Du Dir sicher, dass das funktioniert:

    url = "<?=$pfadhtml;?>/templates/do_login.php";

    Ansonsten wäre es eine gute Idee, wenn die do_login.php die übergebenen Daten mit

      
    <?php error_reporting(E_ALL); ?>  
    <html>  
    <strong>Die POST-Daten:</strong>  
    <hr><pre>  
    <?php var_dump($_POST); ?>  
    </pre>  
    <hr>  
    </html>  
    
    

    oder dergleichen einfach mal nur zurück "schmeißt". Achte darauf, dass Du (wie wie ich im Beispiel zeige) auch statischen Kram sendest, damit Du ggf. überhaupt eine Ausgabe hast.

    Jörg Reinholz

    1. Bist Du Dir sicher, dass das funktioniert:

      url = "<?=$pfadhtml;?>/templates/do\_login.php";  
      

      Da bin ich mir ziemlich sicher, da es mit dem Firefox ja funktioniert.
      Ansonsten wird mir Safari & Co. warscheinlich ebensowenig ausgeben, da do_login.php in jedem Fall irgendwas zurück gibt und ich dieses "irgendwas" immer zur Anzeige bringe.
      Aber Safari etc. eben gar nichts anzeigt.
      Ich habe die Url aber auch mal fest vorgegeben und es hatte den selben Effekt.

  4. Hallo,

      if (resulttext.search("Success") >= 0) {  
    

    Hier kannst du besser .indexOf("Success") > -1 verwenden. search() ist für reguläre Ausdrücke gedacht.

    req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    req.setRequestHeader("Content-length", formdaten.length);
    req.setRequestHeader("Connection", "close");

    Diese Zeilen dürften eigentlich unnötig sein. Bei POST-Requests ist application/x-www-form-urlencoded Standard, und der Browser setzt auch die korrekte Content-Length. Die Angabe von Connection: Close ist nicht zwingend nötig, lass den Browser entscheiden, wann er die TCP-Verbindung schließt.

    [code lang=html]
    $verbindung = @mysql_connect($server,$login,$pass);
    $test_pw = md5($password);
    $abfrage = "select anzeigename,loginname from userlogins where loginname='$loginname' AND password='$test_pw'";

    Dieses Script hat verschiedene Schwächen und Sicherheitslücken. Problematisch sind hier:

    • Verwendung von register_globals anstatt $_GET, $_POST usw.
    • Verwendung der veralteten mysql_*-Funktionen anstatt mysqli oder PDO mit Prepared Statements
    • Eingabedaten werden ohne Behandlung (Escaping) in SQL-Anfragen eingebaut. Das ist ein Sicherheitsrisiko, siehe auch Kontextwechsel
    • Die Passwörter werden ohne Salt mit MD5 gehasht. Es ist ein Kinderspiel, das zu knacken. Besser wäre bcrypt oder PBKDF2.
    • Als Authentifizierung im Cookie dient anscheinend der Benutzername und ein davon direkt abgeleiteter Wert. Das ist leicht zu fälschen, wenn der Benutzername bekannt ist. Trenne die Session-Daten von der zufälligen Session-ID. Ändere die Laufzeit der Session entsprechend, falls nötig.

    Ich rate dir, ein gutes PHP-/MySQL-Buch durchzuarbeiten. Das bringt einem bei, wie man heutzutage Datenbank-Verbindungen aufbaut, Inhalte bereinigt, Passwörter speichert und Sessions erzeugt.

    Viele Grüße,
    Mathias

    1. Hallo!

      Ändere die Laufzeit der Session entsprechend, falls nötig.

      Der Vollständigkeit halber...
      session_cache_expire() ist in dem Zusammenhang auch interessant. Es hat allerdings nichts mit der Laufzeit der Session zu tun, sondern mit der Verfallszeit des HTTP Caches.

      Grüße, Matze

    2.   if (resulttext.search("Success") >= 0) {  
      

      Hier kannst du besser .indexOf("Success") > -1 verwenden. search() ist für reguläre Ausdrücke gedacht.

      Danke für den Hinweis, ist geändert.

      req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");  
      req.setRequestHeader("Content-length", formdaten.length);  
      req.setRequestHeader("Connection", "close");  
      

      Diese Zeilen dürften eigentlich unnötig sein. Bei POST-Requests ist application/x-www-form-urlencoded Standard, und der Browser setzt auch die korrekte Content-Length. Die Angabe von Connection: Close ist nicht zwingend nötig, lass den Browser entscheiden, wann er die TCP-Verbindung schließt.

      Die Zeilen habe ich auch auskommentiert.

      [code lang=html]
      $verbindung = @mysql_connect($server,$login,$pass);
      $test_pw = md5($password);
      $abfrage = "select anzeigename,loginname from userlogins where loginname='$loginname' AND password='$test_pw'";

      Dieses Script hat verschiedene Schwächen und Sicherheitslücken.

      Die übergebenen Daten werden mittels require('/grab_globals.lib.php');
      geprüft. Hat bisher allen von mir getesteten typischen SQL-Injektionen widerstanden.
      Die sichere Speicherung und Prüfung des Passworts hat im Moment noch keine Priorität.
      Das Session-Handling umzustellen dürfte auch kein Akt sein, dient derzeit nicht um sichere Daten zu schützen, sondern lediglich zur Authentifizierung, wer gerade die Seite aufruft. Zudem werden bei jeder Speicherung im Anschluss weitere Daten z.B. IP-Adresse gespeichert.

      Die Sicherheit ist aber im Moment nicht das Thema, sondern vielmehr warum die Anmeldung bzw. der Ajax-Aufruf bei allen anderen Browsern nicht funktioniert.