From 85d00ec54c3a552ca28a996f6e1209e45821358a Mon Sep 17 00:00:00 2001 From: John Blackbourn Date: Mon, 3 Feb 2025 19:50:50 +0000 Subject: [PATCH] Security: Add the `SensitiveParameter` attribute to sensitive parameters. Values passed to parameters with this attribute will be redacted if present in a stack trace when using PHP 8.2 or later. This reduces the chance that passwords and security keys get accidentally exposed in debug logs and bug reports. Props petitphp, TobiasBg, jrf, johnbillion. Fixes #57304 git-svn-id: https://develop.svn.wordpress.org/trunk@59754 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/includes/class-wp-importer.php | 8 ++- src/wp-admin/includes/upgrade.php | 19 ++++++- .../class-wp-application-passwords.php | 5 +- src/wp-includes/class-wp-xmlrpc-server.php | 12 ++++- src/wp-includes/class-wpdb.php | 8 ++- src/wp-includes/ms-functions.php | 54 ++++++++++++++++--- src/wp-includes/pluggable-deprecated.php | 17 +++++- src/wp-includes/pluggable.php | 24 +++++++-- .../class-wp-rest-users-controller.php | 7 ++- src/wp-includes/user.php | 53 +++++++++++++++--- 10 files changed, 178 insertions(+), 29 deletions(-) diff --git a/src/wp-admin/includes/class-wp-importer.php b/src/wp-admin/includes/class-wp-importer.php index eca3199a7b5c7..8edef7dba58de 100644 --- a/src/wp-admin/includes/class-wp-importer.php +++ b/src/wp-admin/includes/class-wp-importer.php @@ -195,7 +195,13 @@ public function cmpr_strlen( $a, $b ) { * @param bool $head * @return array */ - public function get_page( $url, $username = '', $password = '', $head = false ) { + public function get_page( + $url, + $username = '', + #[\SensitiveParameter] + $password = '', + $head = false + ) { // Increase the timeout. add_filter( 'http_request_timeout', array( $this, 'bump_request_timeout' ) ); diff --git a/src/wp-admin/includes/upgrade.php b/src/wp-admin/includes/upgrade.php index 068e28b040e33..94f40cda318a1 100644 --- a/src/wp-admin/includes/upgrade.php +++ b/src/wp-admin/includes/upgrade.php @@ -44,7 +44,16 @@ * @type string $password_message The explanatory message regarding the password. * } */ - function wp_install( $blog_title, $user_name, $user_email, $is_public, $deprecated = '', $user_password = '', $language = '' ) { + function wp_install( + $blog_title, + $user_name, + $user_email, + $is_public, + $deprecated = '', + #[\SensitiveParameter] + $user_password = '', + $language = '' + ) { if ( ! empty( $deprecated ) ) { _deprecated_argument( __FUNCTION__, '2.6.0' ); } @@ -563,7 +572,13 @@ function wp_install_maybe_enable_pretty_permalinks() { * @param string $password Administrator's password. Note that a placeholder message is * usually passed instead of the actual password. */ - function wp_new_blog_notification( $blog_title, $blog_url, $user_id, $password ) { + function wp_new_blog_notification( + $blog_title, + $blog_url, + $user_id, + #[\SensitiveParameter] + $password + ) { $user = new WP_User( $user_id ); $email = $user->user_email; $name = $user->user_login; diff --git a/src/wp-includes/class-wp-application-passwords.php b/src/wp-includes/class-wp-application-passwords.php index b76b5c7e2a5ae..43965790594b1 100644 --- a/src/wp-includes/class-wp-application-passwords.php +++ b/src/wp-includes/class-wp-application-passwords.php @@ -459,7 +459,10 @@ protected static function set_user_application_passwords( $user_id, $passwords ) * @param string $raw_password The raw application password. * @return string The chunked password. */ - public static function chunk_password( $raw_password ) { + public static function chunk_password( + #[\SensitiveParameter] + $raw_password + ) { $raw_password = preg_replace( '/[^a-z\d]/i', '', $raw_password ); return trim( chunk_split( $raw_password, 4, ' ' ) ); diff --git a/src/wp-includes/class-wp-xmlrpc-server.php b/src/wp-includes/class-wp-xmlrpc-server.php index a406b63105d6e..e69d0eb395acf 100644 --- a/src/wp-includes/class-wp-xmlrpc-server.php +++ b/src/wp-includes/class-wp-xmlrpc-server.php @@ -285,7 +285,11 @@ public function addTwoNumbers( $args ) { * @param string $password User's password. * @return WP_User|false WP_User object if authentication passed, false otherwise. */ - public function login( $username, $password ) { + public function login( + $username, + #[\SensitiveParameter] + $password + ) { if ( ! $this->is_enabled ) { $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site.' ) ) ); return false; @@ -330,7 +334,11 @@ public function login( $username, $password ) { * @param string $password User's password. * @return bool Whether authentication passed. */ - public function login_pass_ok( $username, $password ) { + public function login_pass_ok( + $username, + #[\SensitiveParameter] + $password + ) { return (bool) $this->login( $username, $password ); } diff --git a/src/wp-includes/class-wpdb.php b/src/wp-includes/class-wpdb.php index 69b934ae38ed2..5ace90d490e17 100644 --- a/src/wp-includes/class-wpdb.php +++ b/src/wp-includes/class-wpdb.php @@ -749,7 +749,13 @@ class wpdb { * @param string $dbname Database name. * @param string $dbhost Database host. */ - public function __construct( $dbuser, $dbpassword, $dbname, $dbhost ) { + public function __construct( + $dbuser, + #[\SensitiveParameter] + $dbpassword, + $dbname, + $dbhost + ) { if ( WP_DEBUG && WP_DEBUG_DISPLAY ) { $this->show_errors(); } diff --git a/src/wp-includes/ms-functions.php b/src/wp-includes/ms-functions.php index 404eb1497a7cc..1f890dab69232 100644 --- a/src/wp-includes/ms-functions.php +++ b/src/wp-includes/ms-functions.php @@ -938,7 +938,16 @@ function wpmu_signup_user( $user, $user_email, $meta = array() ) { * @param array $meta Optional. Signup meta data. By default, contains the requested privacy setting and lang_id. * @return bool */ -function wpmu_signup_blog_notification( $domain, $path, $title, $user_login, $user_email, $key, $meta = array() ) { +function wpmu_signup_blog_notification( + $domain, + $path, + $title, + $user_login, + $user_email, + #[\SensitiveParameter] + $key, + $meta = array() +) { /** * Filters whether to bypass the new site email notification. * @@ -1073,7 +1082,13 @@ function wpmu_signup_blog_notification( $domain, $path, $title, $user_login, $us * @param array $meta Optional. Signup meta data. Default empty array. * @return bool */ -function wpmu_signup_user_notification( $user_login, $user_email, $key, $meta = array() ) { +function wpmu_signup_user_notification( + $user_login, + $user_email, + #[\SensitiveParameter] + $key, + $meta = array() +) { /** * Filters whether to bypass the email notification for new user sign-up. * @@ -1175,7 +1190,10 @@ function wpmu_signup_user_notification( $user_login, $user_email, $key, $meta = * @param string $key The activation key provided to the user. * @return array|WP_Error An array containing information about the activated user and/or blog. */ -function wpmu_activate_signup( $key ) { +function wpmu_activate_signup( + #[\SensitiveParameter] + $key +) { global $wpdb; $signup = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->signups WHERE activation_key = %s", $key ) ); @@ -1327,7 +1345,12 @@ function wp_delete_signup_on_user_delete( $id, $reassign, $user ) { * @param string $email The new user's email address. * @return int|false Returns false on failure, or int $user_id on success. */ -function wpmu_create_user( $user_name, $password, $email ) { +function wpmu_create_user( + $user_name, + #[\SensitiveParameter] + $password, + $email +) { $user_name = preg_replace( '/\s+/', '', sanitize_user( $user_name, true ) ); $user_id = wp_create_user( $user_name, $password, $email ); @@ -1611,7 +1634,14 @@ function domain_exists( $domain, $path, $network_id = 1 ) { * @param array $meta Optional. Signup meta data. By default, contains the requested privacy setting and lang_id. * @return bool Whether the email notification was sent. */ -function wpmu_welcome_notification( $blog_id, $user_id, $password, $title, $meta = array() ) { +function wpmu_welcome_notification( + $blog_id, + $user_id, + #[\SensitiveParameter] + $password, + $title, + $meta = array() +) { $current_network = get_network(); /** @@ -1845,7 +1875,12 @@ function wpmu_new_site_admin_notification( $site_id, $user_id ) { * @param array $meta Optional. Signup meta data. Default empty array. * @return bool */ -function wpmu_welcome_user_notification( $user_id, $password, $meta = array() ) { +function wpmu_welcome_user_notification( + $user_id, + #[\SensitiveParameter] + $password, + $meta = array() +) { $current_network = get_network(); /** @@ -2271,7 +2306,12 @@ function add_existing_user_to_blog( $details = false ) { * @param string $password User password. Ignored. * @param array $meta Signup meta data. */ -function add_new_user_to_blog( $user_id, $password, $meta ) { +function add_new_user_to_blog( + $user_id, + #[\SensitiveParameter] + $password, + $meta +) { if ( ! empty( $meta['add_to_blog'] ) ) { $blog_id = $meta['add_to_blog']; $role = $meta['new_role']; diff --git a/src/wp-includes/pluggable-deprecated.php b/src/wp-includes/pluggable-deprecated.php index 11e393ddfdb84..11ccad2e8a57b 100644 --- a/src/wp-includes/pluggable-deprecated.php +++ b/src/wp-includes/pluggable-deprecated.php @@ -101,7 +101,15 @@ function get_user_by_email($email) { * @param string $siteurl Optional. Will be used instead of SITECOOKIEPATH if set * @param bool $remember Optional. Remember that the user is logged in */ -function wp_setcookie($username, $password = '', $already_md5 = false, $home = '', $siteurl = '', $remember = false) { +function wp_setcookie( + $username, + #[\SensitiveParameter] + $password = '', + $already_md5 = false, + $home = '', + $siteurl = '', + $remember = false +) { _deprecated_function( __FUNCTION__, '2.5.0', 'wp_set_auth_cookie()' ); $user = get_user_by('login', $username); wp_set_auth_cookie($user->ID, $remember); @@ -168,7 +176,12 @@ function wp_get_cookie_login() { * @param string $deprecated Not used * @return bool True on successful check, false on login failure. */ -function wp_login($username, $password, $deprecated = '') { +function wp_login( + $username, + #[\SensitiveParameter] + $password, + $deprecated = '' +) { _deprecated_function( __FUNCTION__, '2.5.0', 'wp_signon()' ); global $error; diff --git a/src/wp-includes/pluggable.php b/src/wp-includes/pluggable.php index f4a8d8412e88c..60ad0841ed8ce 100644 --- a/src/wp-includes/pluggable.php +++ b/src/wp-includes/pluggable.php @@ -598,7 +598,11 @@ function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() * @return WP_User|WP_Error WP_User object if the credentials are valid, * otherwise WP_Error. */ - function wp_authenticate( $username, $password ) { + function wp_authenticate( + $username, + #[\SensitiveParameter] + $password + ) { $username = sanitize_user( $username ); $password = trim( $password ); @@ -2631,7 +2635,10 @@ function wp_hash( $data, $scheme = 'auth', $algo = 'md5' ) { * @param string $password Plain text user password to hash. * @return string The hash string of the password. */ - function wp_hash_password( $password ) { + function wp_hash_password( + #[\SensitiveParameter] + $password + ) { global $wp_hasher; if ( empty( $wp_hasher ) ) { @@ -2667,7 +2674,12 @@ function wp_hash_password( $password ) { * @param string|int $user_id Optional. User ID. * @return bool False, if the $password does not match the hashed password. */ - function wp_check_password( $password, $hash, $user_id = '' ) { + function wp_check_password( + #[\SensitiveParameter] + $password, + $hash, + $user_id = '' + ) { global $wp_hasher; // If the hash is still md5... @@ -2863,7 +2875,11 @@ function wp_rand( $min = null, $max = null ) { * @param string $password The plaintext new user password. * @param int $user_id User ID. */ - function wp_set_password( $password, $user_id ) { + function wp_set_password( + #[\SensitiveParameter] + $password, + $user_id + ) { global $wpdb; $old_user_data = get_userdata( $user_id ); diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php index 84bcea052cf32..64578c7810b7b 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php @@ -1310,7 +1310,12 @@ public function check_username( $value, $request, $param ) { * @param string $param The parameter name. * @return string|WP_Error The sanitized password, if valid, otherwise an error. */ - public function check_user_password( $value, $request, $param ) { + public function check_user_password( + #[\SensitiveParameter] + $value, + $request, + $param + ) { $password = (string) $value; if ( empty( $password ) ) { diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php index e4e6f5b652744..ede1330251263 100644 --- a/src/wp-includes/user.php +++ b/src/wp-includes/user.php @@ -150,7 +150,12 @@ function wp_signon( $credentials = array(), $secure_cookie = '' ) { * @param string $password Password for authentication. * @return WP_User|WP_Error WP_User on success, WP_Error on failure. */ -function wp_authenticate_username_password( $user, $username, $password ) { +function wp_authenticate_username_password( + $user, + $username, + #[\SensitiveParameter] + $password +) { if ( $user instanceof WP_User ) { return $user; } @@ -228,7 +233,12 @@ function wp_authenticate_username_password( $user, $username, $password ) { * @param string $password Password for authentication. * @return WP_User|WP_Error WP_User on success, WP_Error on failure. */ -function wp_authenticate_email_password( $user, $email, $password ) { +function wp_authenticate_email_password( + $user, + $email, + #[\SensitiveParameter] + $password +) { if ( $user instanceof WP_User ) { return $user; } @@ -301,7 +311,12 @@ function wp_authenticate_email_password( $user, $email, $password ) { * @param string $password Password. If not empty, cancels the cookie authentication. * @return WP_User|WP_Error WP_User on success, WP_Error on failure. */ -function wp_authenticate_cookie( $user, $username, $password ) { +function wp_authenticate_cookie( + $user, + $username, + #[\SensitiveParameter] + $password +) { global $auth_secure_cookie; if ( $user instanceof WP_User ) { @@ -342,7 +357,12 @@ function wp_authenticate_cookie( $user, $username, $password ) { * @return WP_User|WP_Error|null WP_User on success, WP_Error on failure, null if * null is passed in and this isn't an API request. */ -function wp_authenticate_application_password( $input_user, $username, $password ) { +function wp_authenticate_application_password( + $input_user, + $username, + #[\SensitiveParameter] + $password +) { if ( $input_user instanceof WP_User ) { return $input_user; } @@ -2846,7 +2866,12 @@ function wp_update_user( $userdata ) { * @return int|WP_Error The newly created user's ID or a WP_Error object if the user could not * be created. */ -function wp_create_user( $username, $password, $email = '' ) { +function wp_create_user( + $username, + #[\SensitiveParameter] + $password, + $email = '' +) { $user_login = wp_slash( $username ); $user_email = wp_slash( $email ); $user_pass = $password; @@ -3034,7 +3059,11 @@ function get_password_reset_key( $user ) { * @param string $login The user login. * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys. */ -function check_password_reset_key( $key, $login ) { +function check_password_reset_key( + #[\SensitiveParameter] + $key, + $login +) { global $wp_hasher; $key = preg_replace( '/[^a-z0-9]/i', '', $key ); @@ -3371,7 +3400,11 @@ function retrieve_password( $user_login = '' ) { * @param WP_User $user The user * @param string $new_pass New password for the user in plaintext */ -function reset_password( $user, $new_pass ) { +function reset_password( + $user, + #[\SensitiveParameter] + $new_pass +) { /** * Fires before the user's password is reset. * @@ -4932,7 +4965,11 @@ function wp_generate_user_request_key( $request_id ) { * @param string $key Provided key to validate. * @return true|WP_Error True on success, WP_Error on failure. */ -function wp_validate_user_request_key( $request_id, $key ) { +function wp_validate_user_request_key( + $request_id, + #[\SensitiveParameter] + $key +) { global $wp_hasher; $request_id = absint( $request_id );