CVE-2023-0793: Weak Password Requirements (CWE-521)
value: Weak Password Requirements in GitHub repository thorsten/phpmyfaq prior to 3.1.11.


CWE description: The product does not require that users should have strong passwords, which makes it easier for attackers to compromise user accounts.
CVSS v3 base score: 8.8
Published: 2023-02-12T14:15Z
Repository: https://github.com/thorsten/phpmyfaq
Language: PHP
Files changed: phpmyfaq/admin/ajax.user.php, phpmyfaq/admin/assets/js/user.js, phpmyfaq/admin/user.php
Commit date: 2023-01-23 08:23:30 +0100
Commit message: fix: added missing check on password length

Diff: diff --git a/phpmyfaq/admin/ajax.user.php b/phpmyfaq/admin/ajax.user.php
index 13f2bdf19b..7d15ca8e29 100644
--- a/phpmyfaq/admin/ajax.user.php
+++ b/phpmyfaq/admin/ajax.user.php
@@ -118,10 +118,11 @@ if (
             $postData = json_decode(file_get_contents('php://input'), true);
 
             $userName = Filter::filterVar($postData['userName'], FILTER_UNSAFE_RAW);
             $userRealName = Filter::filterVar($postData['realName'], FILTER_UNSAFE_RAW);
             $userEmail = Filter::filterVar($postData['email'], FILTER_VALIDATE_EMAIL);
+            $automaticPassword = Filter::filterVar($postData['automaticPassword'], FILTER_VALIDATE_BOOLEAN);
             $userPassword = Filter::filterVar($postData['password'], FILTER_UNSAFE_RAW);
             $userPasswordConfirm = Filter::filterVar($postData['passwordConfirm'], FILTER_UNSAFE_RAW);
             $userIsSuperAdmin = Filter::filterVar($postData['isSuperAdmin'], FILTER_VALIDATE_BOOLEAN);
 
             $newUser = new User($faqConfig);
@@ -136,10 +137,16 @@ if (
                 $errorMessage[] = $PMF_LANG['ad_user_error_noRealName'];
             }
             if (is_null($userEmail)) {
                 $errorMessage[] = $PMF_LANG['ad_user_error_noEmail'];
             }
+            if (!$automaticPassword) {
+                if (strlen($userPassword) <= 7 || strlen($userPasswordConfirm) <= 7) {
+                    $errorMessage[] = $PMF_LANG['ad_passwd_fail'];
+                }
+            }
+
             if (count($errorMessage) === 0) {
                 if (!$newUser->createUser($userName, $userPassword)) {
                     $errorMessage[] = $newUser->error();
                 } else {
                     $newUser->userdata->set(['display_name', 'email', 'is_visible'], [$userRealName, $userEmail, 0]);
@@ -202,10 +209,16 @@ if (
                 $http->setStatus(400);
                 $http->sendJsonWithHeaders(['error' => $PMF_LANG['err_NotAuth']]);
                 exit(1);
             }
 
+            if (strlen($newPassword) <= 7 || strlen($retypedPassword) <= 7) {
+                $http->setStatus(400);
+                $http->sendJsonWithHeaders(['error' => $PMF_LANG['ad_passwd_fail']]);
+                exit(1);
+            }
+
             $user->getUserById($userId, true);
             $auth = new Auth($faqConfig);
             $authSource = $auth->selectAuth($user->getAuthSource('name'));
             $authSource->selectEncType($user->getAuthData('encType'));
 
diff --git a/phpmyfaq/admin/assets/js/user.js b/phpmyfaq/admin/assets/js/user.js
index cc5d0cc26e..0c960bb2ac 100644
--- a/phpmyfaq/admin/assets/js/user.js
+++ b/phpmyfaq/admin/assets/js/user.js
@@ -155,20 +155,22 @@ document.addEventListener('DOMContentLoaded', () => {
       event.preventDefault();
       const csrf = document.getElementById('add_user_csrf').value;
       const userName = document.getElementById('add_user_name').value;
       const realName = document.getElementById('add_user_realname').value;
       const email = document.getElementById('add_user_email').value;
+      const automaticPassword = document.getElementById('add_user_automatic_password').checked;
       const password = document.getElementById('add_user_password').value;
       const passwordConfirm = document.getElementById('add_user_password_confirm').value;
       const isSuperAdmin = document.querySelector('#add_user_is_superadmin').checked;
 
       addUserForm.classList.add('was-validated');
 
       const userData = {
         userName,
         realName,
         email,
+        automaticPassword,
         password,
         passwordConfirm,
         isSuperAdmin,
       };
 
diff --git a/phpmyfaq/admin/user.php b/phpmyfaq/admin/user.php
index 95f877d0c0..6a2f83ca1e 100755
--- a/phpmyfaq/admin/user.php
+++ b/phpmyfaq/admin/user.php
@@ -442,20 +442,22 @@ if (
                 <div class="form-group row">
                   <label class="col-lg-4 col-form-label" for="npass">
                       <?= $PMF_LANG['ad_passwd_new'] ?>
                   </label>
                   <div class="col-lg-8">
-                    <input type="password" autocomplete="off" name="npass" id="npass" class="form-control" required>
+                    <input type="password" autocomplete="off" name="npass" id="npass" class="form-control" minlength="8"
+                           required>
                   </div>
                 </div>
 
                 <div class="form-group row">
                   <label class="col-lg-4 col-form-label" for="bpass">
                       <?= $PMF_LANG['ad_passwd_con'] ?>
                   </label>
                   <div class="col-lg-8">
-                    <input type="password" autocomplete="off" name="bpass" id="bpass" class="form-control" required>
+                    <input type="password" autocomplete="off" name="bpass" id="bpass" class="form-control" minlength="8"
+                           required>
                   </div>
                 </div>
 
               </form>
             </div>
@@ -682,20 +684,20 @@ if (
               <div class="form-group row">
                 <label class="col-lg-4 col-form-label"
                        for="add_user_password"><?= $PMF_LANG['ad_adus_password'] ?></label>
                 <div class="col-lg-8">
                   <input type="password" name="add_user_password" id="add_user_password" class="form-control"
-                         autocomplete="off" tabindex="4">
+                         minlength="8" autocomplete="off" tabindex="4">
                 </div>
               </div>
 
               <div class="form-group row">
                 <label class="col-lg-4 col-form-label"
                        for="add_user_password_confirm"><?= $PMF_LANG['ad_passwd_con'] ?></label>
                 <div class="col-lg-8">
                   <input type="password" name="add_user_password_confirm" id="add_user_password_confirm"
-                         class="form-control" autocomplete="off" tabindex="5">
+                         minlength="8" class="form-control" autocomplete="off" tabindex="5">
                 </div>
               </div>
             </div>
 
             <div class="form-group row">

Vulnerable code:                     <input type="password" autocomplete="off" name="npass" id="npass" class="form-control" required>
                    <input type="password" autocomplete="off" name="bpass" id="bpass" class="form-control" required>
                         autocomplete="off" tabindex="4">
                         class="form-control" autocomplete="off" tabindex="5">
Fixed code:             $automaticPassword = Filter::filterVar($postData['automaticPassword'], FILTER_VALIDATE_BOOLEAN);
            if (!$automaticPassword) {
                if (strlen($userPassword) <= 7 || strlen($userPasswordConfirm) <= 7) {
                    $errorMessage[] = $PMF_LANG['ad_passwd_fail'];
                }
            }

            if (strlen($newPassword) <= 7 || strlen($retypedPassword) <= 7) {
                $http->setStatus(400);
                $http->sendJsonWithHeaders(['error' => $PMF_LANG['ad_passwd_fail']]);
                exit(1);
            }

      const automaticPassword = document.getElementById('add_user_automatic_password').checked;
        automaticPassword,
                    <input type="password" autocomplete="off" name="npass" id="npass" class="form-control" minlength="8"
                           required>
                    <input type="password" autocomplete="off" name="bpass" id="bpass" class="form-control" minlength="8"
                           required>
                         minlength="8" autocomplete="off" tabindex="4">
                         minlength="8" class="form-control" autocomplete="off" tabindex="5">