feat: protect WP login, password reset, and fix Elementor AJAX bypass
- Move elementor_pro/forms/validation hook before is_admin() early return (same AJAX bypass bug as CF7 — Elementor submits to admin-ajax.php) - Add login_head + login_footer hooks so CSS/JS HMAC token loads on wp-login.php (wp_head/footer do not fire on that page) - Add lostpassword_form + woocommerce_lostpassword_form injection hooks - Add authenticate filter (validate_wp_login) for WP native login, guarded to skip WC login and non-form auth calls - Add lostpassword_post action (validate_lost_password) for password reset, covering both WP and WC My Account lost-password forms - Exclude woocommerce-lost-password-nonce from generic catch-all to avoid double-processing WC lost-password submissions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1195,9 +1195,10 @@ class SmartHoneypotAntiSpam {
|
||||
|
||||
/* ── Init ──────────────────────────────────────────────────────── */
|
||||
public function init() {
|
||||
// CF7 submits via admin-ajax.php where is_admin()=true, so hook here
|
||||
// before the early return so spam validation still runs.
|
||||
// CF7 and Elementor submit via admin-ajax.php where is_admin()=true,
|
||||
// so register their validation hooks before the early return.
|
||||
add_filter('wpcf7_spam', [$this, 'validate_cf7_spam'], 10, 2);
|
||||
add_action('elementor_pro/forms/validation', [$this, 'validate_elementor_form'], 10, 2);
|
||||
|
||||
if (is_admin()) {
|
||||
add_action('admin_notices', [$this, 'activation_notice']);
|
||||
@@ -1213,6 +1214,8 @@ class SmartHoneypotAntiSpam {
|
||||
add_action('woocommerce_after_order_notes',[$this, 'echo_honeypot']);
|
||||
add_action('register_form', [$this, 'echo_honeypot']);
|
||||
add_action('login_form', [$this, 'echo_honeypot']);
|
||||
add_action('lostpassword_form', [$this, 'echo_honeypot']);
|
||||
add_action('woocommerce_lostpassword_form',[$this, 'echo_honeypot']);
|
||||
add_action('elementor_pro/forms/render_field', [$this, 'add_to_elementor_form'], 10, 2);
|
||||
add_action('elementor/widget/render_content', [$this, 'filter_elementor_widget'], 10, 2);
|
||||
add_filter('gform_form_tag', [$this, 'add_to_gravity_forms'], 10, 2);
|
||||
@@ -1224,13 +1227,16 @@ class SmartHoneypotAntiSpam {
|
||||
add_filter('woocommerce_process_login_errors', [$this, 'validate_wc_login'], 10, 3);
|
||||
add_action('woocommerce_after_checkout_validation', [$this, 'validate_wc_checkout'], 10, 2);
|
||||
add_filter('registration_errors', [$this, 'validate_wp_registration'], 10, 3);
|
||||
add_filter('authenticate', [$this, 'validate_wp_login'], 20, 3);
|
||||
add_action('lostpassword_post', [$this, 'validate_lost_password'], 10, 2);
|
||||
add_filter('preprocess_comment', [$this, 'validate_comment']);
|
||||
add_action('elementor_pro/forms/validation', [$this, 'validate_elementor_form'], 10, 2);
|
||||
add_action('template_redirect', [$this, 'validate_generic_post']);
|
||||
|
||||
// CSS & JS
|
||||
add_action('wp_head', [$this, 'print_css']);
|
||||
add_action('wp_footer', [$this, 'print_js'], 99);
|
||||
// CSS & JS (wp-login.php uses login_head/login_footer, not wp_head/footer)
|
||||
add_action('wp_head', [$this, 'print_css']);
|
||||
add_action('wp_footer', [$this, 'print_js'], 99);
|
||||
add_action('login_head', [$this, 'print_css']);
|
||||
add_action('login_footer', [$this, 'print_js'], 99);
|
||||
}
|
||||
|
||||
/* ── Honeypot HTML ─────────────────────────────────────────────── */
|
||||
@@ -1417,6 +1423,38 @@ class SmartHoneypotAntiSpam {
|
||||
return $errors;
|
||||
}
|
||||
|
||||
public function validate_wp_login($user, $username, $password) {
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
return $user;
|
||||
}
|
||||
// Only validate if our honeypot was injected (fields present in POST)
|
||||
if (!isset($_POST[$this->hp_name]) && !isset($_POST[$this->token_name])) {
|
||||
return $user;
|
||||
}
|
||||
// WooCommerce login has its own validator
|
||||
if (isset($_POST['woocommerce-login-nonce'])) {
|
||||
return $user;
|
||||
}
|
||||
$this->current_form_type = 'WP Login';
|
||||
if (!$this->check_submission(true)) {
|
||||
return new \WP_Error(
|
||||
'honeypot_spam',
|
||||
__('<strong>Error</strong>: Login blocked. Enable JavaScript and try again.', 'smart-honeypot')
|
||||
);
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function validate_lost_password($errors, $user_data) {
|
||||
$this->current_form_type = 'WP Password Reset';
|
||||
if (!$this->check_submission(true)) {
|
||||
$errors->add(
|
||||
'honeypot_spam',
|
||||
__('<strong>Error</strong>: Request blocked. Enable JavaScript and try again.', 'smart-honeypot')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function validate_comment($commentdata) {
|
||||
if (is_user_logged_in() && current_user_can('moderate_comments')) {
|
||||
$this->clean_post_data();
|
||||
@@ -1462,7 +1500,8 @@ class SmartHoneypotAntiSpam {
|
||||
isset($_POST['woocommerce-process-checkout-nonce']) ||
|
||||
isset($_POST['comment_post_ID']) ||
|
||||
isset($_POST['_wpcf7']) || // CF7: handled by wpcf7_spam filter
|
||||
(isset($_POST['action']) && $_POST['action'] === 'elementor_pro_forms_send_form')
|
||||
(isset($_POST['action']) && $_POST['action'] === 'elementor_pro_forms_send_form') ||
|
||||
isset($_POST['woocommerce-lost-password-nonce']) // WC lost password: handled by lostpassword_post
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user