Enforce Strong Passwords Without a Plugin

Improve your WordPress site’s security and enforce strong passwords for all your site’s users. We’ll go through securing your site by adding just a little bit of code to your theme.

To follow this tutorial, you’ll need to be using a custom child theme, so we can make changes to functions.php. We’ll keep the code tidy by using a standalone PHP file, so it’ll be really easy to reuse it in other WordPress projects.

infoIf your site is hosted with Headwall Hosting then you don’t need to follow this tutorial, because our managed WordPress hosting packages automatically enforce strong user passwords for you.

infoTry combining this tutorial with disabling user enumeration and hiding your wp-login.php from bots to increase your website’s security even more.

Define the Problem

Before we start tapping away at the keyboard, we need to be clear on what the problem is and how we’re going to solve it. We don’t need a big project plan, but if you have a clear idea of the logic-flow before you start writing code, it’ll be easier to write the code, and it will be better code – usually.

  • Prevent users of your website being able to set weak passwords.
  • Code should be in a standalone PHP file that we can just throw in to any of our WordPress projects in the future, and it’ll just work.
  • We don’t want to have any extra JavaScript dependencies if we can avoid them.

Modern versions WordPress have some built-in hooks to help us out here:

We’re just going to hook these actions, run a test on whatever password is given to us, then either reject it or accept it – based on a bit of easy-to-follow logic. We could get all clever and try to give the password a score, such as “weak”, “fair”, etc, but really all we want to do is stop anything less than a strong password.

Let’s Write some Code

We need somewhere to write our code, so go to the folder for your custom child theme, create a new file called functions-strong-passwords.php and paste the following into it.

<?php

/**
 * Enforce strong passwords (ESP) for site users.
 *
 * To disable enforcing strong passwords:
 *   define('ESP_IS_ENABLED', false);
 */

if (!defined('WPINC')) {
	exit('Do NOT access this file directly.');
}

/**
 * Initialise constants and handlers.
 */
function esp_init() {
	if (defined('ESP_IS_ENABLED') && (ESP_IS_ENABLED === false)) {
		// Disabled by configuration.
	} else {
		// Hook into WordPress here...
		// ...
	}
}
add_action('init', 'esp_init');

/**
 * Given a password, return true if it's OK, otherwise return false.
 */
function esp_is_password_ok($password, $user_name) {
	$is_ok = false;

	// ...

	return $is_ok;
}

This is the skeleton of our little module. To make sure it’s referenced by WordPress, we just need to add the following to your child theme’s functions.php file.

// Enforce strong passwords.
require_once 'functions-strong-passwords.php';

These two chunks of code are the starting point for extending a custom child theme… getting our foot in-the-door. Now all we need to do is hook the three actions, and put our password-checking code in the esp_is_password_ok() function.

Enforce Password Strength

The function to enforce a strong password is quite simple – as it should be. Just a single function that takes a couple of input parameters and returns a true or false. Let’s take a look at it – don’t copy-and-paste this yet. The full code listing is towards the end of the post..

function esp_is_password_ok($password, $user_name) {
	// Default to the password not being valid - fail safe.
	$is_ok = false;

	$password = sanitize_text_field($password);
	$user_name = sanitize_text_field($user_name);

	$is_number_found = preg_match('/[0-9]/', $password);
	$is_lowercase_found = preg_match('/[a-z]/', $password);
	$is_uppercase_found = preg_match('/[A-Z]/', $password);
	$is_symbol_found = preg_match('/[^a-zA-Z0-9]/', $password);

	if (strlen($password) < 8) {
		// Too short
	} elseif (strtolower($user_name) == strtolower($password)) {
		// User name and password can't be the same.
	} elseif (!$is_number_found) {
		// ...
	} elseif (!$is_lowercase_found) {
		// ...
	} elseif (!$is_uppercase_found) {
		// ...
	} elseif (!$is_symbol_found) {
		// ...
	} else {
		// Password is OK.
		$is_ok = true;
	}

	return $is_ok;
}

