Created
June 12, 2025 23:58
-
-
Save chrisdavidmiles/e256b3bf6fea420e3426f1fc133e9968 to your computer and use it in GitHub Desktop.
WordPress Plugin: Password Policy Enforcement
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Plugin Name: Password Policy Enforcement | |
* Description: Enforces strong passwords, provides detailed error messages, and checks passwords against HaveIBeenPwned on creation and reset. | |
* Version: 1.0 | |
* Update URI: false | |
*/ | |
defined('ABSPATH') || exit; // Exit if accessed directly | |
class PasswordPolicyEnforcement { | |
private $hibp_api_url = 'https://api.pwnedpasswords.com/range/'; | |
public function __construct() { | |
add_action('user_profile_update_errors', [$this, 'validate_password'], 10, 3); | |
add_action('validate_password_reset', [$this, 'validate_password_reset'], 10, 2); | |
} | |
public function validate_password($errors, $update, $user) { | |
if (!empty($_POST['pass1'])) { | |
$password = $_POST['pass1']; | |
$this->check_password_policy($password, $errors); | |
} | |
} | |
public function validate_password_reset($errors, $user) { | |
if (!empty($_POST['pass1'])) { | |
$password = $_POST['pass1']; | |
$this->check_password_policy($password, $errors); | |
} | |
} | |
private function check_password_policy($password, &$errors) { | |
$messages = []; | |
if (strlen($password) < 12) { | |
$messages[] = 'Password must be at least 12 characters.'; | |
} | |
if (!preg_match('/[A-Z]/', $password)) { | |
$messages[] = 'Password must contain at least one uppercase letter.'; | |
} | |
if (!preg_match('/[a-z]/', $password)) { | |
$messages[] = 'Password must contain at least one lowercase letter.'; | |
} | |
if (!preg_match('/[0-9]/', $password)) { | |
$messages[] = 'Password must contain at least one number.'; | |
} | |
if (!preg_match('/[\W_]/', $password)) { | |
$messages[] = 'Password must contain at least one special character.'; | |
} | |
if ($this->is_pwned_password($password)) { | |
$messages[] = 'This password has been compromised in a data breach. Please choose a different one.'; | |
} | |
if (!empty($messages)) { | |
foreach ($messages as $message) { | |
$errors->add('password_policy_error', $message); | |
} | |
} | |
} | |
private function is_pwned_password($password) { | |
$sha1 = strtoupper(sha1($password)); | |
$prefix = substr($sha1, 0, 5); | |
$suffix = substr($sha1, 5); | |
$response = wp_remote_get($this->hibp_api_url . $prefix); | |
if (is_wp_error($response)) { | |
return false; // If API fails, don't block user unnecessarily | |
} | |
$body = wp_remote_retrieve_body($response); | |
$lines = explode("\n", $body); | |
foreach ($lines as $line) { | |
list($hash_suffix, $count) = explode(':', trim($line)); | |
if ($hash_suffix === $suffix) { | |
return true; | |
} | |
} | |
return false; | |
} | |
} | |
new PasswordPolicyEnforcement(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment