Greatly improve quality of automatically generated passwords
authorTim Düsterhus <duesterhus@woltlab.com>
Sun, 14 Dec 2014 00:13:43 +0000 (01:13 +0100)
committerTim Düsterhus <duesterhus@woltlab.com>
Sun, 14 Dec 2014 00:31:01 +0000 (01:31 +0100)
Previously the amount of characters for each type was deterministic, as
the character types were chosen in a round robin fashion, instead of
randomly choosing from the entire character set. This lead to about
47 bit of entropy with the default length of 8 characters. Additionally
str_shuffle does not use a SRNG, which may have reduced the entropy even
further.

The new algorithm, choosing fairly from the whole range of alphanumeric
characters with a default length of 12 characters provides about 71 bits
of entropy.

wcfsetup/install/files/lib/acp/form/MasterPasswordInitForm.class.php
wcfsetup/install/files/lib/form/NewPasswordForm.class.php
wcfsetup/install/files/lib/system/worker/SendNewPasswordWorker.class.php
wcfsetup/install/files/lib/util/PasswordUtil.class.php

index 93f105b30fe58aa311234e609e514e163644f380..7f287c16bee2df480880ae3058d073fb27a17936 100755 (executable)
@@ -61,7 +61,7 @@ class MasterPasswordInitForm extends MasterPasswordForm {
                }
                
                // check password security
-               if (mb_strlen($this->masterPassword) < 8) {
+               if (mb_strlen($this->masterPassword) < 12) {
                        throw new UserInputException('masterPassword', 'notSecure');
                }
                // digits
@@ -76,10 +76,6 @@ class MasterPasswordInitForm extends MasterPasswordForm {
                if (!Regex::compile('[A-Z]')->match($this->masterPassword)) {
                        throw new UserInputException('masterPassword', 'notSecure');
                }
-               // special characters
-               if (!Regex::compile('[^0-9a-zA-Z]')->match($this->masterPassword)) {
-                       throw new UserInputException('masterPassword', 'notSecure');
-               }
                
                // password equals username
                if ($this->masterPassword == WCF::getUser()->username) {
@@ -121,7 +117,7 @@ define('MASTER_PASSWORD', '".PasswordUtil::getDoubleSaltedHash($this->masterPass
                
                WCF::getTPL()->assign(array(
                        'confirmMasterPassword' => $this->confirmMasterPassword,
-                       'exampleMasterPassword' => PasswordUtil::getRandomPassword(12),
+                       'exampleMasterPassword' => PasswordUtil::getRandomPassword(16),
                        'relativeWcfDir' => RELATIVE_WCF_DIR
                ));
        }
index 6b4e9f6908d277493fedf7bb146a845cc1172601..e4297d09e6e102ae63ac66e81baa67df12d83881 100644 (file)
@@ -100,7 +100,7 @@ class NewPasswordForm extends AbstractForm {
                parent::save();
                
                // generate new password
-               $this->newPassword = PasswordUtil::getRandomPassword((REGISTER_PASSWORD_MIN_LENGTH > 9 ? REGISTER_PASSWORD_MIN_LENGTH : 9));
+               $this->newPassword = PasswordUtil::getRandomPassword((REGISTER_PASSWORD_MIN_LENGTH > 12 ? REGISTER_PASSWORD_MIN_LENGTH : 12));
                
                // update user
                $this->objectAction = new UserAction(array($this->user), 'update', array(
index fb054e11256c168cc571ddcb8209f4a62dc735b3..6f9a7130505dbdfdad3bd9d067b372f6134914e9 100644 (file)
@@ -79,7 +79,7 @@ class SendNewPasswordWorker extends AbstractWorker {
         * @param       \wcf\data\user\UserEditor       $userEditor
         */
        protected function sendNewPassword(UserEditor $userEditor) {
-               $newPassword = PasswordUtil::getRandomPassword();
+               $newPassword = PasswordUtil::getRandomPassword((REGISTER_PASSWORD_MIN_LENGTH > 12 ? REGISTER_PASSWORD_MIN_LENGTH : 12));
                
                $userAction = new UserAction(array($userEditor), 'update', array(
                        'data' => array(
index ffc88674bcf265e91f81cf303685094e17d5c7f8..34d9e1bc916f10c224e41dc916daea7697887f16 100644 (file)
@@ -14,6 +14,12 @@ use wcf\system\Regex;
  * @category   Community Framework
  */
 final class PasswordUtil {
+       /**
+        * list of possible characters in generated passwords
+        * @var string
+        */
+       const PASSWORD_CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+
        /**
         * concated list of valid blowfish salt characters
         * @var string
@@ -203,27 +209,20 @@ final class PasswordUtil {
        }
        
        /**
-        * Generates a random user password with the given character length.
+        * Generates a random alphanumeric user password with the given character length.
         * 
         * @param       integer         $length
         * @return      string
         */
-       public static function getRandomPassword($length = 8) {
-               $availableCharacters = array(
-                       'abcdefghijklmnopqrstuvwxyz',
-                       'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
-                       '0123456789',
-                       '+#-.,;:?!'
-               );
-               
+       public static function getRandomPassword($length = 12) {
+               $charset = self::PASSWORD_CHARSET;
                $password = '';
-               $type = 0;
-               for ($i = 0; $i < $length; $i++) {
-                       $type = ($i % 4 == 0) ? 0 : ($type + 1);
-                       $password .= substr($availableCharacters[$type], self::secureRandomNumber(0, strlen($availableCharacters[$type]) - 1), 1);
+
+               for ($i = 0, $maxIndex = (strlen($charset) - 1); $i < $length; $i++) {
+                       $password .= $charset[self::secureRandomNumber(0, $maxIndex)];
                }
-               
-               return str_shuffle($password);
+
+               return $password;
        }
        
        /**