WordPress password reset feature has been exploited by hackers in the past, therefore disabling it is a considerable security feature. In most cases WordPress websites will only have a limited number of users who need to access the admin interface – e.g. admins themselves, shop managers or content editors, therefore in this scenario, it is more than healthy to entrust the password management to the main administrator of the site or at a minimum – to add some limitations for this feature.
On January, 2024 there was a serious security vulnerability in one of the popular and widely used WordPress plugins – Post SMTP. The vulnerability was indirectly related to password reset functionality, which resulted in many sites being hacked.
Disable WordPress password reset for all users
To do this, you can either create a php file which must be placed in “wp-content/mu-plugins” folder (technically a “must use” plugin) and add the following code to it, or you can just append the code to your WordPress theme functions file.
/*Prevents users from resetting their password via the WordPress Login forms*/
/*Note the priority of this filter - it is specifically set to 30, so it can override other of its declarations in the code*/
add_filter( 'allow_password_reset', '__return_false', 30, 1 );
/*Disables direct access to password reset forms present in WordPress Login interface*/
add_action( "login_init", function() {
if (isset( $_GET['action'] )){
if ( in_array( $_GET['action'], array('lostpassword', 'retrievepassword') ) ) {
wp_redirect( wp_login_url(), 301 );
exit;
}
}
});
/*Removes the "Lost your password" text from login screen*/
add_filter( 'lost_password_html_link', '__return_false' );
The comments I’ve left in the code are self-explanatory. These three customizations:
- denies the password reset feature for all users
- redirects users away from the default password reset forms (if they try to access them)
- removes a “Lost Password” link from the WordPress admin login screen
Disable the password reset only for specific users
Sometimes it is necessary to leave the password reset feature enabled for a specific set of users. You can adjust the previous code snippet by modifying the “allow_password_reset” filter to something like this:
add_filter( 'allow_password_reset', 'dwp_deny_pwd_reset_for_staff', 99, 2 );
function dwp_deny_pwd_reset_for_staff($allow, $user_id) {
if(!empty($user_id)) {
$user_meta = get_userdata($user_id);
if(!empty($user_meta)) {
$user_roles = $user_meta->roles;
if(!in_array('customer', $user_roles) && !in_array('subscriber', $user_roles)) {
return false;
}
}
}
return $allow;
}
This will leave the password reset feature enabled for subscribers and WooCommerce customers while disabling it for other user roles.
WP-Members and allow_password_reset filter compatibility issues
WP-Members Membership plugin for WordPress by Chad Butler is a quite popular choice for membership management and custom registration forms, but despite its popularity, in its last version (3.4.9.1) it has a bug which conflicts with other password reset limitations.
This lies in the usage of allow_password_reset – a filter integrated in WordPress which allows to declare specific conditions when a password reset for WordPress users should be allowed or not. When a custom override for this filter has been declared in other plugins or the site theme, WP-Members either:
- enforces its own customizations and basically allows to request a password reset for each and every user;
- crashes the site with throwing a PHP Fatal error when a larger priority number for the other allow_password_reset override is being used
There’s however a simple workaround which fixes the mentioned conflict. For this to work, you need to add this code snippet in your WordPress theme functions file or place / append it to a must-use plugin:
add_filter('wpmem_pwdreset_args', 'dwp_fix_pwd_reset_restrictions_wp_members', 10, 1);
function dwp_fix_pwd_reset_restrictions_wp_members($args) {
if(!empty($args['user'])) {
$user_id = strpos($args['user'], '@') !== false ? email_exists($args['user']) : username_exists($args['user']);
if(!empty($user_id)) {
$should_allow_reset_password = apply_filters( 'allow_password_reset', true, $user_id);
$args['user'] = !empty($should_allow_reset_password)? $args['user'] : '';
}
}
return $args;
}
What this does – it catches an e-mail address or username that was entered in the password reset form and checks it against the site database – if it is a valid user, it then checks if a password reset feature for this user is enabled by applying the “allow_password_reset” filter to the user ID. If the password reset is not available for the particular user, it will artificially trigger an error about the e-mail/username not being found in the system.