This code should be easy to read from start to finish. The only black-art mojo is the preg_match() stuff. These funny functions are called regular expressions – a really powerful concept but they can be a bit tricky to get your head around at first. Because regular expressions can be tricky, we store the results of these functions in variable names that are easy to read. That way we can write a series of if/elseif statements that are really clean. Even a non-programmer can look at this function, see how it works and figure-out that passwords need…

  1. At least 8 characters in length.
  2. The user name and password can’t be the same.
  3. Passwords must have at least one number, one lowercase character, one uppercase and one symbol.

Putting it All Together

We’ve laid out the structure of functions-strong-passwords.php and sorted out the logic for the password good-or-bad code, so let’s bring it all together. This is the complete contents of our functions-strong-passwords.php file.

<?php

/**
 * Enforce strong passwords (ESP) for all website users.
 *
 * To disable enforcing strong passwords:
 *   define('ESP_IS_ENABLED', false);
 */

if (!defined('WPINC')) {
	exit('Do NOT access this file directly.');
}

/**
 * Initialise constants and handlers.
 */
function esp_init() {
	if (defined('ESP_IS_ENABLED') && (ESP_IS_ENABLED === false)) {
		// Disabled by configuration.
	} else {
		add_action('user_profile_update_errors', 'esp_user_profile_update_errors', 0, 3);
		add_action('resetpass_form', 'esp_resetpass_form', 10);
		add_action('validate_password_reset', 'esp_validate_password_reset', 10, 2);
	}
}
add_action('init', 'esp_init');

function esp_user_profile_update_errors($errors, $update, $user_data) {
	return esp_validate_password_reset($errors, $user_data);
}

function esp_resetpass_form($user_data) {
	return esp_validate_password_reset(false, $user_data);
}

/**
 * Sanitise the input parameters and then check the password strength.
 */
function esp_validate_password_reset($errors, $user_data) {
	$is_password_ok = false;

	$user_name = null;
	if (isset($_POST['user_login'])) {
		$user_name = sanitize_text_field($_POST['user_login']);
	} elseif (isset($user_data->user_login)) {
		$user_name = $user_data->user_login;
	} else {
		// No user specified.
	}

	$password = null;
	if (isset($_POST['pass1']) && !empty(trim($_POST['pass1']))) {
		$password = sanitize_text_field(trim($_POST['pass1']));
	}

	$error_message = null;
	if (is_null($password)) {
		// Don't do anything if there isn't a password to check.
	} elseif (is_wp_error($errors) && $errors->get_error_data('pass')) {
		// We've already got a password-related error.
	} elseif (empty($user_name)) {
		$error_message = __('User name cannot be empty.');
	} elseif (!($is_password_ok = esp_is_password_ok($password, $user_name))) {
		$error_message = __('Password is not strong enough.');
	} else {
		// Password is strong enough. All OK.
	}

	if (!empty($error_message)) {
		$error_message = '<strong>ERROR</strong>: ' . $error_message;
		if (!is_a($errors, 'WP_Error')) {
			$errors = new WP_Error('pass', $error_message);
		} else {
			$errors->add('pass', $error_message);
		}
	}

	return $errors;
}

/**
 * Given a password, return true if it's OK, otherwise return false.
 */
function esp_is_password_ok($password, $user_name) {
	// Default to the password not being valid - fail safe.
	$is_ok = false;

	$password = sanitize_text_field($password);
	$user_name = sanitize_text_field($user_name);

	$is_number_found = preg_match('/[0-9]/', $password);
	$is_lowercase_found = preg_match('/[a-z]/', $password);
	$is_uppercase_found = preg_match('/[A-Z]/', $password);
	$is_symbol_found = preg_match('/[^a-zA-Z0-9]/', $password);

	if (strlen($password) < 8) {
		// Too short
	} elseif (strtolower($user_name) == strtolower($password)) {
		// User name and password can't be the same.
	} elseif (!$is_number_found) {
		// ...
	} elseif (!$is_lowercase_found) {
		// ...
	} elseif (!$is_uppercase_found) {
		// ...
	} elseif (!$is_symbol_found) {
		// ...
	} else {
		// Password is OK.
		$is_ok = true;
	}

	return $is_ok;
}

There you have it. Run some tests and try to set a weak password for one of your users – you should see that it won’t let you do it. Win!

image-4317040

No more users with “password1234” for their logins 🙂

Leave a Comment

Your email address will not be published. Required fields are marked *