Skip to content

Instantly share code, notes, and snippets.

@chrisdavidmiles
Created June 12, 2025 23:58
Show Gist options
  • Save chrisdavidmiles/e256b3bf6fea420e3426f1fc133e9968 to your computer and use it in GitHub Desktop.
Save chrisdavidmiles/e256b3bf6fea420e3426f1fc133e9968 to your computer and use it in GitHub Desktop.
WordPress Plugin: Password Policy Enforcement
<?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