Jörg Reinholz: Kleine Bibliothek für Passwort-Hash-Erzeugug und Verifizierung

Beitrag lesen

In der Hoffnung, dass es gefällt stelle ich das hier mal online. Könnte für jene nützlich sein, die sich mit alten PHP-Versionen herumschlagen müssen oder nicht wissen, für welche sie programmieren.

Zum Testen in Zeile 2 den Funktionsaufruf test_me(); "scharfschalten" und die Funktion test_hash_passwd() durch die Kommentare modifizieren.

<?php  
#test_me();  
error_reporting(0);  
## Erzeugt gesalzte Passwörter nach "bester" Möglichkeit. Reihenfolge mit  
## 	* password_hash  
## 	* CRYPT_SHA512  
## 	* CRYPT_SHA256  
## 	* CRYPT_BLOWFISH  
## 	* Apache MD5 - $apr1$ (als Fallback)  
## und kann diese verifizieren  
## verifiziert zusätzlich auch Passwörter, deren Hash früher mal mit  
## 	* htpasswd -nbs myName myPassword (SHA1, {sha})  
##      * gesalzenem md5 ( siehe test_hash_passwd() )  
##      * gesalzenem sha1 ( siehe test_hash_passwd() )  
## erzeugt wurde...  
  
function create_salt($l=16, $allowed='1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJLKMNOPQRSTUVWXYZ./') {  
	$salt='';  
	for ( $i = 0; $i < $l; $i++ ) {  
		$salt .= $allowed{rand( 0, strlen($allowed) - 1 )};  
	}  
return $salt;  
}  
  
  
function fastix_hash_passwd ($string) {  
	if ( function_exists ('password_hash') ) {  
		return password_hash($string, PASSWORD_DEFAULT);  
	} else {  
		if ( function_exists ('crypt') and defined('CRYPT_SHA512') and  CRYPT_SHA512 ) {  
			return crypt( $string, '$6$rounds=5000$' .  create_salt() );  
		} else if ( function_exists ('crypt') and defined('CRYPT_SHA256') and  CRYPT_SHA256 ) {  
			return crypt( $string, '$5$rounds=5000$' .  create_salt() );	  
		} else if ( function_exists ('crypt') and defined('CRYPT_BLOWFISH') and CRYPT_BLOWFISH ) {  
			if ( version_compare(phpversion(), '5.3.7', '>') ) {  
			      return crypt( $string, '$2y$12$' . create_salt(22) );	  
			} else {  
			      return crypt( $string, '$2a$12$' . create_salt(22) );  
			}  
		} else {  
			# Fallback: Apache md5 - $apr1$  
			$salt = create_salt(8, 'abcdefghijklmnopqrstuvwxyz0123456789');  
			# Code from http://php.net/manual/de/function.crypt.php, comment No. 8:  
			$len = strlen($string);  
			$tmp='';  
			$text = $string.'$apr1$'.$salt;  
			$bin = pack("H32", md5($string.$salt.$string));  
			for($i = $len; $i > 0; $i -= 16) { $text .= substr($bin, 0, min(16, $i)); }  
			for($i = $len; $i > 0; $i >>= 1) { $text .= ($i & 1) ? chr(0) : $string{0}; }  
			$bin = pack("H32", md5($text));  
			for($i = 0; $i < 1000; $i++) {  
			    $new = ($i & 1) ? $string : $bin;  
			    if ($i % 3) $new .= $salt;  
			    if ($i % 7) $new .= $string;  
			    $new .= ($i & 1) ? $bin : $string;  
			    $bin = pack("H32", md5($new));  
			}  
			for ($i = 0; $i < 5; $i++) {  
			    $k = $i + 6;  
			    $j = $i + 12;  
			    if ($j == 16) $j = 5;  
			    $tmp = $bin[$i].$bin[$k].$bin[$j].$tmp;  
			}  
			$tmp = chr(0).chr(0).$bin[11].$tmp;  
			$tmp = strtr(strrev(substr(base64_encode($tmp), 2)), "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");  
			return "$"."apr1"."$".$salt."$".$tmp;  
		}  
	}  
	header('HTTP/1.1 500 Script Generated Error (NO HASH-METHOD Found)');  
	exit;  
}  
  
