[TikiWiki-commits] [Git][tikiwiki/tiki][29.x] [FIX] Prevent unauthenticated password change via new_user_validation
"Espoir Baraka \(@esbarakabigega\) via TikiWiki-cvs" <[email protected]>
| Newsgroups | gmane.comp.cms.tiki.cvs |
|---|---|
| Message-ID | <6a84138b91887_3818c34c418f@gitlab-sidekiq-low-urgency-cpu-bound-v2-b96b6f55-rfbtd.mail> |
Espoir Baraka pushed to branch 29.x at Tiki Wiki CMS Groupware / Tiki
Commits:
5d6fd676 by Espoir Baraka at 2026-08-18T10:04:04+02:00
[FIX] Prevent unauthenticated password change via new_user_validation
---
* [FIX] Prevent unauthenticated password change via new_user_validation
---
* [FIX] Prevent unauthenticated password change via new_user_validation
---
* [FIX] Prevent unauthenticated password change via new_user_validation
Bind new-user password setup to a server-side session marker set after
account validation, instead of trusting the client-supplied flag.
(cherry picked from commit 5b92a8de526556ef3402d6d7e2c03a9b9b48792e)
See merge request tikiwiki/tiki!10909
(cherry picked from commit 8d9789194c0f9de3ce50ea82445c59238eaa56ff)
See merge request tikiwiki/tiki!10914
(cherry picked from commit 9e59c97b04280ce9212421f7987157796c9a995b)
See merge request tikiwiki/tiki!10929
- - - - -
3 changed files:
- installer/tiki-installer.php
- tiki-change_password.php
- tiki-login_validate.php
Changes:
=====================================
installer/tiki-installer.php
=====================================
@@ -623,7 +623,8 @@ if ($install_step == '9') {
if ($install_type == 'scratch') {
initialize_prefs(true);
TikiLib::lib('unifiedsearch')->rebuild();
- $u = isset($defaultpass) ? 'tiki-change_password.php?user=admin&oldpass=' . $defaultpass . '&newuser=y' : 'tiki-change_password.php?user=admin&newuser=y';
+ // Always pass oldpass so first admin password setup cannot rely on a forgeable flag
+ $u = 'tiki-change_password.php?user=admin&oldpass=' . urlencode($defaultpass ?? 'admin') . '&newuser=y';
$tikilib = TikiLib::lib('tiki');
$tikilib->set_preference('tiki_install_version', $TWV->version);
} else {
=====================================
tiki-change_password.php
=====================================
@@ -39,12 +39,47 @@ if (! isset($_REQUEST["oldpass"])) {
$user = $_REQUEST["user"];
$secure_token = $_REQUEST["token"] ?? '';
-if (isset($_REQUEST["newuser"]) && $_REQUEST["newuser"] == 'y') {
+$pass_confirm = $userlib->getOne('select `pass_confirm` from `users_users` where binary `login`=?', [$user]);
+$must_change_password = ($pass_confirm === 0 || $pass_confirm === null);
+
+/**
+ * New-user password setup is only allowed after a server-side validation step
+ * (email/admin validation) that stored a session marker for this exact user.
+ * Never trust client-supplied new_user_validation / newuser alone.
+ */
+$getPendingNewUserPasswordUser = static function (): ?string {
+ $pending = $_SESSION['pending_new_user_password']['user'] ?? null;
+ if (is_string($pending) && $pending !== '') {
+ return $pending;
+ }
+ $fromValidation = $_SESSION['last_validation']['user'] ?? null;
+ if (is_string($fromValidation) && $fromValidation !== '') {
+ return $fromValidation;
+ }
+ return null;
+};
+
+$pending_new_user = $getPendingNewUserPasswordUser();
+$server_new_user_validation = $must_change_password
+ && $pending_new_user !== null
+ && hash_equals($pending_new_user, (string) $user);
+
+if ($server_new_user_validation) {
+ $smarty->assign('new_user_validation', 'y');
+} elseif (isset($_REQUEST["newuser"]) && $_REQUEST["newuser"] == 'y' && $must_change_password) {
$smarty->assign('new_user_validation', 'y');
}
$smarty->assign('userlogin', $_REQUEST["user"]);
-$smarty->assign('oldpass', $_REQUEST["oldpass"]);
+if (
+ empty($_REQUEST['oldpass'])
+ && $server_new_user_validation
+ && ! empty($_SESSION['last_validation']['pass'])
+) {
+ $smarty->assign('oldpass', $_SESSION['last_validation']['pass']);
+} else {
+ $smarty->assign('oldpass', $_REQUEST["oldpass"]);
+}
$smarty->assign('secure_token', $secure_token);
if (isset($_REQUEST["change"])) {
@@ -54,9 +89,6 @@ if (isset($_REQUEST["change"])) {
$is_authenticated = false;
$authenticated_oldpass = null;
$can_change_password = false;
- $is_new_user_validation = isset($_REQUEST["new_user_validation"]) && $_REQUEST["new_user_validation"] === 'y';
- $pass_confirm = $userlib->getOne('select `pass_confirm` from `users_users` where binary `login`=?', [$user]);
- $must_change_password = ($pass_confirm === 0 || $pass_confirm === null);
// Method 1: Activation code
if (! empty($_REQUEST['actpass'])) {
@@ -91,11 +123,16 @@ if (isset($_REQUEST["change"])) {
$is_authenticated = true;
$can_change_password = true;
$authenticated_oldpass = $_REQUEST['oldpass'];
+ } elseif ($server_new_user_validation) {
+ // After email validation, provpass may be present in the form but not yet a login hash
+ $is_authenticated = true;
+ $can_change_password = true;
} else {
Feedback::error(tra("Invalid old password"));
}
- } elseif ($is_new_user_validation) {
- // Method 4: New user validation (no authentication required - legitimate exception)
+ } elseif ($server_new_user_validation) {
+ // Method 4: New user validation — session-bound after real account validation only
+ $is_authenticated = true;
$can_change_password = true;
} elseif ($must_change_password) {
// Method 5: User must change password - old password is required
@@ -144,8 +181,11 @@ if (isset($_REQUEST["change"])) {
$userlib->addPasswordHistory($user, $_REQUEST["pass"]);
}
+ // One-time marker: do not allow reuse of the validation session for another change
+ unset($_SESSION['pending_new_user_password'], $_SESSION['last_validation']);
+
// Mark reset token as used only after successful password change
- if (! empty($secure_token) && ! $is_new_user_validation && ! $must_change_password) {
+ if (! empty($secure_token) && ! $server_new_user_validation && ! $must_change_password) {
$passwordResetLib = new \Tiki\Lib\Auth\PasswordResetLib();
$passwordResetLib->markPasswordResetTokenUsed($user, $secure_token);
}
=====================================
tiki-login_validate.php
=====================================
@@ -81,6 +81,10 @@ if ($isvalid) {
if (! empty($_SESSION['last_validation']['pass'])) {
$smarty->assign('oldpass', $_SESSION['last_validation']['pass']);
}
+ $_SESSION['pending_new_user_password'] = [
+ 'user' => $_REQUEST['user'],
+ 'created' => $tikilib->now,
+ ];
$smarty->assign('new_user_validation', 'y');
$smarty->assign('userlogin', $_REQUEST['user']);
if ($prefs['login_is_email'] === 'y') {
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/5d6fd676fe65a835ddc0f5a9b79a83c9f88c9243
--
View it on GitLab: https://gitlab.com/tikiwiki/tiki/-/commit/5d6fd676fe65a835ddc0f5a9b79a83c9f88c9243
You're receiving this email because of your account on gitlab.com. Manage all notifications: https://gitlab.com/-/profile/notifications | Help: https://gitlab.com/help
_______________________________________________
TikiWiki-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tikiwiki-cvs