200 ] );
}
wp_safe_redirect( home_url() );
exit;
}
// Redirect unauthenticated wp-admin requests to home
if ( is_admin() && ! is_user_logged_in() && ! defined( 'DOING_AJAX' ) ) {
wp_safe_redirect( home_url() );
exit;
}
}
add_filter( 'login_url', 'aio_custom_login_url', 10, 3 );
function aio_custom_login_url( $login_url, $redirect, $force_reauth ) {
global $aio_hide_login_url;
$login_url = home_url( $aio_hide_login_url );
if ( ! empty( $redirect ) ) {
$login_url = add_query_arg( 'redirect_to', rawurlencode( $redirect ), $login_url );
}
if ( $force_reauth ) {
$login_url = add_query_arg( 'reauth', '1', $login_url );
}
return $login_url;
}
add_filter( 'site_url', 'aio_custom_site_url_login', 10, 4 );
function aio_custom_site_url_login( $url, $path, $scheme, $blog_id ) {
global $aio_hide_login_url;
if ( 'wp-login.php' === $path || strpos( (string) $path, 'wp-login.php?' ) === 0 ) {
return home_url( $aio_hide_login_url . substr( (string) $path, 12 ) );
}
return $url;
}
add_filter( 'network_site_url', 'aio_custom_network_site_url_login', 10, 3 );
function aio_custom_network_site_url_login( $url, $path, $scheme ) {
global $aio_hide_login_url;
if ( 'wp-login.php' === $path || strpos( (string) $path, 'wp-login.php?' ) === 0 ) {
return home_url( $aio_hide_login_url . substr( (string) $path, 12 ) );
}
return $url;
}
}
/* ==========================================================================
2. ANTI-SPAM (CAPTCHA: Turnstile, Google reCAPTCHA v2 & reCAPTCHA v3)
========================================================================== */
$aio_captcha_provider = aio_get_option( 'optCaptchaProvider', 'turnstile' );
$aio_captcha_site = trim( (string) aio_get_option( 'optCaptchaSiteKey', '' ) );
$aio_captcha_secret_raw = aio_get_option( 'optCaptchaSecretKey', '' );
$aio_captcha_secret = ! empty( $aio_captcha_secret_raw ) ? AIO_Crypto::decrypt( $aio_captcha_secret_raw ) : '';
$aio_captcha_v3_threshold = max( 0.1, min( 0.9, (float) aio_get_option( 'optRecaptchaV3Threshold', 0.5 ) ) );
$aio_captcha_login = ! empty( aio_get_option( 'optCaptchaForLogin' ) );
$aio_captcha_cf7 = ! empty( aio_get_option( 'optCaptchaForCF7' ) );
$aio_captcha_woo = ! empty( aio_get_option( 'optCaptchaForWoo' ) );
if ( ! empty( $aio_captcha_site ) && ! empty( $aio_captcha_secret ) ) {
function aio_render_captcha_html() {
global $aio_captcha_provider, $aio_captcha_site;
static $script_printed = false;
if ( 'turnstile' === $aio_captcha_provider ) {
if ( ! $script_printed ) {
$script_printed = true;
// phpcs:ignore PluginCheck.CodeAnalysis.Offloading.OffloadedContent, WordPress.WP.EnqueuedResources.NonEnqueuedScript
echo '';
}
echo '
';
} elseif ( 'recaptcha_v3' === $aio_captcha_provider ) {
echo '';
if ( ! $script_printed ) {
$script_printed = true;
$api_src = 'https://www.google.com/recaptcha/api.js?render=' . rawurlencode( $aio_captcha_site );
// phpcs:ignore PluginCheck.CodeAnalysis.Offloading.OffloadedContent, WordPress.WP.EnqueuedResources.NonEnqueuedScript
echo '';
?>
';
}
echo '';
}
}
function aio_get_captcha_field_name() {
global $aio_captcha_provider;
return ( 'turnstile' === $aio_captcha_provider ) ? 'cf-turnstile-response' : 'g-recaptcha-response';
}
function aio_verify_captcha_response( $response_field = '' ) {
global $aio_captcha_provider, $aio_captcha_secret, $aio_captcha_v3_threshold;
static $verified_in_request = null;
if ( true === $verified_in_request ) {
return true;
}
if ( '' === $response_field ) {
$response_field = aio_get_captcha_field_name();
}
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$response = isset( $_POST[ $response_field ] ) ? sanitize_text_field( wp_unslash( $_POST[ $response_field ] ) ) : '';
if ( empty( $response ) ) {
return false;
}
$verify_url = ( 'turnstile' === $aio_captcha_provider )
? 'https://challenges.cloudflare.com/turnstile/v0/siteverify'
: 'https://www.google.com/recaptcha/api/siteverify';
$api_response = wp_safe_remote_post( $verify_url, [
'timeout' => 10,
'body' => [
'secret' => $aio_captcha_secret,
'response' => $response,
'remoteip' => aio_get_client_ip(),
],
] );
if ( is_wp_error( $api_response ) ) {
return false;
}
$result = json_decode( wp_remote_retrieve_body( $api_response ) );
if ( ! isset( $result->success ) || true !== $result->success ) {
return false;
}
if ( 'recaptcha_v3' === $aio_captcha_provider && isset( $result->score ) ) {
if ( (float) $result->score < (float) $aio_captcha_v3_threshold ) {
return false;
}
}
$verified_in_request = true;
return true;
}
if ( $aio_captcha_login ) {
add_action( 'login_form', 'aio_render_captcha_html' );
add_action( 'register_form', 'aio_render_captcha_html' );
add_action( 'lostpassword_form', 'aio_render_captcha_html' );
add_filter( 'wp_authenticate_user', 'aio_verify_captcha_login', 10, 2 );
function aio_verify_captcha_login( $user, $password ) {
// Only verify on standard wp-login.php submission here unless WooCommerce CAPTCHA is also enabled
global $aio_captcha_woo;
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$is_wp_login = isset( $_POST['log'] );
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$is_wc_login = ! empty( $aio_captcha_woo ) && isset( $_POST['woocommerce-login-nonce'] );
if ( ! $is_wp_login && ! $is_wc_login ) {
return $user;
}
if ( ! aio_verify_captcha_response() ) {
return new WP_Error( 'captcha_error', '' . esc_html__( 'Error:', 'aio-tools' ) . ' ' . esc_html__( 'CAPTCHA verification failed.', 'aio-tools' ) );
}
return $user;
}
add_filter( 'registration_errors', 'aio_verify_captcha_register', 10, 3 );
function aio_verify_captcha_register( $errors, $sanitized_user_login, $user_email ) {
if ( ! aio_verify_captcha_response() ) {
$errors->add( 'captcha_error', '' . esc_html__( 'Error:', 'aio-tools' ) . ' ' . esc_html__( 'CAPTCHA verification failed.', 'aio-tools' ) );
}
return $errors;
}
add_action( 'lostpassword_post', 'aio_verify_captcha_lostpassword', 10, 1 );
function aio_verify_captcha_lostpassword( $errors ) {
// Skip WooCommerce lost password unless WooCommerce CAPTCHA is enabled
global $aio_captcha_woo;
// phpcs:ignore WordPress.Security.NonceVerification.Missing
if ( isset( $_POST['wc_reset_password'] ) && empty( $aio_captcha_woo ) ) {
return;
}
if ( ! aio_verify_captcha_response() ) {
if ( is_wp_error( $errors ) ) {
$errors->add( 'captcha_error', '' . esc_html__( 'Error:', 'aio-tools' ) . ' ' . esc_html__( 'CAPTCHA verification failed.', 'aio-tools' ) );
} else {
wp_die( esc_html__( 'CAPTCHA verification failed.', 'aio-tools' ), esc_html__( 'Error', 'aio-tools' ), [ 'response' => 403, 'back_link' => true ] );
}
}
}
}
// WooCommerce Login, Registration & Guest Checkout CAPTCHA Protection
if ( $aio_captcha_woo ) {
add_action( 'woocommerce_login_form', 'aio_render_captcha_html' );
add_action( 'woocommerce_register_form', 'aio_render_captcha_html' );
add_action( 'woocommerce_lostpassword_form', 'aio_render_captcha_html' );
if ( ! $aio_captcha_login ) {
add_filter( 'wp_authenticate_user', 'aio_verify_captcha_woo_login_only', 10, 2 );
function aio_verify_captcha_woo_login_only( $user, $password ) {
// phpcs:ignore WordPress.Security.NonceVerification.Missing
if ( ! isset( $_POST['woocommerce-login-nonce'] ) ) {
return $user;
}
if ( ! aio_verify_captcha_response() ) {
return new WP_Error( 'captcha_error', '' . esc_html__( 'Error:', 'aio-tools' ) . ' ' . esc_html__( 'CAPTCHA verification failed.', 'aio-tools' ) );
}
return $user;
}
}
add_filter( 'woocommerce_process_registration_errors', 'aio_verify_captcha_woo_register', 10, 4 );
function aio_verify_captcha_woo_register( $validation_error, $username, $password, $email ) {
if ( ! aio_verify_captcha_response() ) {
return new WP_Error( 'captcha_error', esc_html__( 'CAPTCHA verification failed.', 'aio-tools' ) );
}
return $validation_error;
}
add_action( 'woocommerce_review_order_before_submit', 'aio_render_captcha_woo_checkout' );
function aio_render_captcha_woo_checkout() {
if ( ! is_user_logged_in() ) {
aio_render_captcha_html();
}
}
add_action( 'woocommerce_after_checkout_validation', 'aio_verify_captcha_woo_checkout', 10, 2 );
function aio_verify_captcha_woo_checkout( $data, $errors ) {
if ( is_user_logged_in() ) {
return;
}
if ( ! aio_verify_captcha_response() ) {
$errors->add( 'captcha_error', esc_html__( 'CAPTCHA verification failed. Please complete the security check.', 'aio-tools' ) );
}
}
}
if ( $aio_captcha_cf7 && defined( 'WPCF7_VERSION' ) ) {
add_filter( 'wpcf7_form_elements', 'aio_cf7_add_captcha' );
function aio_cf7_add_captcha( $elements ) {
ob_start();
aio_render_captcha_html();
$captcha_html = ob_get_clean();
if ( preg_match( '/(<(?:input|button)[^>]+type=["\']submit["\'][^>]*>)/i', $elements ) ) {
$elements = preg_replace( '/(<(?:input|button)[^>]+type=["\']submit["\'][^>]*>)/i', $captcha_html . '$1', $elements, 1 );
} else {
$elements .= $captcha_html;
}
return $elements;
}
add_filter( 'wpcf7_validate', 'aio_cf7_verify_captcha', 20, 2 );
function aio_cf7_verify_captcha( $result, $tags ) {
if ( ! aio_verify_captcha_response() ) {
$result->invalidate( new WPCF7_FormTag( [ 'name' => 'captcha', 'type' => 'captcha', 'basetype' => 'captcha' ] ), esc_html__( 'Please verify CAPTCHA.', 'aio-tools' ) );
}
return $result;
}
}
}
/* ==========================================================================
3. TWO-FACTOR AUTHENTICATION (Post-Password 2-Step Challenge + Backup Codes)
========================================================================== */
if ( ! function_exists( 'aio_render_2fa_interstitial_page' ) ) {
function aio_render_2fa_interstitial_page( $challenge_token, $error_msg = '' ) {
if ( ! defined( 'DONOTCACHEPAGE' ) ) {
define( 'DONOTCACHEPAGE', true );
}
nocache_headers();
header( 'Content-Type: text/html; charset=UTF-8' );
?>
>
(int) $user->ID,
'remember' => (bool) $remember,
'redirect_to' => ! empty( $redirect_to ) ? esc_url_raw( $redirect_to ) : admin_url(),
'ip' => aio_get_client_ip(),
'attempts' => 0,
],
5 * MINUTE_IN_SECONDS
);
aio_render_2fa_interstitial_page( $challenge_token );
}
}
add_filter( 'authenticate', 'aio_intercept_2fa_after_password', 50, 3 );
function aio_intercept_2fa_after_password( $user, $username, $password ) {
if ( ! ( $user instanceof WP_User ) ) {
return $user;
}
$is_2fa_enabled = get_user_meta( $user->ID, '_aio_2fa_enabled', true );
if ( ! $is_2fa_enabled ) {
return $user;
}
$raw_secret = get_user_meta( $user->ID, '_aio_2fa_secret', true );
$secret = ! empty( $raw_secret ) ? AIO_Crypto::decrypt( (string) $raw_secret ) : '';
if ( empty( $secret ) ) {
return $user;
}
// Block non-interactive XML-RPC / REST password logins when 2FA is active
if ( ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
return new WP_Error( 'aio_2fa_required', __( 'Two-Factor Authentication is enabled for this account.', 'aio-tools' ) );
}
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$remember = ! empty( $_POST['rememberme'] );
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$redirect_to = isset( $_REQUEST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ) : admin_url();
aio_start_2fa_challenge_for_user( $user, $redirect_to, $remember );
return new WP_Error( 'aio_2fa_pending', __( '2FA verification required.', 'aio-tools' ) );
}
add_action( 'init', 'aio_handle_2fa_challenge_submission', 1 );
function aio_handle_2fa_challenge_submission() {
if ( ! isset( $_POST['aio_2fa_action'] ) || 'verify' !== $_POST['aio_2fa_action'] ) {
return;
}
if ( ! isset( $_POST['aio_2fa_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['aio_2fa_nonce'] ) ), 'aio_2fa_challenge_verify' ) ) {
wp_die( esc_html__( 'Security session expired. Please log in again.', 'aio-tools' ), esc_html__( 'Error', 'aio-tools' ), [ 'response' => 403, 'back_link' => true ] );
}
$challenge_token = isset( $_POST['aio_2fa_token'] ) ? sanitize_text_field( wp_unslash( $_POST['aio_2fa_token'] ) ) : '';
$code = isset( $_POST['aio_2fa_code'] ) ? trim( sanitize_text_field( wp_unslash( $_POST['aio_2fa_code'] ) ) ) : '';
$transient_key = 'aio_2fa_challenge_' . hash( 'sha256', $challenge_token );
$challenge = get_transient( $transient_key );
if ( empty( $challenge_token ) || ! is_array( $challenge ) || empty( $challenge['user_id'] ) ) {
wp_safe_redirect( wp_login_url() );
exit;
}
// Bind 2FA challenge session strictly to the originating client IP
if ( ! empty( $challenge['ip'] ) && $challenge['ip'] !== aio_get_client_ip() ) {
delete_transient( $transient_key );
wp_safe_redirect( wp_login_url() );
exit;
}
$user = get_user_by( 'id', (int) $challenge['user_id'] );
if ( ! $user ) {
delete_transient( $transient_key );
wp_safe_redirect( wp_login_url() );
exit;
}
$raw_secret = get_user_meta( $user->ID, '_aio_2fa_secret', true );
$secret = ! empty( $raw_secret ) ? AIO_Crypto::decrypt( (string) $raw_secret ) : '';
$verified = false;
$matched_slice = null;
// 1. Check 6-digit TOTP with time-slice replay protection
if ( AIO_TOTP::verify_code( $secret, $code, 1, $matched_slice ) ) {
$last_slice = (int) get_user_meta( $user->ID, '_aio_2fa_last_slice', true );
if ( $matched_slice > $last_slice ) {
update_user_meta( $user->ID, '_aio_2fa_last_slice', (int) $matched_slice );
$verified = true;
} else {
aio_render_2fa_interstitial_page( $challenge_token, __( 'This 2FA code was already used. Please wait for the next 6-digit code.', 'aio-tools' ) );
}
} elseif ( AIO_TOTP::verify_and_consume_backup_code( $user->ID, $code ) ) {
// 2. Fallback: One-time 8-character recovery code
$verified = true;
}
if ( ! $verified ) {
$challenge['attempts'] = (int) ( $challenge['attempts'] ?? 0 ) + 1;
if ( $challenge['attempts'] >= 5 ) {
delete_transient( $transient_key );
do_action( 'wp_login_failed', $user->user_login );
wp_safe_redirect( wp_login_url() );
exit;
}
set_transient( $transient_key, $challenge, 5 * MINUTE_IN_SECONDS );
aio_render_2fa_interstitial_page( $challenge_token, __( 'Invalid authentication or recovery code. Please try again.', 'aio-tools' ) );
}
delete_transient( $transient_key );
wp_clear_auth_cookie();
wp_set_current_user( $user->ID );
wp_set_auth_cookie( $user->ID, ! empty( $challenge['remember'] ) );
do_action( 'wp_login', $user->user_login, $user );
wp_safe_redirect( ! empty( $challenge['redirect_to'] ) ? $challenge['redirect_to'] : admin_url() );
exit;
}
/* ==========================================================================
4. IP LOCKOUT ENFORCEMENT & LIMIT LOGIN ATTEMPTS (+ IP Whitelist)
========================================================================== */
add_filter( 'authenticate', 'aio_enforce_ip_lockout_and_limits', 30, 3 );
function aio_enforce_ip_lockout_and_limits( $user, $username, $password ) {
if ( empty( $username ) && empty( $password ) ) {
return $user;
}
$ip = aio_get_client_ip();
$whitelist = (string) aio_get_option( 'txtLoginWhitelist', '' );
if ( '' !== $whitelist && function_exists( 'aio_is_ip_in_list' ) && aio_is_ip_in_list( $ip, $whitelist ) ) {
return $user;
}
$lockout_key = 'aio_lockout_' . md5( $ip );
// 1. Check fast transient lockout
if ( ! empty( aio_get_option( 'optEnableLimitLogin' ) ) && get_transient( $lockout_key ) ) {
$duration = max( 1, (int) aio_get_option( 'optLimitLoginDuration', 15 ) );
/* translators: %d: lockout duration in minutes */
return new WP_Error( 'too_many_retries', sprintf( __( 'ERROR: Too many failed login attempts. Please try again in %d minutes.', 'aio-tools' ), $duration ) );
}
// 2. Enforce persistent DB lockouts & manual IP bans (survives Redis/Object Cache flush)
global $wpdb;
$lockouts_table = $wpdb->prefix . 'aio_lockouts';
$now_mysql = current_time( 'mysql' );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$expires_at = $wpdb->get_var(
$wpdb->prepare(
"SELECT expires_at FROM {$lockouts_table} WHERE ip_address = %s AND expires_at > %s ORDER BY expires_at DESC LIMIT 1",
$ip,
$now_mysql
)
);
if ( ! empty( $expires_at ) ) {
if ( strpos( (string) $expires_at, '2099-' ) === 0 ) {
return new WP_Error( 'aio_ip_banned', __( 'ERROR: Your IP address has been blocked by the site administrator.', 'aio-tools' ) );
}
$remaining_sec = max( 60, strtotime( $expires_at ) - current_time( 'timestamp' ) );
$remaining_min = max( 1, (int) ceil( $remaining_sec / 60 ) );
set_transient( $lockout_key, true, $remaining_sec );
/* translators: %d: remaining lockout duration in minutes */
return new WP_Error( 'too_many_retries', sprintf( __( 'ERROR: Too many failed login attempts. Please try again in %d minutes.', 'aio-tools' ), $remaining_min ) );
}
return $user;
}
if ( ! empty( aio_get_option( 'optEnableLimitLogin' ) ) ) {
add_action( 'wp_login_failed', 'aio_login_failed' );
function aio_login_failed( $username ) {
$ip = aio_get_client_ip();
$whitelist = (string) aio_get_option( 'txtLoginWhitelist', '' );
if ( '' !== $whitelist && function_exists( 'aio_is_ip_in_list' ) && aio_is_ip_in_list( $ip, $whitelist ) ) {
return;
}
$attempts_key = 'aio_login_attempts_' . md5( $ip );
$lockout_key = 'aio_lockout_' . md5( $ip );
$max_attempts = max( 1, (int) aio_get_option( 'optLimitLoginMax', 3 ) );
$duration = max( 1, (int) aio_get_option( 'optLimitLoginDuration', 15 ) );
$attempts = (int) get_transient( $attempts_key ) + 1;
if ( $attempts >= $max_attempts ) {
set_transient( $lockout_key, true, $duration * MINUTE_IN_SECONDS );
delete_transient( $attempts_key );
global $wpdb;
$lockouts_table = $wpdb->prefix . 'aio_lockouts';
// Prune expired lockouts to keep table clean
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->query( $wpdb->prepare( "DELETE FROM {$lockouts_table} WHERE expires_at < %s", current_time( 'mysql' ) ) );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$wpdb->insert(
$lockouts_table,
[
'ip_address' => substr( $ip, 0, 45 ),
'reason' => 'Limit Login Attempts',
'blocked_at' => current_time( 'mysql' ),
'expires_at' => wp_date( 'Y-m-d H:i:s', time() + ( $duration * MINUTE_IN_SECONDS ) ),
],
[ '%s', '%s', '%s', '%s' ]
);
} else {
set_transient( $attempts_key, $attempts, 12 * HOUR_IN_SECONDS );
}
}
add_action( 'wp_login', 'aio_login_success', 10, 2 );
function aio_login_success( $user_login, $user ) {
$ip = aio_get_client_ip();
delete_transient( 'aio_login_attempts_' . md5( $ip ) );
}
}
/* ==========================================================================
5. AUDIT LOG (System Activity + Automatic 500-Row Retention)
========================================================================== */
if ( ! empty( aio_get_option( 'optEnableAuditLog' ) ) ) {
class AIO_Audit_Log {
public function __construct() {
add_action( 'wp_login', [ $this, 'log_login' ], 10, 2 );
add_action( 'wp_login_failed', [ $this, 'log_login_failed' ] );
add_action( 'deleted_post', [ $this, 'log_deleted_post' ] );
add_action( 'activated_plugin', [ $this, 'log_activated_plugin' ] );
add_action( 'deactivated_plugin', [ $this, 'log_deactivated_plugin' ] );
}
private function insert_log( $event, $details, $user_id = null, $user_login = null ) {
global $wpdb;
$table_name = $wpdb->prefix . 'aio_audit_logs';
if ( null === $user_id ) {
$user = wp_get_current_user();
$user_id = $user->ID;
$user_login = $user->user_login;
}
if ( empty( $user_login ) ) {
$user_login = 'System/Guest';
}
$ip = aio_get_client_ip();
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$wpdb->insert(
$table_name,
[
'time' => current_time( 'mysql' ),
'user_id' => (int) $user_id,
'user_login' => substr( sanitize_text_field( (string) $user_login ), 0, 60 ),
'event' => substr( sanitize_text_field( (string) $event ), 0, 100 ),
'details' => sanitize_text_field( (string) $details ),
'ip_address' => substr( $ip, 0, 45 ),
],
[ '%s', '%d', '%s', '%s', '%s', '%s' ]
);
// Keep only the latest 500 audit events to prevent unbounded DB bloat
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$cutoff_id = $wpdb->get_var( "SELECT id FROM {$table_name} ORDER BY id DESC LIMIT 1 OFFSET 500" );
if ( $cutoff_id ) {
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->query( $wpdb->prepare( "DELETE FROM {$table_name} WHERE id <= %d", (int) $cutoff_id ) );
}
}
public function log_login( $user_login, $user ) {
$this->insert_log( 'User Logged In', 'User logged in successfully.', $user->ID, $user_login );
}
public function log_login_failed( $username ) {
$clean_user = sanitize_user( (string) $username );
$this->insert_log( 'Failed Login', "Failed login attempt for username: {$clean_user}", 0, $clean_user ?: 'Unknown' );
}
public function log_deleted_post( $post_id ) {
$post = get_post( $post_id );
if ( $post && 'revision' !== $post->post_type ) {
$this->insert_log( 'Post Deleted', "Deleted post #{$post_id}: {$post->post_title} ({$post->post_type})" );
}
}
public function log_activated_plugin( $plugin ) {
$this->insert_log( 'Plugin Activated', "Activated plugin: {$plugin}" );
}
public function log_deactivated_plugin( $plugin ) {
$this->insert_log( 'Plugin Deactivated', "Deactivated plugin: {$plugin}" );
}
}
new AIO_Audit_Log();
}
/* ==========================================================================
6. SYSTEM HARDENING (Disable XML-RPC & Disable File Editing)
========================================================================== */
if ( ! empty( aio_get_option( 'opt-perf-disable-xmlrpc' ) ) ) {
add_filter( 'xmlrpc_enabled', '__return_false' );
add_filter( 'xmlrpc_methods', '__return_empty_array' );
remove_action( 'wp_head', 'rsd_link' );
}
if ( ! empty( aio_get_option( 'optEnableDisableFileEdit' ) ) ) {
if ( ! defined( 'DISALLOW_FILE_EDIT' ) ) {
define( 'DISALLOW_FILE_EDIT', true );
}
add_filter( 'map_meta_cap', 'aio_disable_file_edit_caps', 10, 2 );
function aio_disable_file_edit_caps( $caps, $cap ) {
if ( in_array( $cap, [ 'edit_files', 'edit_plugins', 'edit_themes' ], true ) ) {
$caps[] = 'do_not_allow';
}
return $caps;
}
}
/* ==========================================================================
7. FILE INTEGRITY MONITOR (+ Scheduled Daily Background Scan & Alerts)
========================================================================== */
if ( ! function_exists( 'aio_scan_core_files' ) ) {
function aio_scan_core_files() {
global $wp_version;
$locale = get_locale();
$api_url = 'https://api.wordpress.org/core/checksums/1.0/?version=' . rawurlencode( $wp_version ) . '&locale=' . rawurlencode( $locale );
$response = wp_safe_remote_get( $api_url, [ 'timeout' => 15 ] );
if ( is_wp_error( $response ) ) {
return [ 'success' => false, 'message' => __( 'Cannot connect to WordPress API.', 'aio-tools' ) ];
}
$data = json_decode( wp_remote_retrieve_body( $response ), true );
if ( empty( $data['checksums'] ) || ! is_array( $data['checksums'] ) ) {
$api_url = 'https://api.wordpress.org/core/checksums/1.0/?version=' . rawurlencode( $wp_version ) . '&locale=en_US';
$response = wp_safe_remote_get( $api_url, [ 'timeout' => 15 ] );
$data = json_decode( wp_remote_retrieve_body( $response ), true );
}
if ( empty( $data['checksums'] ) || ! is_array( $data['checksums'] ) ) {
return [ 'success' => false, 'message' => __( 'No checksum data found for this version.', 'aio-tools' ) ];
}
$modified_files = [];
foreach ( $data['checksums'] as $file => $expected_md5 ) {
if ( strpos( $file, 'wp-content/' ) === 0 || 'wp-config-sample.php' === $file ) {
continue;
}
$local_file = ABSPATH . $file;
if ( ! file_exists( $local_file ) ) {
$modified_files[] = [ 'file' => $file, 'status' => 'missing' ];
continue;
}
if ( md5_file( $local_file ) !== $expected_md5 ) {
$modified_files[] = [ 'file' => $file, 'status' => 'modified' ];
}
}
update_option(
'aio_core_integrity_issues',
[
'scanned_at' => current_time( 'mysql' ),
'count' => count( $modified_files ),
'issues' => array_slice( $modified_files, 0, 100 ),
],
false
);
return [ 'success' => true, 'modified' => $modified_files ];
}
}
add_action( 'init', 'aio_sync_daily_integrity_cron' );
function aio_sync_daily_integrity_cron() {
$auto_scan = ! empty( aio_get_option( 'optEnableAutoIntegrityScan' ) );
$scheduled = wp_next_scheduled( 'aio_daily_integrity_scan' );
if ( $auto_scan && ! $scheduled ) {
wp_schedule_event( time() + HOUR_IN_SECONDS, 'daily', 'aio_daily_integrity_scan' );
} elseif ( ! $auto_scan && $scheduled ) {
wp_clear_scheduled_hook( 'aio_daily_integrity_scan' );
}
}
add_action( 'aio_daily_integrity_scan', 'aio_run_scheduled_integrity_scan' );
function aio_run_scheduled_integrity_scan() {
$result = aio_scan_core_files();
if ( empty( $result['success'] ) || empty( $result['modified'] ) ) {
delete_transient( 'aio_integrity_last_alert_hash' );
return;
}
$issues_hash = md5( wp_json_encode( $result['modified'] ) );
if ( get_transient( 'aio_integrity_last_alert_hash' ) === $issues_hash ) {
return;
}
set_transient( 'aio_integrity_last_alert_hash', $issues_hash, DAY_IN_SECONDS );
if ( ! class_exists( 'AIO_Messenger' ) && file_exists( AIO_MODULES_PATH . 'messenger/public-messenger.php' ) ) {
require_once AIO_MODULES_PATH . 'messenger/public-messenger.php';
}
if ( class_exists( 'AIO_Messenger' ) && method_exists( 'AIO_Messenger', 'send_instant_alert' ) ) {
$sample_files = array_slice( array_column( $result['modified'], 'file' ), 0, 5 );
$msg = sprintf(
/* translators: 1: site URL, 2: count of modified/missing core files, 3: list of sample files */
__( "Site: %1\$s\nModified or missing WordPress core files detected: %2\$d\nFiles: %3\$s", 'aio-tools' ),
esc_html( home_url() ),
count( $result['modified'] ),
esc_html( implode( ', ', $sample_files ) )
);
AIO_Messenger::send_instant_alert( __( '🚨 Core Integrity Alert', 'aio-tools' ), $msg, false );
}
}
/* ==========================================================================
8. UPLOADS DIRECTORY PROTECTION (.htaccess with non-destructive markers)
========================================================================== */
if ( ! function_exists( 'aio_sync_uploads_protection_htaccess' ) ) {
function aio_sync_uploads_protection_htaccess( $enabled ) {
if ( ! function_exists( 'insert_with_markers' ) ) {
require_once ABSPATH . 'wp-admin/includes/misc.php';
}
$upload_dir = wp_upload_dir();
if ( empty( $upload_dir['basedir'] ) || ! is_dir( $upload_dir['basedir'] ) ) {
return;
}
$htaccess_file = trailingslashit( $upload_dir['basedir'] ) . '.htaccess';
// Clean up legacy un-markered file if created by v3.0.8
if ( file_exists( $htaccess_file ) ) {
$existing = @file_get_contents( $htaccess_file );
if ( is_string( $existing ) && trim( $existing ) === "\ndeny from all\n" ) {
@unlink( $htaccess_file );
}
}
if ( $enabled ) {
$rules = [
'',
' ',
' Require all denied',
' ',
' ',
' Order allow,deny',
' Deny from all',
' ',
'',
];
@insert_with_markers( $htaccess_file, 'AIO Tools Upload Protection', $rules );
} elseif ( file_exists( $htaccess_file ) ) {
@insert_with_markers( $htaccess_file, 'AIO Tools Upload Protection', [] );
}
}
}
add_action( 'aio_tools_settings_updated', function ( $new_settings ) {
aio_sync_uploads_protection_htaccess( ! empty( $new_settings['optEnableUploadProtection'] ) );
}, 10, 1 );
add_action( 'admin_init', function () {
if ( ! empty( aio_get_option( 'optEnableUploadProtection' ) ) ) {
$upload_dir = wp_upload_dir();
$htaccess_file = ! empty( $upload_dir['basedir'] ) ? trailingslashit( $upload_dir['basedir'] ) . '.htaccess' : '';
if ( $htaccess_file && ( ! file_exists( $htaccess_file ) || strpos( (string) @file_get_contents( $htaccess_file ), 'BEGIN AIO Tools Upload Protection' ) === false ) ) {
aio_sync_uploads_protection_htaccess( true );
}
}
} );
/* ==========================================================================
9. WEB APPLICATION FIREWALL (Lightweight Application-Layer WAF)
========================================================================== */
if ( ! empty( aio_get_option( 'optEnableWAF' ) ) ) {
add_action( 'plugins_loaded', 'aio_waf_intercept', 0 );
function aio_waf_intercept() {
if ( is_user_logged_in() && current_user_can( 'edit_posts' ) ) {
return;
}
$patterns = [
'/\bunion\s+(?:all\s+)?select\b/i',
'/\binformation_schema\b/i',
'/(?:benchmark|sleep)\s*\(\s*\d+/i',
'/<\s*script\b/i',
'/javascript\s*:/i',
'/\bon(?:error|load|mouseover|focus)\s*=/i',
'/(?:base64_decode|eval|gzinflate)\s*\(/i',
'/(?:\.\.\/|\.\.\\\\){2,}/',
'/(?:\/etc\/passwd|wp-config\.php)/i',
'/(?:php|expect|data):\/\//i',
];
$inspect = function ( $input ) use ( &$inspect, $patterns ) {
if ( is_array( $input ) ) {
foreach ( $input as $v ) {
$inspect( $v );
}
return;
}
$decoded = rawurldecode( rawurldecode( (string) $input ) );
foreach ( $patterns as $regex ) {
if ( preg_match( $regex, $decoded ) ) {
status_header( 403 );
nocache_headers();
die( 'AIO Tools WAF: Malicious request blocked.' );
}
}
};
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( ! empty( $_GET ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$inspect( $_GET );
}
// phpcs:ignore WordPress.Security.NonceVerification.Missing
if ( ! empty( $_POST ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$inspect( $_POST );
}
if ( ! empty( $_COOKIE ) ) {
foreach ( $_COOKIE as $c_key => $c_val ) {
if ( strpos( (string) $c_key, 'wordpress_' ) === 0 || strpos( (string) $c_key, 'wp-settings-' ) === 0 ) {
continue;
}
$inspect( $c_val );
}
}
}
}