From 24ffbac6f40f6e13948d0dfe2ea8041e148d7ab4 Mon Sep 17 00:00:00 2001 From: Ville Korhonen Date: Wed, 21 Feb 2024 20:12:35 +0200 Subject: [PATCH] Import external bcrypt plugin for password hashing It was noted in that our project template defaults to WordPress default hashing method for password, which is still in 2024 weak MD5. There has for a long time existed mu-plugin, which can be used to replace password hashing with bcrypt. This plugin hooks to WordPress core password handling functions, and overrides password checks so all obsolete MD5-hashed passwords will be converted to bcrypt (or whatever is in future the PHP default password hashing function). See source code for more documentation. Source: Original commit: <15f0d8919fb3731f79a0cf2fb47e1baecb86cb26> License: MIT Author: Roots Author URI: Closes: #216 --- .gitignore | 1 + .../mu-plugins/wp-password-bcrypt.php | 137 ++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 htdocs/wp-content/mu-plugins/wp-password-bcrypt.php diff --git a/.gitignore b/.gitignore index 5673b24..5da7df5 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ htdocs/wp-content/mu-plugins/* !htdocs/wp-content/mu-plugins/.gitkeep !htdocs/wp-content/mu-plugins/register-theme-directory.php !htdocs/wp-content/mu-plugins/bedrock-autoloader.php +!htdocs/wp-content/mu-plugins/wp-password-bcrypt.php htdocs/wp-content/themes/twenty* !htdocs/wp-content/themes/.gitkeep htdocs/wp-content/languages/* diff --git a/htdocs/wp-content/mu-plugins/wp-password-bcrypt.php b/htdocs/wp-content/mu-plugins/wp-password-bcrypt.php new file mode 100644 index 0000000..e665911 --- /dev/null +++ b/htdocs/wp-content/mu-plugins/wp-password-bcrypt.php @@ -0,0 +1,137 @@ +CheckPassword($password, $hash)) { + $hash = wp_set_password($password, $user_id); + } + + return apply_filters( + 'check_password', + password_verify($password, $hash), + $password, + $hash, + $user_id + ); +} + +/** + * Hash the provided password using the PASSWORD_DEFAULT (bcrypt) + * algorithm. + * + * @link https://www.php.net/manual/en/function.password-hash.php + * + * @param string $password The password in plain text. + * @return string + */ +function wp_hash_password($password) +{ + return password_hash( + $password, + PASSWORD_DEFAULT, + apply_filters('wp_hash_password_options', []) + ); +} + +/** + * Hash and update the user's password. + * + * @param string $password The new user password in plaintext. + * @param int $user_id The user ID. + * @return string + */ +function wp_set_password($password, $user_id) +{ + $hash = wp_hash_password($password); + $is_api_request = apply_filters( + 'application_password_is_api_request', + (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) || + (defined('REST_REQUEST') && REST_REQUEST) + ); + + if (! $is_api_request) { + global $wpdb; + + $wpdb->update($wpdb->users, [ + 'user_pass' => $hash, + 'user_activation_key' => '' + ], ['ID' => $user_id]); + + clean_user_cache($user_id); + + return $hash; + } + + if ( + ! class_exists('WP_Application_Passwords') || + empty($passwords = WP_Application_Passwords::get_user_application_passwords($user_id)) + ) { + return; + } + + global $wp_hasher; + + if (empty($wp_hasher)) { + require_once ABSPATH . WPINC . '/class-phpass.php'; + $wp_hasher = new PasswordHash(8, true); + } + + foreach ($passwords as $key => $value) { + if (! $wp_hasher->CheckPassword($password, $value['password'])) { + continue; + } + + $passwords[$key]['password'] = $hash; + } + + update_user_meta( + $user_id, + WP_Application_Passwords::USERMETA_KEY_APPLICATION_PASSWORDS, + $passwords + ); + + return $hash; +}