Project

General

Profile

« Previous | Next » 

Revision 1900

Added by darkviper over 11 years ago

Classes Password / PasswordHash added for an essential strengthening of the password security

View differences:

branches/2.8.x/CHANGELOG
12 12
===============================================================================
13 13

  
14 14

  
15

  
15
18 Apr-2013 Build 1900 Werner v.d.Decken(DarkViper)
16
+ Classes Password / PasswordHash Added for an essential strengthening of the password security
16 17
04 Apr-2013 Build 1899 Dietmar Woellbrink (Luisehahne)
17 18
# bugfix missing td in form layout
18 19
03 Apr-2013 Build 1898 Dietmar Woellbrink (Luisehahne)
branches/2.8.x/wb/include/phpass/PasswordHash.php
1
<?php
2
/**
3
 * Portable PHP password hashing framework.
4
 *
5
 * Version 0.4 / WebsiteBaker Rev. 0.1
6
 * WB changes: added SHA-256, SHA-512 (2012/10/27 DarkViper)
7
 *
8
 * Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
9
 * the public domain.  Revised in subsequent years, still public domain.
10
 *
11
 * There's absolutely no warranty.
12
 *
13
 * The homepage URL for this framework is:
14
 *
15
 *	http://www.openwall.com/phpass/
16
 *
17
 * Please be sure to update the Version line if you edit this file in any way.
18
 * It is suggested that you leave the main version number intact, but indicate
19
 * your project name (after the slash) and add your own revision information.
20
 *
21
 * Please do not change the "private" password hashing method implemented in
22
 * here, thereby making your hashes incompatible.  However, if you must, please
23
 * change the hash type identifier (the "$P$") to something different.
24
 *
25
 * Obviously, since this code is in the public domain, the above are not
26
 * requirements (there can be none), but merely suggestions.
27
 */
28

  
29
class PasswordHash {
30
	private $itoa64;
31
	private $itoa64BlowFish;
32
	private $iteration_count_log2;
33
	private $portable_hashes;
34
	private $random_state;
35

  
36
	public function __construct($iteration_count_log2, $portable_hashes)
37
	{
38
		$this->itoa64         = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
39
		$this->itoa64BlowFish = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
40

  
41
		if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) {
42
			$iteration_count_log2 = 8;
43
		}
44
		$this->iteration_count_log2 = $iteration_count_log2;
45
		$this->portable_hashes = $portable_hashes;
46
		$this->random_state = microtime();
47
		if (function_exists('getmypid')) {
48
			$this->random_state .= getmypid();
49
		}
50
	}
51

  
52
	private function get_random_bytes($count)
53
	{
54
		$output = '';
55
		if (@is_readable('/dev/urandom') &&
56
		    ($fh = @fopen('/dev/urandom', 'rb'))) {
57
			$output = fread($fh, $count);
58
			fclose($fh);
59
		}
60
		if (strlen($output) < $count) {
61
			$output = '';
62
			for ($i = 0; $i < $count; $i += 16) {
63
				$this->random_state = md5(microtime() . $this->random_state);
64
				$output .= pack('H*', md5($this->random_state));
65
			}
66
			$output = substr($output, 0, $count);
67
		}
68
		return $output;
69
	}
70

  
71
	private function encode64($input, $count)
72
	{
73
		$output = '';
74
		$i = 0;
75
		do {
76
			$value = ord($input[$i++]);
77
			$output .= $this->itoa64[$value & 0x3f];
78
			if ($i < $count) { $value |= ord($input[$i]) << 8; }
79
			$output .= $this->itoa64[($value >> 6) & 0x3f];
80
			if ($i++ >= $count) { break; }
81
			if ($i < $count) { $value |= ord($input[$i]) << 16; }
82
			$output .= $this->itoa64[($value >> 12) & 0x3f];
83
			if ($i++ >= $count) { break; }
84
			$output .= $this->itoa64[($value >> 18) & 0x3f];
85
		} while ($i < $count);
86

  
87
		return $output;
88
	}
89

  
90
	private function gensalt_private($input)
91
	{
92
		$output = '$P$';
93
		$output .= $this->itoa64[min($this->iteration_count_log2 +
94
			((PHP_VERSION >= '5') ? 5 : 3), 30)];
95
		$output .= $this->encode64($input, 6);
96
		return $output;
97
	}
