From f11e3f629dbb3cfcd031166e4547a5c14b06c3b4 Mon Sep 17 00:00:00 2001 From: Sainath Poojary Date: Tue, 5 May 2026 20:02:10 +0530 Subject: [PATCH 1/2] Users: Use `wp_check_password()` for proper password comparison --- src/wp-includes/user.php | 49 ++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php index 9c635f63d288a..e41273126d57f 100644 --- a/src/wp-includes/user.php +++ b/src/wp-includes/user.php @@ -2741,26 +2741,37 @@ function wp_update_user( $userdata ) { // Escape data pulled from DB. $user = add_magic_quotes( $user ); - if ( ! empty( $userdata['user_pass'] ) && $userdata['user_pass'] !== $user_obj->user_pass ) { - // If password is changing, hash it now. - $plaintext_pass = $userdata['user_pass']; - $userdata['user_pass'] = wp_hash_password( $userdata['user_pass'] ); + if ( ! empty( $userdata['user_pass'] ) ) { + // Check if the password is actually changing. + if ( $userdata['user_pass'] === $user_obj->user_pass + || wp_check_password( $userdata['user_pass'], $user_obj->user_pass, $user_id ) + ) { + // Password is the same, remove it so wp_insert_user() doesn't update it. + unset( $userdata['user_pass'] ); + } else { + // Used downstream to clear cookies. + $changed_password = true; - /** This action is documented in wp-includes/pluggable.php */ - do_action( 'wp_set_password', $plaintext_pass, $user_id, $user_obj ); + // Store plaintext for the action, then hash for wp_insert_user(). + $plaintext_pass = $userdata['user_pass']; + $userdata['user_pass'] = wp_hash_password( $userdata['user_pass'] ); - /** - * Filters whether to send the password change email. - * - * @since 4.3.0 - * - * @see wp_insert_user() For `$user` and `$userdata` fields. - * - * @param bool $send Whether to send the email. - * @param array $user The original user array. - * @param array $userdata The updated user array. - */ - $send_password_change_email = apply_filters( 'send_password_change_email', true, $user, $userdata ); + /** This action is documented in wp-includes/pluggable.php */ + do_action( 'wp_set_password', $plaintext_pass, $user_id, $user_obj ); + + /** + * Filters whether to send the password change email. + * + * @since 4.3.0 + * + * @see wp_insert_user() For `$user` and `$userdata` fields. + * + * @param bool $send Whether to send the email. + * @param array $user The original user array. + * @param array $userdata The updated user array. + */ + $send_password_change_email = apply_filters( 'send_password_change_email', true, $user, $userdata ); + } } if ( isset( $userdata['user_email'] ) && $user['user_email'] !== $userdata['user_email'] ) { @@ -2920,7 +2931,7 @@ function wp_update_user( $userdata ) { // Update the cookies if the password changed. $current_user = wp_get_current_user(); if ( $current_user->ID === $user_id ) { - if ( isset( $plaintext_pass ) ) { + if ( isset( $changed_password ) ) { /* * Here we calculate the expiration length of the current auth cookie and compare it to the default expiration. * If it's greater than this, then we know the user checked 'Remember Me' when they logged in. From 77c9e7541233bafa486efdaaf4c343962599e196 Mon Sep 17 00:00:00 2001 From: Sainath Poojary Date: Tue, 5 May 2026 20:14:05 +0530 Subject: [PATCH 2/2] Tests: Add unit test for password comparison fix in `wp_update_user()`. --- tests/phpunit/tests/user.php | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/user.php b/tests/phpunit/tests/user.php index 3770816ec4502..65cf5a8ae2383 100644 --- a/tests/phpunit/tests/user.php +++ b/tests/phpunit/tests/user.php @@ -858,6 +858,34 @@ public function test_wp_update_user_should_not_change_password_when_passed_WP_Us $this->assertSame( $pwd_before, $pwd_after ); } + /** + * @ticket 42183 + */ + public function test_wp_update_user_should_not_change_password_when_same_plaintext_password_passed() { + $plaintext_password = 'test_password_42183'; + $user_id = self::factory()->user->create( + array( + 'user_pass' => $plaintext_password, + ) + ); + + $pwd_before = get_userdata( $user_id )->user_pass; + + // Update user with the same plaintext password. + wp_update_user( + array( + 'ID' => $user_id, + 'user_pass' => $plaintext_password, + ) + ); + + // Reload the data. + $pwd_after = get_userdata( $user_id )->user_pass; + + // Password hash should remain unchanged. + $this->assertSame( $pwd_before, $pwd_after ); + } + /** * @ticket 45747 * @group ms-excluded @@ -1567,7 +1595,7 @@ public function test_changing_password_invalidates_password_reset_key() { $userdata = array( 'ID' => $user->ID, - 'user_pass' => 'password', + 'user_pass' => 'new_password', ); wp_update_user( $userdata );