function fastix_check_passwd ($string, $string_hashed, $error_exit=false) {  
  
	# htpasswd -nbm myName myPassword  
	if ( '$apr1$' == substr($string_hashed, 0, 6) ) {  
		list ($dummy, $method, $salt, $rest)=explode('$', $string_hashed, 4);  
		# Code From http://php.net/manual/de/function.crypt.php, comment No. 8:  
	        $len = strlen($string);  
	        $tmp='';  
		$text = $string.'$apr1$'.$salt;  
		$bin = pack("H32", md5($string.$salt.$string));  
		for($i = $len; $i > 0; $i -= 16) { $text .= substr($bin, 0, min(16, $i)); }  
		for($i = $len; $i > 0; $i >>= 1) { $text .= ($i & 1) ? chr(0) : $string{0}; }  
		$bin = pack("H32", md5($text));  
		for($i = 0; $i < 1000; $i++) {  
		    $new = ($i & 1) ? $string : $bin;  
		    if ($i % 3) $new .= $salt;  
		    if ($i % 7) $new .= $string;  
		    $new .= ($i & 1) ? $bin : $string;  
		    $bin = pack("H32", md5($new));  
		}  
		for ($i = 0; $i < 5; $i++) {  
		    $k = $i + 6;  
		    $j = $i + 12;  
		    if ($j == 16) $j = 5;  
		    $tmp = $bin[$i].$bin[$k].$bin[$j].$tmp;  
		}  
		$tmp = chr(0).chr(0).$bin[11].$tmp;  
		$tmp = strtr(strrev(substr(base64_encode($tmp), 2)), "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");  
		#$hashed = "$"."apr1"."$".$salt."$".$tmp;  
		if ( $rest == $tmp) { return 'bestaetigt:apache2-apr1'; }  
	}  
	  
	# htpasswd -nbs myName myPassword  
	if ( '{SHA}' == substr($string_hashed, 0, 5) ) {  
		$hash='{SHA}' . base64_encode(sha1($string, TRUE));  
		if ( $hash == $string_hashed) { return 'bestaetigt:apache2-sha1'; }  
	}  
  
	if ( function_exists ('password_verify') ) {  
		if ( password_verify($string, $string_hashed) ) { return 'bestaetigt:password_verify()'; }  
	}  
	if ( function_exists ('crypt') ) {  
		list( $dummy, $method, $rest ) = explode( '$', $string_hashed, 3 );	  
		if ( '2y' == $method or '2a' == $method ) {  
		      list ($dummy, $method, $rounds, $rest)=explode('$', $string_hashed, 4);  
		      $salt = substr($rest, 0, 22);  
		      if ( $string_hashed == crypt($string, '$' . $method . '$' . $rounds . '$' . $salt) ) { return 'bestaetigt:crypt():'.$method; }  
		}  
		if ( '6' == $method  or '5' == $method or '1' == $method ) {  
		      list ($dummy, $method, $rounds, $salt, $rest) = explode('$', $string_hashed, 5);  
		      if ( $string_hashed == crypt($string, '$' . $method . '$' . $rounds . '$' . $salt) ) { return 'bestaetigt:crypt()'.$method; }  
		}		  
	}  
	if ( '1' == $method ) {  
		trigger_error("Warnung: Das Passwort ist nicht ausreichend sicher gehasht.", E_USER_NOTICE);	  
		list( $dummy, $method, $rounds, $salt, $rest )=explode( '$', $string_hashed, 5 );  
		$rounds = str_replace('rounds=', '', $rounds);  
		for ( $i = 0; $i < $rounds; $i++ ) {  
			$string = md5($salt.$string);  
		}  
		$string = '$1$rounds=' . $rounds . '$' . $salt . '$' . $string;  
		if ( $string_hashed == $string )  { return 'bestaetigt:md5()'; }  
	}  
	if ( 'sha1' == $method ) {  
		trigger_error("Warnung: Das Passwort ist nicht ausreichend sicher gehasht.", E_USER_NOTICE);	  
		list ($dummy, $method, $rounds, $salt, $rest)=explode('$', $string_hashed, 5);  
		$rounds = str_replace( 'rounds=', '', $rounds );  
		for ( $i = 0; $i < $rounds; $i++ ) {  
			$string = sha1($salt.$string);  
		}  
		$string = '$sha1$rounds=' . $rounds . '$' . $salt . '$' . $string;  
		if ( $string_hashed == $string )  { return 'bestaetigt:sha1()'; }  
	}  
	if ( $error_exit ) { trigger_error("Unbekannte Methode oder falsches Passwort.", $error_exit); }  
	return false;  
}  
  
### Tests ####  
  
function test_hash_passwd ($string) {  
#	return password_hash($string, PASSWORD_DEFAULT);  
#	return crypt( $string, '$6$rounds=5000$' .  create_salt() );  
#	return crypt( $string, '$5$rounds=5000$' .  create_salt() );	  
	return crypt( $string, '$2y$12$' . create_salt(22) );	  
	return crypt( $string, '$2a$12$' . create_salt(22) );  
	$salt=create_salt(32); for ( $i=0; $i<50000; $i++ ) { $string=sha1($salt.$string); } return '$sha1$rounds=50000$' . $salt . '$' . $string;  
	$salt=create_salt(32); for ( $i=0; $i<50000; $i++ ) { $string=md5($salt.$string); } return '$1$rounds=50000$' . $salt . '$' . $string;  
}  
  
function test_me() {  
error_reporting(E_ALL);  
	$string='Dieses Passwort ist vollkommen richtig';  
	$string_hashed=test_hash_passwd ($string);  
	echo "'$string'\n", "'$string_hashed'\n", fastix_check_passwd($string,  $string_hashed, E_USER_ERROR), "\n";  
	$string='Dieses Passwort ist nur fast richtig:2'; echo "Gegentest: '$string'\n", fastix_check_passwd($string, $string_hashed, E_USER_NOTICE), "\n";  
	print fastix_check_passwd('myPassword', '{SHA}VBPuJHI7uixaa6LQGWx4s+5GKNE=')."\n";  
	print fastix_check_passwd('myPassword', '$apr1$r31.....$HqJZimcKQFAMYayBlzkrA/')."\n";  
}