98

  
99
	private function crypt_private($password, $setting)
100
	{
101
		$output = '*0';
102
		if (substr($setting, 0, 2) == $output) { $output = '*1'; }
103
		$id = substr($setting, 0, 3);
104
		# We use "$P$", phpBB3 uses "$H$" for the same thing
105
		if ($id != '$P$' && $id != '$H$') { return $output; }
106
		$count_log2 = strpos($this->itoa64, $setting[3]);
107
		if ($count_log2 < 7 || $count_log2 > 30) { return $output; }
108
		$count = 1 << $count_log2;
109
		$salt = substr($setting, 4, 8);
110
		if (strlen($salt) != 8) { return $output; }
111

  
112
		# We're kind of forced to use MD5 here since it's the only
113
		# cryptographic primitive available in all versions of PHP
114
		# currently in use.  To implement our own low-level crypto
115
		# in PHP would result in much worse performance and
116
		# consequently in lower iteration counts and hashes that are
117
		# quicker to crack (by non-PHP code).
118
		if (PHP_VERSION >= '5') {
119
			$hash = md5($salt . $password, TRUE);
120
			do {
121
				$hash = md5($hash . $password, TRUE);
122
			} while (--$count);
123
		} else {
124
			$hash = pack('H*', md5($salt . $password));
125
			do {
126
				$hash = pack('H*', md5($hash . $password));
127
			} while (--$count);
128
		}
129
		$output = substr($setting, 0, 12);
130
		$output .= $this->encode64($hash, 16);
131
		return $output;
132
	}
133

  
134
	private function gensalt_extended($input)
135
	{
136
		$count_log2 = min($this->iteration_count_log2 + 8, 24);
137
		# This should be odd to not reveal weak DES keys, and the
138
		# maximum valid value is (2**24 - 1) which is odd anyway.
139
		$count = (1 << $count_log2) - 1;
140
		$output = '_';
141
		$output .= $this->itoa64[$count & 0x3f];
142
		$output .= $this->itoa64[($count >> 6) & 0x3f];
143
		$output .= $this->itoa64[($count >> 12) & 0x3f];
144
		$output .= $this->itoa64[($count >> 18) & 0x3f];
145
		$output .= $this->encode64($input, 3);
146
		return $output;
147
	}
148
/** Begin inserted function for WebsiteBaker by W.v.d.Decken **/	
149
/**
150
 * 
151
 * @param type $input
152
 * @param type $sType
153
 * @return type
154
 */
155
	private function gensalt_sha($input, $sType = 'SHA512')
156
	{
157
		$iType = ($sType === 'SHA512') ? 6 : (($sType === 'SHA256') ? 5 : 6);
158
		$iIterations = pow(2, $this->iteration_count_log2);
159
		$iIterations = min(max($iIterations, 10000), 999999999);
160
		$output = '$'.(string)$iType.'$rounds='.(string)$iIterations.'$';
161
		$output .= $this->encode64($input, 16);
162
		return $output;
163
	}
164
/** End inserted function for WebsiteBaker by W.v.d.Decken **/	
165

  
166
	private function gensalt_blowfish($input)
167
	{
168
		# This one needs to use a different order of characters and a
169
		# different encoding scheme from the one in encode64() above.
170
		# We care because the last character in our encoded string will
171
		# only represent 2 bits.  While two known implementations of
172
		# bcrypt will happily accept and correct a salt string which
173
		# has the 4 unused bits set to non-zero, we do not want to take
174
		# chances and we also do not want to waste an additional byte
175
		# of entropy.
176

  
177
		$output = '$2a$';
178
		$output .= chr(ord('0') + $this->iteration_count_log2 / 10);
179
		$output .= chr(ord('0') + $this->iteration_count_log2 % 10);
180
		$output .= '$';
181
		$i = 0;
182
		do {
183
			$c1 = ord($input[$i++]);
184
			$output .= $this->itoa64BlowFish[$c1 >> 2];
185
			$c1 = ($c1 & 0x03) << 4;
186
			if ($i >= 16) {
187
				$output .= $this->itoa64BlowFish[$c1];
188
				break;
189
			}
190
			$c2 = ord($input[$i++]);
191
			$c1 |= $c2 >> 4;
192
			$output .= $this->itoa64BlowFish[$c1];
193
			$c1 = ($c2 & 0x0f) << 2;
194
			$c2 = ord($input[$i++]);
195
			$c1 |= $c2 >> 6;
196
			$output .= $this->itoa64BlowFish[$c1];
197
			$output .= $this->itoa64BlowFish[$c2 & 0x3f];
198
		} while (1);
199
		return $output;
200
	}
201

  
202
	public function HashPassword($password)
203
	{
204
		$random = '';
205

  
206
/** Begin inserted function for WebsiteBaker by W.v.d.Decken **/	
207
		if (CRYPT_SHA512 == 1 && !$this->portable_hashes) {
208
			$random = $this->get_random_bytes(16);
209
			$hash = crypt($password, $this->gensalt_sha($random, 'SHA512'));
210
			if (strlen(substr(strrchr($hash, 36), 1)) == 86) { return $hash; }
211
		}
212

  
213
		if (CRYPT_SHA256 == 1 && !$this->portable_hashes) {
214
			$random = $this->get_random_bytes(16);
215
			$hash = crypt($password, $this->gensalt_sha($random, 'SHA256'));
216
			if (strlen(substr(strrchr($hash, 36), 1)) == 43) { return $hash; }
217
		}
218
/** End inserted function for WebsiteBaker by W.v.d.Decken **/	
219

  
220
		if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) {
221
			$random = $this->get_random_bytes(16);
222
			$hash = crypt($password, $this->gensalt_blowfish($random));
223
			if (strlen($hash) == 60) { return $hash; }
224
		}
225

  
226
		if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) {
227
			if (strlen($random) < 3) {
228
				$random = $this->get_random_bytes(3);
229
			}
230
			$hash = crypt($password, $this->gensalt_extended($random));
231
			if (strlen($hash) == 20) { return $hash; }
232
		}
233

  
234
		if (strlen($random) < 6) {
235
			$random = $this->get_random_bytes(6);
236
		}
237
		$hash = $this->crypt_private($password, $this->gensalt_private($random));
238
		if (strlen($hash) == 34) { return $hash; }
239

  
240
		# Returning '*' on error is safe here, but would _not_ be safe
241
		# in a crypt(3)-like function used _both_ for generating new
242
		# hashes and for validating passwords against existing hashes.
243
		return '*';
244
	}
245

  
246
	public function CheckPassword($password, $stored_hash)
247
	{
248
		$hash = $this->crypt_private($password, $stored_hash);
249
		if ($hash[0] == '*') {
250
			$hash = crypt($password, $stored_hash);
251
		}
252
		return $hash == $stored_hash;
253
	}
254
}
255

  
0 256

  
branches/2.8.x/wb/admin/interface/version.php
51 51

  
52 52
// check if defined to avoid errors during installation (redirect to admin panel fails if PHP error/warnings are enabled)
53 53
if(!defined('VERSION')) define('VERSION', '2.8.3');
54
if(!defined('REVISION')) define('REVISION', '1899');
54
if(!defined('REVISION')) define('REVISION', '1900');
55 55
if(!defined('SP')) define('SP', '');
branches/2.8.x/wb/framework/initialize.php
208 208
	if(!function_exists('globalExceptionHandler')) {
209 209
		include(dirname(__FILE__).'/globalExceptionHandler.php');
210 210
	}
211
// activate secure PasswordHashes
212
	if(!class_exists('PasswordHash')) {
213
		include(dirname(dirname(__FILE__)).'/include/phpass/PasswordHash.php'); 
214
	}
211 215
// ---------------------------
212 216
// Create global database instance ---
213 217
	$database = WbDatabase::getInstance();
branches/2.8.x/wb/framework/Password.php
1
<?php
2
/**
3
 *  Copyright (C) 2012 Werner v.d. Decken <wkl@isteam.de>
4
 *
5
 *  This program is free software: you can redistribute it and/or modify
6
 *  it under the terms of the GNU General Public License as published by
7
 *  the Free Software Foundation, either version 3 of the License, or
8
 *  (at your option) any later version.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU General Public License
16
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
 */
18
/**
19
 * Description of Password
20
 * *****************************************************************************
21
 * This class is interfacing the Portable PHP password hashing framework.
22
 * Version 0.4 / ISTeam Rev. 0.1
23
 * ISTeam changes: added SHA-256, SHA-512 (2012/10/27 Werner v.d. Decken)
24
 * *****************************************************************************
25
 *
26
 * @category     WBCore
27
 * @package      WBCore_Security
28
 * @author       Werner v.d. Decken <wkl@isteam.de>
29
 * @copyright    Werner v.d. Decken <wkl@isteam.de>
30
 * @license      http://www.gnu.org/licenses/gpl.html   GPL License
31
 * @version      1.0.3
32
 * @revision     $Revision$
33
 * @link         $HeadURL$
34
 * @lastmodified $Date$
35
 * @since        Datei vorhanden seit Release 1.2.0
36
 */
37

  
38
// use \vendors\phpass\PasswordHash;
39

  
40
class Password extends PasswordHash
41
//class Password extends v_phpass_PasswordHash
42
{
43

  
44
	const MIN_CRYPT_LOOPS     =  6;  // minimum numbers of loops is 2^6 (64) very, very quick
45
	const MAX_CRYPT_LOOPS     = 32;  // maximum numbers of loops is 2^32 (4,294,967,296) extremely slow
46
	const DEFAULT_CRYPT_LOOPS = 12;  // default numbers of loopf is 2^12 (4096) a good average
47

  
48
	const HASH_TYPE_PORTABLE  = true;  // use MD5 only
49
	const HASH_TYPE_AUTO      = false; // select highest available crypting methode
50

  
51
	const MIN_PW_LENGTH       =   6;
52
	const MAX_PW_LENGTH       = 100;
53
	const DEFAULT_PW_LENGTH   =  10;
54

  
55
	const PW_USE_LOWERCHAR    = 0x0001; // use lower chars
56
	const PW_USE_UPPERCHAR    = 0x0002; // use upper chars
57
	const PW_USE_DIGITS       = 0x0004; // use numeric digits
58
	const PW_USE_SPECIAL      = 0x0008; // use special chars
59
	const PW_USE_ALL          = 0xFFFF; // use all possibilities
60

  
61
/**
62
 * @param int number of iterations as exponent of 2 (must be between 4 and 31)
63
 * @param bool TRUE = use MD5 only | FALSE = automatic
64
 */
65
	public function __construct($iIterationCountLog2 = self::DEFAULT_CRYPT_LOOPS, $bPortableHashes = self::HASH_TYPE_AUTO)
66
	{
67
		parent::__construct($iIterationCountLog2, $bPortableHashes);
68
	}
69
/**
70
 * @param string password to hash
71
 * @return string generated hash. Null if failed.
72
 */
73
	public function HashPassword($sPassword)
74
	{
75
		$sNewHash = parent::HashPassword($sPassword);
76
		return ($sNewHash == '*') ? null : $sNewHash;
77
	}
78
/**
79
 * @param string Password to test against given Hash
80
 * @param string existing stored hash
81
 * @return bool true if PW matches the stored hash
82
 */
83
	public function CheckPassword($sPassword, $sStoredHash)
84
	{
85
		// compatibility layer for deprecated, simple and old MD5 hashes
86
		if(preg_match('/^[0-9a-f]{32}$/si', $sStoredHash)) {
87
			return (md5($sPassword) === $sStoredHash);
88
		}
89
		return parent::CheckPassword($sPassword, $sStoredHash);
90
	}
91
/**
92
 * generate a case sensitive mnemonic password including numbers and special chars
93
 * makes no use of confusing characters like 'O' and '0' and so on.
94
 * @param int length of the generated password. default = DEFAULT_PW_LENGTH
95
 * @param int defines which elemets are used to generate a password. Default = PW_USE_ALL
96
 * @return string
97
 */
98
	public static function createNew($iLength = self::DEFAULT_PW_LENGTH, $iElements = self::PW_USE_ALL)
99
	{
100
		$aChars = array(
101
			array('b','c','d','f','g','h','j','k','m','n','p','q','r','s','t','v','w','x','y','z'),
102
			array('B','C','D','F','G','H','J','K','M','N','P','Q','R','S','T','V','W','X','Y','Z'),
103
			array('a','e','i','o','u'),
104
			array('A','E','U'),
105
			array('!','-','@','_',':','.','+','%','/','*')
106
		);
107
		$iElements = ($iElements & self::PW_USE_ALL) == 0 ? self::PW_USE_ALL : $iElements;
108
		if(($iLength < self::MIN_PW_LENGTH) || ($iLength > self::MAX_PW_LENGTH)) {
109
			$iLength = self::DEFAULT_PW_LENGTH;
110
		}
111
	// at first create random arrays of lowerchars and uperchars
112
	// alternating between vowels and consonants
113
		$aUpperCase = array();
114
		$aLowerCase = array();
115
		for($x = 0; $x < ceil($iLength / 2); $x++) {
116
			// consonants
117
			$y = rand(1000, 10000) % sizeof($aChars[0]);
118
			$aLowerCase[] = $aChars[0][$y];
119
			$y = rand(1000, 10000) % sizeof($aChars[1]);
120
			$aUpperCase[] = $aChars[1][$y];
121
			// vowels
122
			$y = rand(1000, 10000) % sizeof($aChars[2]);
123
			$aLowerCase[] = $aChars[2][$y];
124
			$y = rand(1000, 10000) % sizeof($aChars[3]);
125
			$aUpperCase[] = $aChars[3][$y];
126
		}
127
	// create random arrays of numeric digits 2-9 and  special chars
128
		$aDigits       = array();
129
		$aSpecialChars = array();
130
		for($x = 0; $x < $iLength; $x++) {
131
			$aDigits[] = (rand(1000, 10000) % 8) + 2;
132
			$aSpecialChars[] = $aChars[4][rand(1000, 10000) % sizeof($aChars[4])];
133
		}
134
		// take string or merge chars depending from $iElements
135
		$aPassword = array();
136
		$bMerge = false;
137
		if($iElements & self::PW_USE_LOWERCHAR) {
138
			$aPassword = $aLowerCase;
139
			$bMerge = true;
140
		}
141
		if($iElements & self::PW_USE_UPPERCHAR) {
142
			if($bMerge) {
143
				$iNumberOfUpperChars = rand(1000, 10000) % ($iLength);
144
				$aPassword = self::_mergeIntoPassword($aPassword, $aUpperCase, $iNumberOfUpperChars);
145
			}else {
146
				$aPassword = $aUpperCase;
147
				$bMerge = true;
148
			}
149
		}
150
		if($iElements & self::PW_USE_DIGITS) {
151
			if($bMerge) {
152
				$iNumberOfDigits = (rand(1000, 10000) % ceil($iLength / 2.5));
153
				$iNumberOfDigits = $iNumberOfDigits ? $iNumberOfDigits : 1;
154
				$aPassword = self::_mergeIntoPassword($aPassword, $aDigits, $iNumberOfDigits);
155
			}else {
156
				$aPassword = $aDigits;
157
				$bMerge = true;
158
			}
159
		}
160
		if($iElements & self::PW_USE_SPECIAL) {
161
			if($bMerge) {
162
				$iNumberOfSpecialChars = rand(1000, 10000) % ceil($iLength / 5);
163
				$iNumberOfSpecialChars = $iNumberOfSpecialChars ? $iNumberOfSpecialChars : 1;
164
				$aPassword = self::_mergeIntoPassword($aPassword, $aSpecialChars, $iNumberOfSpecialChars);
165
			}else {
166
				$aPassword = $aSpecialChars;
167
				$bMerge = true;
168
			}
169
		}
170
		$sPassword = implode('', array_slice($aPassword, 0, $iLength));
171
		return $sPassword;
172
	}
173
/**
174
 * merges $iCount chars from $aInsert randomly into $aPassword
175
 * @param array $aPassword
176
 * @param array $aInsert
177
 * @param integer $iCount
178
 * @return array
179
 */
180
	private static function _mergeIntoPassword($aPassword, $aInsert, $iCount)
181
	{
182
		$aListOfIndexes = array();
183
		while(sizeof($aListOfIndexes) < $iCount) {
184
			$x = rand(1000, 10000) % sizeof($aInsert);
185
			$aListOfIndexes[$x] = $x;
186
		}
187
		foreach($aListOfIndexes as $x) {
188
			$aPassword[$x] = $aInsert[$x];
189
		}
190
		return $aPassword;
191
	}
192

  
193
} // end of class PasswordHash
0 194

  

Also available in: Unified diff