Advanced IP Blocker: Plugin initialization failed. The following required files are missing from the installation: ' . esc_html(implode(', ', $missing_files)) . '. Please reinstall the plugin to fix this issue.
';
});
}
return;
}
$this->site_scanner = new ADVAIPBL_Site_Scanner($this);
$this->fim_engine = new ADVAIPBL_FIM_Engine($this);
$this->community_manager = new ADVAIPBL_Community_Manager($this);
$this->admin_pages = new ADVAIPBL_Admin_Pages($this);
$this->action_handler = new ADVAIPBL_Action_Handler($this);
$this->ajax_handler = new ADVAIPBL_Ajax_Handler($this);
$this->settings_manager = new ADVAIPBL_Settings_Manager($this, $this->admin_pages);
$this->geolocation_manager = new ADVAIPBL_Geolocation_Manager($this);
$this->session_manager = new ADVAIPBL_User_Session_Manager($this, $this->geolocation_manager);
$this->asn_manager = new ADVAIPBL_Asn_Manager($this, $this->geolocation_manager);
$this->waf_manager = new ADVAIPBL_Waf_Manager($this);
$this->rate_limit_manager = new ADVAIPBL_Rate_Limiting_Manager($this);
$this->dashboard_manager = new ADVAIPBL_Dashboard_Manager($this, $this->session_manager);
$this->threat_score_manager = new ADVAIPBL_Threat_Score_Manager($this);
$this->bot_verifier = new ADVAIPBL_Bot_Verifier($this);
$this->rules_engine = new ADVAIPBL_Rules_Engine($this);
$this->rules_metrics = new ADVAIPBL_Rules_Metrics($this);
$this->challenge_metrics = new ADVAIPBL_Challenge_Metrics($this);
$this->fingerprint_manager = new ADVAIPBL_Fingerprint_Manager($this);
$this->abuseipdb_manager = new ADVAIPBL_AbuseIPDB_Manager($this);
$this->htaccess_manager = new ADVAIPBL_Htaccess_Manager($this);
$this->cloudflare_manager = new ADVAIPBL_Cloudflare_Manager($this);
$this->cdn_manager = new ADVAIPBL_CDN_Manager($this);
$this->reporter_manager = new ADVAIPBL_Reporter_Manager($this);
$this->security_headers_manager = new ADVAIPBL_Security_Headers($this);
$this->audit_logger = new ADVAIPBL_Audit_Logger($this);
$this->file_verifier = new ADVAIPBL_File_Verifier($this);
$this->js_challenge_manager = new ADVAIPBL_JS_Challenge($this);
$this->captcha_manager = new ADVAIPBL_Captcha_Manager($this);
$this->cache_manager = new ADVAIPBL_Cache_Manager();
$this->live_feed_manager = new ADVAIPBL_Live_Feed_Manager($this);
$this->cron_manager = new ADVAIPBL_Cron_Manager($this);
$this->notification_manager = new ADVAIPBL_Notification_Manager($this);
if (version_compare(PHP_VERSION, '8.1', '>=')) {
if (($this->options['geolocation_method'] ?? 'api') === 'local_db') {
$this->geoip_manager = new ADVAIPBL_GeoIP_Manager($this);
} else {
$this->geoip_manager = new stdClass();
}
if (is_admin() || (isset($GLOBALS['pagenow']) && $GLOBALS['pagenow'] === 'wp-login.php')) {
require_once plugin_dir_path(__FILE__) . 'class-advaipbl-2fa-manager.php';
if (class_exists('BaconQrCode\Renderer\ImageRenderer')) {
$this->tfa_manager = new ADVAIPBL_2FA_Manager($this);
} else {
$this->tfa_manager = new stdClass();
}
} else {
$this->tfa_manager = new stdClass();
}
} else {
$this->geoip_manager = new stdClass();
$this->tfa_manager = new stdClass();
}
add_filter('authenticate', [$this, 'check_login_rules'], 20, 3);
add_action('admin_init', [$this, 'check_database_update']);
$this->add_hooks();
if (is_admin()) {
add_action('admin_notices', [$this, 'display_admin_notice']);
}
}
public function initialize_backend_managers()
{
static $initialized = false;
if ($initialized) {
return;
}
require_once plugin_dir_path(__FILE__) . 'class-advaipbl-dashboard-manager.php';
$this->dashboard_manager = new ADVAIPBL_Dashboard_Manager($this, $this->session_manager);
if (version_compare(PHP_VERSION, '8.1', '>=')) {
require_once plugin_dir_path(__FILE__) . 'class-advaipbl-2fa-manager.php';
if (class_exists('BaconQrCode\Renderer\ImageRenderer')) {
$this->tfa_manager = new ADVAIPBL_2FA_Manager($this);
} else {
$this->tfa_manager = new stdClass();
}
} else {
$this->tfa_manager = new stdClass();
}
$initialized = true;
}
private function add_hooks()
{
add_action('init', [$this, 'check_database_version'], -9999);
add_action('init', [$this, 'detect_and_whitelist_loopback'], -10000);
add_action('init', [$this, 'auto_whitelist_admin_on_session'], -9998);
add_filter('user_has_cap', [$this, 'filter_advaipbl_capabilities'], 10, 4);
add_filter('http_request_args', [$this, 'inject_loopback_token'], 10, 2);
add_action('init', [$this, 'is_visitor_asn_whitelisted'], -1000);
add_action('init', [$this, 'verify_known_bots'], -999);
add_action('init', [$this->rules_engine, 'evaluate'], -998);
// Global and mandatory interception for all JS Challenges
add_action('init', [$this, 'check_for_under_attack_mode'], -996);
add_action('init', [$this->js_challenge_manager, 'verify_submission'], -997);
add_action('init', [$this->captcha_manager, 'verify_submission'], -997);
add_action('init', [$this, 'check_ip_with_abuseipdb'], 10);
add_action('init', [$this, 'block_xmlrpc_requests_if_disabled'], -5);
add_action('init', [$this, 'log_request_signature'], -2);
add_action('plugins_loaded', [$this, 'maybe_set_donotcachepage_constant'], 0);
add_action('init', [$this, 'check_for_bot_impersonator_block'], -1);
add_action('init', [$this, 'check_for_ghost_ips'], -1);
add_action('init', [$this, 'check_for_endpoint_lockdown'], -1);
add_action('init', [$this, 'check_for_malicious_signature'], -1);
add_action('init', [$this, 'check_for_geo_challenge'], -1);
add_action('init', [$this, 'run_all_block_checks'], 0);
add_action('init', [$this, 'log_wp_cron_execution'], 1);
add_action('init', [$this->rate_limit_manager, 'check_request_rate'], -1);
add_action('init', [$this, 'add_admin_ip_to_whitelist_on_first_run']);
add_filter('status_header', [$this, 'detect_http_error_status'], 10, 2);
add_action('advaipbl_community_report_event_v2', [$this, 'execute_community_report']);
add_action('advaipbl_update_community_list_event', [$this->community_manager, 'update_list']);
add_action('wp_ajax_advaipbl_run_deep_scan', [$this->ajax_handler, 'ajax_run_deep_scan']);
add_action('wp_ajax_advaipbl_check_server_reputation', [$this->ajax_handler, 'ajax_check_server_reputation']);
add_action('wp_ajax_advaipbl_clear_audit_log', [$this->ajax_handler, 'ajax_clear_audit_logs']);
add_action('wp_ajax_advaipbl_run_fim_scan', [$this->ajax_handler, 'ajax_run_fim_scan']);
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$action = isset($_GET['action']) ? sanitize_text_field(wp_unslash($_GET['action'])) : '';
if (is_admin() || (strpos($action, 'advaipbl_') === 0)) {
add_action('admin_init', [$this, 'initialize_backend_managers'], 0);
}
global $pagenow;
if ($pagenow === 'wp-login.php') {
$this->initialize_backend_managers();
}
add_action('init', [$this, 'prevent_author_enumeration_redirect'], 9);
add_filter('the_author_login', [$this, 'prevent_user_enumeration_via_feeds']);
add_filter('get_the_author_login', [$this, 'prevent_user_enumeration_via_feeds']);
add_action('wp_login_failed', [$this, 'registrar_intento_login_fallido']);
add_action('init', [ $this, 'handle_login_page_restriction' ], 5);
add_action('init', [ $this, 'handle_login_geo_restriction' ], 6);
add_action('wp_login', [$this, 'auto_whitelist_admin_on_login'], 10, 2);
add_filter('rest_endpoints', [ $this, 'disable_rest_api_user_endpoints' ]);
add_filter('oembed_response_data', [$this, 'prevent_user_enumeration_via_oembed'], 99, 4);
add_filter('authenticate', [$this, 'prevent_login_hinting'], 99, 1);
add_action('lostpassword_post', [$this, 'prevent_lostpassword_hinting']);
add_action('rest_api_init', [$this, 'register_live_feed_api_endpoint']);
add_action('rest_api_init', function () {
register_rest_route('advaipbl/v1', '/live-feed-nonce', [
'methods' => WP_REST_Server::READABLE,
'callback' => [$this, 'get_live_feed_nonce'],
'permission_callback' => '__return_true',
]);
});
add_shortcode('advaipbl_live_feed', [$this, 'render_live_feed_shortcode']);
$this->add_2fa_hooks();
if (! empty($this->options['show_admin_bar_menu']) && '1' === $this->options['show_admin_bar_menu']) {
add_action('admin_bar_menu', [ $this, 'add_admin_bar_menu' ], 999);
}
add_action('advaipbl_purge_old_logs_event', [$this, 'purge_old_logs']);
add_action('advaipbl_cloudflare_cleanup_event', [$this->cloudflare_manager, 'clear_all_aib_rules']);
add_action('advaipbl_send_telemetry_data_event', [$this, 'send_telemetry_data']);
add_action('advaipbl_update_geoip_db_event', [$this, 'execute_geoip_db_update']);
add_action('advaipbl_clear_expired_blocks_event', [$this, 'limpiar_ips_expiradas']);
add_action('advaipbl_cleanup_expired_cache_event', [$this, 'cleanup_expired_cache_entries']);
add_action('advaipbl_daily_fim_scan', [$this->file_verifier, 'scan_files']);
if (is_admin()) {
add_action('admin_notices', [$this, 'display_setup_wizard_notice']);
add_action('admin_init', [$this, 'maybe_redirect_to_wizard']);
add_action('admin_init', [$this, 'schedule_cron_jobs']);
add_action('admin_init', [$this, 'conditionally_remove_admin_notices']);
add_action('admin_init', [$this, 'update_option_autoload_states']);
add_action('admin_menu', [$this, 'admin_menu']);
add_action('admin_init', [$this->settings_manager, 'register_settings']);
add_action('admin_init', [$this->action_handler, 'handle_admin_actions']);
add_action('update_option_' . self::OPTION_SETTINGS, [$this, 'on_settings_update'], 10, 2);
add_action('update_option_' . self::OPTION_WAF_RULES, [$this, 'on_waf_rules_update'], 10, 2);
add_action('admin_post_advaipbl_refresh_spamhaus', [$this, 'handle_spamhaus_refresh_action']);
add_action('admin_notices', [$this, 'display_force_2fa_setup_notice']);
add_action('admin_notices', [$this, 'display_admin_notice']);
add_action('admin_notices', [$this, 'display_under_attack_notice']);
add_action('admin_head', function () {
?>
ajax_handler, 'ajax_test_outbound_connection']);
add_action('wp_ajax_advaipbl_add_ip_to_whitelist', [$this->ajax_handler, 'ajax_add_ip_to_whitelist']);
add_action('wp_ajax_advaipbl_verify_api_key', [$this->ajax_handler, 'ajax_verify_api_key']);
add_action('wp_ajax_advaipbl_get_free_api_key', [$this->ajax_handler, 'ajax_get_free_api_key']);
add_action('wp_ajax_advaipbl_update_geoip_db', [$this->ajax_handler, 'ajax_update_geoip_db']);
add_action('wp_ajax_advaipbl_get_dashboard_stats', [$this->ajax_handler, 'ajax_get_dashboard_stats']);
add_action('wp_ajax_advaipbl_export_settings_ajax', [ $this, 'handle_export_settings_ajax' ]);
add_action('wp_ajax_advaipbl_handle_telemetry_notice', [$this->ajax_handler, 'ajax_handle_telemetry_notice']);
add_action('wp_ajax_advaipbl_reset_threat_score', [$this->ajax_handler, 'ajax_reset_threat_score']);
add_action('wp_ajax_advaipbl_get_score_history', [$this->ajax_handler, 'ajax_get_score_history']);
add_action('wp_ajax_advaipbl_delete_signature', [$this->ajax_handler, 'ajax_delete_signature']);
add_action('wp_ajax_advaipbl_get_signature_details', [$this->ajax_handler, 'ajax_get_signature_details']);
add_action('wp_ajax_advaipbl_whitelist_signature', [$this->ajax_handler, 'ajax_whitelist_signature']);
add_action('wp_ajax_advaipbl_get_lockdown_details', [$this->ajax_handler, 'ajax_get_lockdown_details']);
add_action('wp_ajax_advaipbl_inspect_ip', [$this->ajax_handler, 'ajax_inspect_ip']);
add_action('wp_ajax_advaipbl_force_run_cron', [$this->ajax_handler, 'ajax_force_run_cron']);
add_action('admin_post_advaipbl_import_settings', [ $this, 'handle_import_settings' ]);
add_action('admin_post_advaipbl_clear_location_cache_action', [$this, 'handle_clear_cache_action']);
add_action('admin_post_advaipbl_revoke_vip_passes_action', [$this, 'handle_revoke_vip_passes_action']);
add_action('admin_post_advaipbl_send_test_email', [ $this, 'handle_send_test_email' ]);
add_action('admin_post_advaipbl_send_test_push', [ $this, 'handle_send_test_push' ]);
add_action('admin_post_advaipbl_run_manual_scan', [ $this, 'handle_run_manual_scan' ]);
add_action('admin_post_advaipbl_wizard_step_1', [ $this->action_handler, 'handle_wizard_step_1' ]);
add_action('admin_post_advaipbl_wizard_step_2', [ $this->action_handler, 'handle_wizard_step_2' ]);
add_action('admin_post_advaipbl_wizard_step_3', [ $this->action_handler, 'handle_wizard_step_3' ]);
add_action('admin_post_advaipbl_wizard_step_4', [ $this->action_handler, 'handle_wizard_step_4' ]);
}
add_action('wp_ajax_advaipbl_get_advanced_rules', [$this->ajax_handler, 'ajax_get_advanced_rules']);
add_action('wp_ajax_advaipbl_save_advanced_rule', [$this->ajax_handler, 'ajax_save_advanced_rule']);
add_action('wp_ajax_advaipbl_toggle_advanced_rule', [$this->ajax_handler, 'ajax_toggle_advanced_rule']);
add_action('wp_ajax_advaipbl_delete_advanced_rule', [$this->ajax_handler, 'ajax_delete_advanced_rule']);
add_action('wp_ajax_advaipbl_reorder_rules', [$this->ajax_handler, 'ajax_reorder_advanced_rules']);
add_action('wp_ajax_advaipbl_bulk_delete_advanced_rules', [$this->ajax_handler, 'ajax_bulk_delete_advanced_rules']);
add_action('wp_ajax_advaipbl_export_advanced_rules', [$this->ajax_handler, 'ajax_export_advanced_rules']);
add_action('wp_ajax_advaipbl_export_selected_advanced_rules', [$this->ajax_handler, 'ajax_export_selected_advanced_rules']);
add_action('wp_ajax_advaipbl_import_advanced_rules', [$this->ajax_handler, 'ajax_import_advanced_rules']);
add_action('wp_ajax_advaipbl_verify_abuseipdb_key', [$this->ajax_handler, 'ajax_verify_abuseipdb_key']);
add_action('wp_ajax_advaipbl_bulk_import_whitelist', [$this->ajax_handler, 'ajax_bulk_import_whitelist']);
add_action('wp_ajax_advaipbl_bulk_export_whitelist', [$this->ajax_handler, 'ajax_bulk_export_whitelist']);
add_action('wp_ajax_advaipbl_bulk_import_blocked_ips', [$this->ajax_handler, 'ajax_bulk_import_blocked_ips']);
add_action('wp_ajax_advaipbl_bulk_export_blocked_ips', [$this->ajax_handler, 'ajax_bulk_export_blocked_ips']);
if (! empty($this->options['xmlrpc_protection_mode']) && 'disabled' === $this->options['xmlrpc_protection_mode']) {
add_filter('xmlrpc_enabled', '__return_false');
remove_action('wp_head', 'rsd_link');
remove_action('wp_head', 'wlwmanifest_link');
}
if (! empty($this->options['disable_imagick'])) {
add_filter('wp_image_editors', [$this, 'force_gd_image_editor']);
}
if (! empty($this->options['remove_x_powered_by'])) {
add_action('init', function () {
if (function_exists('header_remove') && !headers_sent()) {
header_remove('X-Powered-By');
}
}, 9999);
}
if (! empty($this->options['hide_wp_version'])) {
remove_action('wp_head', 'wp_generator');
add_filter('the_generator', '__return_empty_string');
add_filter('style_loader_src', [$this, 'remove_wp_version_strings'], 10, 2);
add_filter('script_loader_src', [$this, 'remove_wp_version_strings'], 10, 2);
}
if (! empty($this->options['disable_app_passwords'])) {
add_filter('wp_is_application_passwords_available', '__return_false');
}
if (! empty($this->options['disable_file_editor'])) {
if (! defined('DISALLOW_FILE_EDIT')) {
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound
define('DISALLOW_FILE_EDIT', true);
}
}
if (!empty($this->options['recaptcha_enable']) && '1' === $this->options['recaptcha_enable'] && !empty($this->options['recaptcha_site_key']) && !empty($this->options['recaptcha_secret_key'])) {
add_action('login_enqueue_scripts', array($this, 'enqueue_recaptcha_script'));
add_action('wp_enqueue_scripts', array($this, 'enqueue_recaptcha_script'));
add_action('login_form', array($this, 'display_recaptcha_field'));
add_action('register_form', array($this, 'display_recaptcha_field'));
add_action('lostpassword_form', array($this, 'display_recaptcha_field'));
add_action('woocommerce_login_form', array($this, 'display_recaptcha_field'));
add_action('woocommerce_register_form', array($this, 'display_recaptcha_field'));
add_action('woocommerce_lostpassword_form', array($this, 'display_recaptcha_field'));
add_action('um_after_login_fields', array($this, 'display_recaptcha_field'));
add_action('bp_sidebar_login_form', array($this, 'display_recaptcha_field'));
add_filter('authenticate', array($this, 'validate_recaptcha_response'), 20, 3);
}
}
/**
* Forces the use of the GD library for image processing, disabling Imagick.
* @param array $editors Array of available image editors.
* @return array
*/
public function force_gd_image_editor($editors)
{
return [ 'WP_Image_Editor_GD' ];
}
/**
* Removes 'ver=' query string from scripts and styles if hide_wp_version is active.
*/
public function remove_wp_version_strings($src)
{
if (strpos($src, 'ver=')) {
$src = remove_query_arg('ver', $src);
}
return $src;
}
/**
* Creates or removes an .htaccess file in the uploads directory to block PHP execution.
*/
public function manage_uploads_htaccess($enable)
{
$upload_dir = wp_upload_dir();
if (! empty($upload_dir['error'])) {
return;
}
$htaccess_path = trailingslashit($upload_dir['basedir']) . '.htaccess';
$marker = 'Advanced IP Blocker - Block PHP in Uploads';
if (!function_exists('insert_with_markers')) {
require_once ABSPATH . 'wp-admin/includes/misc.php';
}
if ($enable) {
$rules = [
'',
'Deny from all',
''
];
insert_with_markers($htaccess_path, $marker, $rules);
} else {
if (file_exists($htaccess_path)) {
insert_with_markers($htaccess_path, $marker, []);
}
}
}
/**
* Checks if the database version matches the version defined in the plugin.
* If it doesn't match, or if forced, it runs the table configuration.
*/
public function check_database_version()
{
$installed_ver = get_option('advaipbl_db_version');
if (version_compare($installed_ver, ADVAIPBL_DB_VERSION, '<')) {
self::setup_database_tables();
$default_asns = [
'# Essential Crawlers and Services',
'# Google LLC',
'#AS15169',
'# Microsoft Corporation',
'#AS8075',
'# Automattic Inc.',
'AS2635',
'# Facebook, Inc.',
'AS32934',
'# Stripe, Inc.',
'AS5091',
'# Stripe, Inc.',
'AS394562',
'# PayPal, Inc.',
'AS17012',
'# Apple Inc.',
'#AS714'
];
add_option(self::OPTION_WHITELISTED_ASNS, $default_asns);
}
}
/**
* Filters user capabilities to dynamically grant 'advaipbl_manage_settings'.
*/
public function filter_advaipbl_capabilities($allcaps, $caps, $args, $user)
{
if (in_array('advaipbl_manage_settings', $caps)) {
$user_id = $user->ID;
$allowed = false;
if ($user_id == 1) {
$allowed = true;
} elseif (is_multisite() && is_super_admin($user_id)) {
$allowed = true;
} else {
$allowed_admins = $this->options['allowed_admin_users'] ?? [];
if (empty($allowed_admins)) {
if (!empty($allcaps['manage_options'])) {
$allowed = true;
}
} else {
if (in_array((string)$user_id, $allowed_admins, true) || in_array($user_id, $allowed_admins, true)) {
$allowed = true;
}
}
}
if ($allowed) {
$allcaps['advaipbl_manage_settings'] = true;
}
}
return $allcaps;
}
/**
* Helper routine to automatically generate V3 API Tokens for users who
* already had the AIB Network activated in older versions.
*/
private function auto_migrate_v3_token()
{
$this->options = get_option(self::OPTION_SETTINGS, []);
if (empty($this->options['enable_community_network'])) {
return;
}
if (!empty($this->options['api_token_v3'])) {
return;
}
if (isset($this->community_manager)) {
$this->community_manager->register_site();
$this->options = get_option(self::OPTION_SETTINGS, []);
}
}
/**
* @param string $key The cache key.
* @return mixed The cache value, or false if it does not exist or has expired.
*/
public function get_from_custom_cache($key, $get_full_object = false)
{
if (! isset($this->cache_manager)) {
$this->cache_manager = new ADVAIPBL_Cache_Manager();
}
return $this->cache_manager->get($key, $get_full_object);
}
/**
* Saves a value in the custom cache table.
*
* @param string $key The cache key.
* @param mixed $value The value to save (will be serialized).
* @param int $expiration Cache duration in seconds.
*/
public function set_in_custom_cache($key, $value, $expiration)
{
if (! isset($this->cache_manager)) {
$this->cache_manager = new ADVAIPBL_Cache_Manager();
}
return $this->cache_manager->set($key, $value, $expiration);
}
/**
* Clears all expired entries from the custom cache table.
* Designed to be called by a WP-Cron job for database maintenance.
*/
public function cleanup_expired_cache_entries()
{
if (! isset($this->cache_manager)) {
$this->cache_manager = new ADVAIPBL_Cache_Manager();
}
$deleted_rows = $this->cache_manager->cleanup_expired();
if (is_numeric($deleted_rows) && $deleted_rows > 0) {
/* translators: %s is a placeholder */
$this->log_event(sprintf(__('Cache cleanup task ran. Removed %d stale entries.', 'advanced-ip-blocker'), $deleted_rows), 'info');
}
}
/**
* Registers all 2FA related hooks.
*/
public function add_2fa_hooks()
{
add_action('show_user_profile', [$this, 'display_2fa_section_in_profile']);
add_action('edit_user_profile', [$this, 'display_2fa_section_in_profile']);
add_action('personal_options_update', [$this, 'save_2fa_section_in_profile']);
add_action('edit_user_profile_update', [$this, 'save_2fa_section_in_profile']);
add_action('wp_ajax_advaipbl_2fa_generate', [$this->ajax_handler, 'ajax_2fa_generate']);
add_action('wp_ajax_advaipbl_2fa_activate', [$this->ajax_handler, 'ajax_2fa_activate']);
add_action('wp_ajax_advaipbl_2fa_deactivate', [$this->ajax_handler, 'ajax_2fa_deactivate']);
add_filter('authenticate', [$this, 'intercept_login_step_1'], 20, 3);
add_action('login_form_advaipbl_validate_2fa', [$this, 'display_2fa_login_form_step_2']);
add_action('login_form_advaipbl_validate_2fa_backup', [$this, 'display_2fa_backup_code_form']);
add_action('login_form_login', [$this, 'handle_login_action']);
}
/**
* Logs the current request signature if the option is enabled.
*/
public function log_request_signature()
{
if (empty($this->options['enable_signature_engine'])) {
return;
}
$request_uri = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
if (strpos($request_uri, '/advaipbl/v1/live-attacks') !== false) {
return;
}
if (wp_doing_ajax() || is_admin() || wp_doing_cron() || (defined('WP_CLI') && WP_CLI)) {
return;
}
if (wp_is_json_request() && strpos($request_uri, '/telemetry/') === false && strpos($request_uri, '/aib-network/') === false && strpos($request_uri, '/aib-scanner/') === false && strpos($request_uri, '/aib-api/') === false) {
return;
}
if ($this->challenge_passed_this_request) {
return;
}
if ($this->request_is_asn_whitelisted) {
return;
}
$ip = $this->get_client_ip();
if ($this->is_whitelisted($ip)) {
return;
}
$signature_hash = $this->fingerprint_manager->generate_signature();
$raw_user_whitelist = $this->options['trusted_signature_hashes'] ?? '';
$user_whitelisted_hashes = [];
if (!empty($raw_user_whitelist)) {
$lines = explode("\n", $raw_user_whitelist);
foreach ($lines as $line) {
$trimmed_line = trim($line);
if (!empty($trimmed_line) && strpos($trimmed_line, '#') !== 0) {
$user_whitelisted_hashes[] = $trimmed_line;
}
}
}
$trusted_signature_hashes = apply_filters('advaipbl_trusted_signature_hashes', $user_whitelisted_hashes);
if (in_array($signature_hash, $trusted_signature_hashes, true)) {
return;
}
global $wpdb;
$table_name = $wpdb->prefix . 'advaipbl_request_log';
$is_fake_bot = 0;
if (isset($this->bot_verifier) && $this->bot_verifier->is_known_bot_impersonator($ip, $this->get_user_agent())) {
if (get_transient('advaipbl_verified_bot_' . md5($ip))) {
$is_fake_bot = 0;
} else {
if (! $this->bot_verifier->is_verified_bot($ip, $this->get_user_agent())) {
$is_fake_bot = 1;
}
}
}
$data_to_log = [
'timestamp' => time(),
'ip_hash' => hash('sha256', $ip),
'request_uri' => $this->get_current_request_uri(),
'user_agent' => $this->get_user_agent(),
'request_headers' => $this->fingerprint_manager->get_request_headers_for_log(),
'request_method' => $this->get_request_method(),
'signature_hash' => $signature_hash,
'is_fake_bot' => $is_fake_bot,
];
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
$wpdb->insert($table_name, $data_to_log);
}
/**
* Purges all cache from popular caching plugins.
* Called when security settings or blocklists change to ensure immediate effect.
*/
public function purge_all_page_caches()
{
if (has_action('litespeed_purge_all')) {
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
do_action('litespeed_purge_all');
} elseif (defined('LSCWP_V')) {
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
do_action('litespeed_purge_all_hook');
}
if (function_exists('rocket_clean_domain')) {
rocket_clean_domain();
}
if (function_exists('w3tc_flush_all')) {
w3tc_flush_all();
}
if (function_exists('wp_cache_clear_cache')) {
wp_cache_clear_cache();
}
if (isset($GLOBALS['wp_fastest_cache']) && method_exists($GLOBALS['wp_fastest_cache'], 'deleteCache')) {
$GLOBALS['wp_fastest_cache']->deleteCache(true);
}
if (class_exists('autoptimizeCache')) {
autoptimizeCache::clearall();
}
if (function_exists('sg_cachepress_purge_cache')) {
sg_cachepress_purge_cache();
}
if (class_exists('Kinsta\Cache')) {
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound
do_action('kinsta_purge_edge_cache');
}
$this->log_event(__('System page caches successfully flushed after a security update.', 'advanced-ip-blocker'), 'info', 'localhost');
}
/**
* Executes early to verify known bots and take action.
*/
public function verify_known_bots()
{
if (empty($this->options['enable_bot_verification']) || is_admin() || wp_doing_cron() || (defined('WP_CLI') && WP_CLI)) {
return;
}
$ip = $this->get_client_ip();
$user_agent = $this->get_user_agent();
if (get_transient('advaipbl_verified_bot_' . md5($ip))) {
$this->request_is_asn_whitelisted = true;
return;
}
$is_verified = $this->bot_verifier->is_verified_bot($ip, $user_agent);
if ($is_verified) {
if ($this->is_visitor_actively_blocked()) {
return;
}
if (!empty($this->options['enable_spamhaus_asn']) || !empty($this->options['enable_manual_asn'])) {
$provider = $this->options['geolocation_provider'] ?? '';
if (in_array($provider, ['ip-api.com', 'ipinfo.io'], true)) {
if ($this->asn_manager->check_asn_block($ip)) {
return;
}
}
}
$blocked_uas = get_option('advaipbl_blocked_user_agents', []);
foreach ($blocked_uas as $blocked_ua) {
$blocked_ua = trim(preg_replace('/#.*$/', '', $blocked_ua));
if (!empty($blocked_ua) && stripos($user_agent, $blocked_ua) !== false) {
return;
}
}
set_transient('advaipbl_verified_bot_' . md5($ip), true, DAY_IN_SECONDS);
$this->request_is_asn_whitelisted = true;
} elseif ($this->bot_verifier->is_known_bot_impersonator($ip, $user_agent)) {
$this->is_bot_impersonator = true;
return;
}
}
public function check_for_ghost_ips()
{
if (empty($this->options['block_ghost_ips'])) {
return;
}
if (!empty($this->request_is_asn_whitelisted) || $this->is_whitelisted($this->get_client_ip()) || !empty($this->is_advanced_rule_allowed)) {
return;
}
$ip = $this->get_client_ip();
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return;
}
$location = $this->geolocation_manager->fetch_location($ip);
if (!empty($location['error']) || !empty($location['fallback_error'])) {
return;
}
$asn_info = $this->asn_manager->extract_asn_from_data($location);
if (!empty($asn_info)) {
return;
}
$hostname = @gethostbyaddr($ip);
if (!empty($hostname) && $hostname !== $ip) {
return;
}
$duration_minutes = 1440;
if (!empty($this->options['enable_threat_scoring'])) {
$points = (int) ($this->options['score_impersonation'] ?? 100);
$this->threat_score_manager->increment_score(
$ip,
$points,
'ghost_ip',
[
'uri' => $this->get_current_request_uri()
]
);
$duration_minutes = (int) ($this->options['duration_threat_score'] ?? 1440);
}
$this->block_ip_instantly(
$ip,
'ghost_ip',
__('Anonymous IP blocked by Ghost IPs Shield.', 'advanced-ip-blocker'),
[],
'frontend_block',
$duration_minutes * 60
);
}
public function check_for_bot_impersonator_block()
{
if (empty($this->is_bot_impersonator)) {
return;
}
if (!empty($this->request_is_asn_whitelisted) || $this->is_whitelisted($this->get_client_ip()) || !empty($this->is_advanced_rule_allowed)) {
return;
}
$ip = $this->get_client_ip();
$user_agent = $this->get_user_agent();
$duration_minutes = 1440;
if (!empty($this->options['enable_threat_scoring'])) {
$points_impersonation = (int) ($this->options['score_impersonation'] ?? 100);
$this->threat_score_manager->increment_score(
$ip,
$points_impersonation,
'impersonation',
[
'impersonated_user_agent' => $user_agent,
'uri' => $this->get_current_request_uri()
]
);
$duration_minutes = (int) ($this->options['duration_threat_score'] ?? 1440);
} else {
$duration_minutes = (int) ($this->options['duration_user_agent'] ?? 1440);
}
$duration_seconds = ($duration_minutes > 0) ? $duration_minutes * 60 : 0;
$this->block_ip_instantly(
$ip,
'impersonation',
__('Blocked for impersonating a known crawler.', 'advanced-ip-blocker'),
[
'impersonated_user_agent' => $user_agent,
'uri' => $this->get_current_request_uri()
],
$context = 'frontend_block',
$duration_seconds
);
}
/**
* Executes on a very early hook to check the request signature.
* If the signature is malicious, it serves a JavaScript challenge.
*/
public function check_for_malicious_signature()
{
if ($this->is_request_uri_excluded()) {
return;
}
if (!empty($this->request_is_asn_whitelisted) || $this->is_whitelisted($this->get_client_ip()) || !empty($this->is_advanced_rule_allowed)) {
return;
}
if (wp_doing_ajax() || wp_is_json_request() || is_admin() || wp_doing_cron() || (defined('WP_CLI') && WP_CLI)) {
return;
}
if (get_transient('advaipbl_grace_pass_' . md5($this->get_client_ip()))) {
return;
}
if (empty($this->options['enable_signature_blocking'])) {
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Missing
if (isset($_POST['_advaipbl_js_token'])) {
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$global_duration_hours = (int)($this->options['global_challenge_cookie_duration'] ?? 4);
$duration_seconds = ($global_duration_hours > 0) ? $global_duration_hours * HOUR_IN_SECONDS : 0;
$this->js_challenge_manager->verify_challenge('advaipbl_js_verified', $duration_seconds);
}
if ($this->js_challenge_manager->is_vip_pass_valid()) {
return;
}
$signature_hash = $this->fingerprint_manager->generate_signature();
global $wpdb;
$signatures_table = $wpdb->prefix . 'advaipbl_malicious_signatures';
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
if ($wpdb->get_var("SHOW TABLES LIKE '$signatures_table'") != $signatures_table) {
return;
}
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
$is_malicious = $wpdb->get_var($wpdb->prepare(
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
"SELECT id FROM {$signatures_table} WHERE signature_hash = %s AND expires_at > %d",
$signature_hash,
time()
));
if ($is_malicious) {
$mode = $this->options['signature_challenge_mode'] ?? 'default';
$this->log_specific_error(
'signature_challenge',
$this->get_client_ip(),
[
'signature_hash' => $signature_hash,
'uri' => $this->get_current_request_uri(),
'mode' => $mode
],
'warning'
);
$this->js_challenge_manager->serve_challenge('signature', $mode);
}
}
/**
* Checks if the visitor comes from a country that requires a geographic challenge.
* Executes on an early 'init' hook.
*/
public function check_for_geo_challenge()
{
if ($this->is_request_uri_excluded()) {
return;
}
if (empty($this->options['enable_geo_challenge'])) {
return;
}
// phpcs:ignore WordPress.Security.NonceVerification.Missing
if (isset($_POST['_advaipbl_challenge_type']) && $_POST['_advaipbl_challenge_type'] === 'geo_challenge') {
$duration_hours = (int)($this->options['global_challenge_cookie_duration'] ?? 4);
$duration_seconds = ($duration_hours > 0) ? $duration_hours * HOUR_IN_SECONDS : 0;
$this->js_challenge_manager->verify_challenge('advaipbl_js_verified', $duration_seconds);
}
if ($this->js_challenge_manager->is_vip_pass_valid()) {
return;
}
$ip = $this->get_client_ip();
if (get_transient('advaipbl_grace_pass_' . md5($ip))) {
return;
}
if ($this->request_is_asn_whitelisted || wp_doing_cron() || is_admin() || (defined('WP_CLI') && WP_CLI) || $this->is_whitelisted($ip) || !empty($this->is_advanced_rule_allowed)) {
return;
}
$challenged_countries = $this->options['geo_challenge_countries'] ?? [];
if (empty($challenged_countries)) {
return;
}
$location = $this->geolocation_manager->fetch_location($ip);
if ($location && !empty($location['country_code'])) {
if (in_array($location['country_code'], $challenged_countries, true)) {
$mode = $this->options['geo_challenge_mode'] ?? 'default';
$this->log_specific_error(
'geo_challenge',
$ip,
[
'country' => $location['country'] ?? $location['country_code'],
'action' => 'Geolocation Challenge',
'trigger' => 'Country Match',
'mode' => $mode
],
'warning'
);
$this->js_challenge_manager->serve_challenge('geo_challenge', $mode);
}
}
}
/**
* Sends notifications (Email/Push) when a new malicious signature is identified.
*
* @param string $signature_hash The signature hash.
* @param string $reason The reason why it was flagged.
* @param string $user_agent The example User-Agent associated with the signature.
*/
/**
* Sends notifications (Email/Push) when a new malicious signature is identified.
*/
public function send_signature_flagged_notification($signature_hash, $reason, $user_agent = 'N/A')
{
if (isset($this->notification_manager)) {
$this->notification_manager->send_signature_flagged_notification($signature_hash, $reason, $user_agent);
}
}
/**
* Callback for the cron event that executes threat score decay.
*/
public function execute_threat_score_decay()
{
if (empty($this->options['enable_threat_scoring'])) {
return;
}
$decay_points = (int) ($this->options['score_decay_points'] ?? 1);
$decay_frequency_hours = (int) ($this->options['score_decay_frequency'] ?? 1);
$inactive_for_seconds = $decay_frequency_hours * HOUR_IN_SECONDS;
if ($decay_points > 0 && $decay_frequency_hours > 0) {
$result = $this->threat_score_manager->decay_scores($decay_points, $inactive_for_seconds);
if (($result['updated'] ?? 0) > 0 || ($result['deleted'] ?? 0) > 0) {
$log_message = sprintf(
/* translators: %s is a placeholder */
__('Threat score decay process ran. Reduced: %1$d IPs, Reset: %2$d IPs.', 'advanced-ip-blocker'),
$result['updated'],
$result['deleted']
);
$this->log_event($log_message, 'info');
}
}
}
/**
* Callback for the cron event that executes attack signature analysis.
*/
public function execute_signature_analysis()
{
if (empty($this->options['enable_signature_analysis'])) {
return;
}
$ip_threshold = (int) ($this->options['signature_ip_threshold'] ?? 5);
$analysis_window_hours = (int) ($this->options['signature_analysis_window'] ?? 1);
$rule_ttl_hours = (int) ($this->options['signature_rule_ttl'] ?? 24);
$this->fingerprint_manager->analyze_and_flag_signatures(
$ip_threshold,
$analysis_window_hours * HOUR_IN_SECONDS,
$rule_ttl_hours * HOUR_IN_SECONDS
);
}
/**
* Registers the REST API endpoint for the live attack feed.
*/
public function register_live_feed_api_endpoint()
{
if (! isset($this->live_feed_manager)) {
$this->live_feed_manager = new ADVAIPBL_Live_Feed_Manager($this);
}
$this->live_feed_manager->register_api_endpoint();
}
/**
* Callback for the API endpoint. Returns the latest attacks.
*
* @param WP_REST_Request $request
* @return WP_REST_Response
*/
public function get_live_attacks_for_feed(WP_REST_Request $request)
{
if (! isset($this->live_feed_manager)) {
$this->live_feed_manager = new ADVAIPBL_Live_Feed_Manager($this);
}
return $this->live_feed_manager->get_live_attacks($request);
}
/**
* REST API endpoint to get a fresh nonce for the live feed.
* This prevents page cache issues.
*/
public function get_live_feed_nonce()
{
if (! isset($this->live_feed_manager)) {
$this->live_feed_manager = new ADVAIPBL_Live_Feed_Manager($this);
}
return $this->live_feed_manager->get_nonce();
}
/**
* Function for the [advaipbl_live_feed] shortcode.
* Generates the required HTML and CSS for the feed, and enqueues the JS script.
*
* @param array $atts Shortcode attributes.
* @return string The HTML and CSS for the feed.
*/
public function render_live_feed_shortcode($atts)
{
if (! isset($this->live_feed_manager)) {
$this->live_feed_manager = new ADVAIPBL_Live_Feed_Manager($this);
}
return $this->live_feed_manager->render_shortcode($atts);
}
/**
* Detects and logs WP-Cron executions.
*/
public function log_wp_cron_execution()
{
$is_doing_cron = (defined('DOING_CRON') && DOING_CRON);
$is_cron_url = (strpos($this->get_current_request_uri(), 'wp-cron.php') !== false);
if ($is_doing_cron || $is_cron_url) {
$ip = $this->get_client_ip();
$last_cron_ip = get_option('advaipbl_last_cron_ip');
if ($last_cron_ip !== $ip) {
update_option('advaipbl_last_cron_ip', $ip, false);
}
$transient_key = 'advaipbl_cron_log_lock_' . md5($ip);
if (false !== get_transient($transient_key)) {
return;
}
set_transient($transient_key, true, 60);
global $wpdb;
$table_name = $wpdb->prefix . 'advaipbl_logs';
$cron_array = _get_cron_array();
$due_hooks = [];
if (is_array($cron_array)) {
$current_time = time();
foreach ($cron_array as $timestamp => $hooks) {
if ($timestamp <= $current_time) {
foreach ($hooks as $hook_name => $events) {
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$due_hooks[] = $hook_name;
}
}
}
}
$due_hooks = array_unique($due_hooks);
$server_ip = $this->get_server_ip();
$source = 'External';
if ($ip === $server_ip || $ip === '127.0.0.1' || $ip === '::1') {
$source = 'Server';
}
$details = [
'url' => $this->get_current_request_uri(),
'uri' => $this->get_current_request_uri(),
'method' => $this->get_request_method(),
'user_agent' => $this->get_user_agent(),
'due_hooks' => $due_hooks,
'source' => $source,
];
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
@$wpdb->insert(
$table_name,
[
'timestamp' => current_time('mysql', 1),
'ip' => $ip,
'log_type' => 'wp_cron',
'level' => 'info',
'message' => __('WP-Cron execution triggered.', 'advanced-ip-blocker'),
'details' => wp_json_encode($details)
]
);
}
}
/**
* Enqueues the Google reCAPTCHA API script on the login page.
*/
public function enqueue_recaptcha_script()
{
if (function_exists('is_user_logged_in') && is_user_logged_in()) {
return;
}
$version = $this->options['recaptcha_version'] ?? 'v3';
$site_key = $this->options['recaptcha_site_key'] ?? '';
if (empty($site_key)) {
return;
}
$script_url = 'https://www.google.com/recaptcha/api.js';
if ('v3' === $version) {
$script_url = add_query_arg('render', $site_key, $script_url);
}
// phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
wp_enqueue_script(
'google-recaptcha',
$script_url,
array(),
null, // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
true
);
}
/**
* Triggered when WAF rules are updated from the settings page.
* Logs the event in the general log.
*
* @param mixed $old_value The old option value.
* @param mixed $new_value The new option value.
*/
public function on_waf_rules_update($old_value, $new_value)
{
if ($old_value !== $new_value) {
$message = sprintf(
/* translators: %s is a placeholder */
__('WAF rules list updated by %s.', 'advanced-ip-blocker'),
$this->get_current_admin_username()
);
$this->log_event($message, 'info');
}
}
/**
* Downloads and processes the Spamhaus ASN DROP list and saves it to the database.
* Called by WP-Cron and manual refresh actions.
*/
public function update_spamhaus_list()
{
$url = 'https://www.spamhaus.org/drop/asndrop.json';
$response = wp_remote_get($url, ['timeout' => 15]);
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
$error_message = is_wp_error($response) ? $response->get_error_message() : 'HTTP ' . wp_remote_retrieve_response_code($response);
$this->log_event(sprintf('Failed to download Spamhaus ASN list. Error: %s', $error_message), 'error');
return;
}
$body_string = wp_remote_retrieve_body($response);
preg_match_all('/\{.*?\}/', $body_string, $matches);
if (empty($matches[0])) {
$this->log_event('Failed to parse Spamhaus ASN list: No valid JSON objects found in the response.', 'error');
return;
}
$json_objects = $matches[0];
$asns = [];
foreach ($json_objects as $json_string) {
$entry = json_decode($json_string, true);
if (json_last_error() === JSON_ERROR_NONE && isset($entry['asn'])) {
$asns[] = 'AS' . $entry['asn'];
}
}
if (empty($asns)) {
$this->log_event('Spamhaus ASN list was parsed but no ASN entries were extracted.', 'warning');
return;
}
update_option('advaipbl_spamhaus_asn_list', array_unique($asns));
update_option('advaipbl_spamhaus_last_update', time());
$this->log_event(sprintf('Successfully updated Spamhaus ASN list with %d entries.', count($asns)), 'info');
}
public function handle_spamhaus_refresh_action()
{
if (!current_user_can('advaipbl_manage_settings') || !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'] ?? '')), 'advaipbl-refresh-spamhaus')) {
wp_die('Security check failed.');
}
$this->update_spamhaus_list();
set_transient(self::TRANSIENT_ADMIN_NOTICE, ['message' => __('Spamhaus ASN list has been updated.', 'advanced-ip-blocker'), 'type' => 'success'], 30);
wp_safe_redirect(wp_get_referer());
exit;
}
/**
* Downloads and applies zero-day WAF rules from the Central Server.
*/
public function sync_zeroday_waf_rules()
{
if (empty($this->options['enable_intelligent_waf']) || '1' !== $this->options['enable_intelligent_waf']) {
return;
}
$api_token = $this->options['api_token_v3'] ?? '';
if (empty($api_token)) {
$this->log_event('Intelligent WAF Sync skipped: Central API Token V3 not configured.', 'warning');
return;
}
$api_url = 'https://advaipbl.com/wp-json/aib-api/v3/waf/zero-day';
$response = wp_remote_get($api_url, [
'headers' => [
'X-AIB-Auth' => 'Bearer ' . $api_token,
'Accept' => 'application/json'
],
'timeout' => 30
]);
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
$error_msg = is_wp_error($response) ? $response->get_error_message() : 'HTTP ' . wp_remote_retrieve_response_code($response);
$this->log_event('Intelligent WAF Sync failed. Reason: ' . $error_msg, 'error');
return;
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (json_last_error() !== JSON_ERROR_NONE || !isset($data['rules'])) {
$this->log_event('Intelligent WAF Sync failed: Invalid JSON response.', 'error');
return;
}
$raw_rules = is_array($data['rules']) ? $data['rules'] : [];
$active_rules_count = 0;
foreach ($raw_rules as $rule) {
$rule = trim((string) $rule);
if (!empty($rule) && strpos($rule, '#') !== 0) {
$active_rules_count++;
}
}
update_option('advaipbl_zeroday_waf_rules', $raw_rules);
update_option('advaipbl_zeroday_waf_last_sync', time());
$this->log_event(sprintf('Intelligent WAF Sync successful: Downloaded %d zero-day signatures.', $active_rules_count), 'info');
}
/**
* Lightweight ping to check if new WAF rules are available.
*/
public function check_zeroday_waf_version()
{
if (empty($this->options['enable_intelligent_waf']) || '1' !== $this->options['enable_intelligent_waf']) {
return;
}
$api_token = $this->options['api_token_v3'] ?? '';
if (empty($api_token)) {
return;
}
$api_url = 'https://advaipbl.com/wp-json/aib-api/v3/waf/version';
$response = wp_remote_get($api_url, [
'headers' => [
'X-AIB-Auth' => 'Bearer ' . $api_token,
'Accept' => 'application/json'
],
'timeout' => 10
]);
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
return;
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (json_last_error() === JSON_ERROR_NONE && isset($data['version'])) {
$remote_version = (int) $data['version'];
$local_version = (int) get_option('advaipbl_zeroday_waf_version', 0);
if ($remote_version > $local_version) {
$this->sync_zeroday_waf_rules();
update_option('advaipbl_zeroday_waf_version', $remote_version);
}
}
}
/**
* Downloads and applies advanced zero-day WAF rules from the Central Server.
*/
public function sync_advanced_zeroday_rules()
{
if (empty($this->options['enable_cloud_advanced_rules']) || '1' !== $this->options['enable_cloud_advanced_rules']) {
return;
}
$api_token = $this->options['api_token_v3'] ?? '';
if (empty($api_token)) {
$this->log_event('Advanced WAF Sync skipped: Central API Token V3 not configured.', 'warning');
return;
}
$api_url = 'https://advaipbl.com/wp-json/aib-api/v3/waf/advanced-zero-day';
$response = wp_remote_get($api_url, [
'headers' => [
'X-AIB-Auth' => 'Bearer ' . $api_token,
'Accept' => 'application/json'
],
'timeout' => 30
]);
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
$error_msg = is_wp_error($response) ? $response->get_error_message() : 'HTTP ' . wp_remote_retrieve_response_code($response);
$this->log_event('Advanced WAF Sync failed. Reason: ' . $error_msg, 'error');
return;
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (json_last_error() !== JSON_ERROR_NONE || !isset($data['rules'])) {
$this->log_event('Advanced WAF Sync failed: Invalid JSON response.', 'error');
return;
}
$remote_rules = is_array($data['rules']) ? $data['rules'] : [];
$local_rules = get_option('advaipbl_advanced_rules', []);
if (!is_array($local_rules)) {
$local_rules = [];
}
$user_rules = array_filter($local_rules, function ($rule) {
return isset($rule['id']) && strpos($rule['id'], 'ar_zd_') !== 0;
});
$merged_rules = array_merge(array_values($user_rules), array_values($remote_rules));
update_option('advaipbl_advanced_rules', $merged_rules);
update_option('advaipbl_advanced_zeroday_waf_last_sync', time());
$this->log_event(sprintf('Advanced WAF Sync successful: Downloaded %d cloud rules.', count($remote_rules)), 'info');
}
/**
* Lightweight ping to check if new Advanced WAF rules are available.
*/
public function check_advanced_zeroday_version()
{
if (empty($this->options['enable_cloud_advanced_rules']) || '1' !== $this->options['enable_cloud_advanced_rules']) {
return;
}
$api_token = $this->options['api_token_v3'] ?? '';
if (empty($api_token)) {
return;
}
$api_url = 'https://advaipbl.com/wp-json/aib-api/v3/waf/advanced-version';
$response = wp_remote_get($api_url, [
'headers' => [
'X-AIB-Auth' => 'Bearer ' . $api_token,
'Accept' => 'application/json'
],
'timeout' => 10
]);
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
return;
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (json_last_error() === JSON_ERROR_NONE && isset($data['version'])) {
$remote_version = (int) $data['version'];
$local_version = (int) get_option('advaipbl_advanced_zeroday_waf_version', 0);
if ($remote_version > $local_version) {
$this->sync_advanced_zeroday_rules();
update_option('advaipbl_advanced_zeroday_waf_version', $remote_version);
}
}
}
/**
* Downloads and applies FIM Malware Signatures from the Central Server.
*/
public function sync_fim_signatures()
{
if (empty($this->options['enable_fim']) || '1' !== $this->options['enable_fim']) {
return 'FIM is disabled.';
}
$api_token = $this->options['api_token_v3'] ?? '';
if (empty($api_token)) {
$this->log_event('FIM Signatures Sync skipped: Central API Token V3 not configured.', 'warning');
return 'Central API Token V3 not configured.';
}
$api_url = 'https://advaipbl.com/wp-json/aib-api/v3/fim/signatures';
$response = wp_remote_get($api_url, [
'headers' => [
'X-AIB-Auth' => 'Bearer ' . $api_token,
'Accept' => 'application/json'
],
'timeout' => 30
]);
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
$error_msg = is_wp_error($response) ? $response->get_error_message() : 'HTTP ' . wp_remote_retrieve_response_code($response);
$this->log_event('FIM Signatures Sync failed. Reason: ' . $error_msg, 'error');
return $error_msg;
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (json_last_error() !== JSON_ERROR_NONE || !is_array($data)) {
$this->log_event('FIM Signatures Sync failed: Invalid JSON response.', 'error');
return 'Invalid JSON response from server.';
}
$opts = $this->options;
$enable_raw = !isset($opts['fim_enable_raw']) || !empty($opts['fim_enable_raw']);
$enable_regex = !empty($opts['fim_enable_regex']);
$enable_domains = !isset($opts['fim_enable_domains']) || !empty($opts['fim_enable_domains']);
$enable_md5 = !isset($opts['fim_enable_md5']) || !empty($opts['fim_enable_md5']);
$enable_sha256 = !empty($opts['fim_enable_sha256']);
if ($enable_raw) {
update_option('advaipbl_fim_signatures_raw', $data['raw'] ?? [], 'no');
} else {
delete_option('advaipbl_fim_signatures_raw');
}
if ($enable_regex) {
update_option('advaipbl_fim_signatures_regex', $data['regex'] ?? [], 'no');
} else {
delete_option('advaipbl_fim_signatures_regex');
}
if ($enable_domains) {
update_option('advaipbl_fim_signatures_domains', $data['domains'] ?? [], 'no');
} else {
delete_option('advaipbl_fim_signatures_domains');
}
if ($enable_md5) {
update_option('advaipbl_fim_signatures_md5', $data['md5'] ?? '', 'no');
} else {
delete_option('advaipbl_fim_signatures_md5');
}
if ($enable_sha256) {
update_option('advaipbl_fim_signatures_sha256', $data['sha256'] ?? '', 'no');
} else {
delete_option('advaipbl_fim_signatures_sha256');
}
update_option('advaipbl_fim_signatures_split', '1', 'yes');
update_option('advaipbl_fim_signatures_last_sync', time());
delete_option('advaipbl_fim_signatures');
$this->log_event('FIM Signatures Sync successful: Splitted signatures stored locally.', 'info');
return true;
}
/**
* Lightweight ping to check if new FIM Malware Signatures are available.
*/
public function check_fim_signatures_version()
{
if (empty($this->options['enable_fim']) || '1' !== $this->options['enable_fim']) {
return;
}
$api_token = $this->options['api_token_v3'] ?? '';
if (empty($api_token)) {
return;
}
$api_url = 'https://advaipbl.com/wp-json/aib-api/v3/fim/version';
$response = wp_remote_get($api_url, [
'headers' => [
'X-AIB-Auth' => 'Bearer ' . $api_token,
'Accept' => 'application/json'
],
'timeout' => 10
]);
if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
return;
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (json_last_error() === JSON_ERROR_NONE && isset($data['version'])) {
$remote_version = (int) $data['version'];
$local_version = (int) get_option('advaipbl_fim_signatures_version', 0);
if ($remote_version > $local_version) {
$this->sync_fim_signatures();
update_option('advaipbl_fim_signatures_version', $remote_version);
}
}
}
/**
* Displays the reCAPTCHA field on the login form.
*/
public function display_recaptcha_field()
{
$site_key = $this->options['recaptcha_site_key'] ?? '';
$version = $this->options['recaptcha_version'] ?? 'v3';
echo '';
if ('v2' === $version) {
echo '';
} else {
echo '';
?>
options['recaptcha_secret_key'] ?? '';
if (empty($secret_key)) {
return $user;
}
if (defined('XMLRPC_REQUEST') && XMLRPC_REQUEST) {
return $user;
}
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$is_wp_login = (isset($GLOBALS['pagenow']) && $GLOBALS['pagenow'] === 'wp-login.php') ||
(isset($_SERVER['PHP_SELF']) && strpos(sanitize_text_field(wp_unslash($_SERVER['PHP_SELF'])), 'wp-login.php') !== false);
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$is_recaptcha_rendered = isset($_POST['advaipbl_recaptcha_rendered']);
if (! $is_wp_login && ! $is_recaptcha_rendered) {
return $user;
}
// phpcs:ignore WordPress.Security.NonceVerification.Missing
$recaptcha_response = isset($_POST['g-recaptcha-response']) ? sanitize_text_field(wp_unslash($_POST['g-recaptcha-response'])) : '';
if (empty($recaptcha_response)) {
return new WP_Error('recaptcha_empty', __('ERROR: Please complete the reCAPTCHA verification.', 'advanced-ip-blocker'));
}
$token = $recaptcha_response;
$visitor_ip = $this->get_client_ip();
$response = wp_remote_post('https://www.google.com/recaptcha/api/siteverify', array(
'body' => array(
'secret' => $secret_key,
'response' => $token,
'remoteip' => $visitor_ip,
),
));
if (is_wp_error($response)) {
return new WP_Error('recaptcha_api_error', __('ERROR: Could not connect to the reCAPTCHA service.', 'advanced-ip-blocker'));
}
$result = json_decode(wp_remote_retrieve_body($response), true);
if (!isset($result['success']) || true !== $result['success']) {
return new WP_Error('recaptcha_failed', __('ERROR: reCAPTCHA verification failed. Please try again.', 'advanced-ip-blocker'));
}
$version = $this->options['recaptcha_version'] ?? 'v3';
if ('v3' === $version) {
$threshold = (float) ($this->options['recaptcha_score_threshold'] ?? 0.5);
if (!isset($result['score']) || $result['score'] < $threshold) {
return new WP_Error('recaptcha_low_score', __('ERROR: Your action was blocked as it was flagged as automated.', 'advanced-ip-blocker'));
}
}
return $user;
}
/**
* Detects active plugins that likely use the XML-RPC interface.
*
* Uses a combination of known plugins and XML-RPC hook analysis.
*
* @return array Array with detected plugin names. Empty if none found.
*/
public function get_xmlrpc_dependent_plugins()
{
$dependent_plugins = [];
$active_plugins = get_option('active_plugins');
$known_slugs = [
'jetpack/jetpack.php',
'xmlrpc-debugger/xmlrpc-debugger.php'
];
foreach ($known_slugs as $slug) {
if (in_array($slug, $active_plugins, true)) {
$plugin_path = WP_PLUGIN_DIR . '/' . $slug;
if (file_exists($plugin_path)) {
$plugin_data = get_plugin_data($plugin_path);
if (!empty($plugin_data['Name'])) {
$dependent_plugins[$slug] = $plugin_data['Name'];
}
}
}
}
global $wp_filter;
$xmlrpc_hooks = [
'xmlrpc_methods',
'xmlrpc_call',
'xmlrpc_call_success',
'xmlrpc_call_failure',
'xmlrpc_before_insert_post',
];
foreach ($xmlrpc_hooks as $hook_name) {
if (isset($wp_filter[$hook_name])) {
foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) {
foreach ($callbacks as $callback) {
$function = $callback['function'];
$reflection = null;
try {
if (is_array($function) && is_object($function[0])) {
$reflection = new ReflectionClass($function[0]);
} elseif (is_string($function) && function_exists($function)) {
$reflection = new ReflectionFunction($function);
}
} catch (ReflectionException $e) {
continue;
}
if ($reflection) {
$file_path = $reflection->getFileName();
if ($file_path && strpos($file_path, WP_PLUGIN_DIR) !== false) {
$plugin_dir_path = str_replace('\\', '/', WP_PLUGIN_DIR);
$file_path = str_replace('\\', '/', $file_path);
$relative_path = ltrim(str_replace($plugin_dir_path, '', $file_path), '/');
$plugin_slug_parts = explode('/', $relative_path);
$plugin_folder = $plugin_slug_parts[0];
foreach ($active_plugins as $active_plugin_slug) {
if (strpos($active_plugin_slug, $plugin_folder) === 0) {
if (!isset($dependent_plugins[$active_plugin_slug])) {
$plugin_data = get_plugin_data(WP_PLUGIN_DIR . '/' . $active_plugin_slug);
if (!empty($plugin_data['Name'])) {
$dependent_plugins[$active_plugin_slug] = $plugin_data['Name'];
}
}
break;
}
}
}
}
}
}
}
}
return array_values($dependent_plugins);
}
/**
* Gets the number of blocked IPs/ranges.
* Caches the result to improve performance.
*
* @return int Total number of blocked IP entries.
*/
public function get_blocked_count()
{
if (null !== $this->blocked_count) {
return $this->blocked_count;
}
$count = wp_cache_get('blocked_ips_count', 'advaipbl');
if (false === $count) {
$this->limpiar_ips_expiradas();
global $wpdb;
$table_name = $wpdb->prefix . 'advaipbl_blocked_ips';
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
$count = (int) $wpdb->get_var("SELECT COUNT(id) FROM {$table_name}");
wp_cache_set('blocked_ips_count', $count, 'advaipbl', 300);
}
$this->blocked_count = $count;
return $this->blocked_count;
}
/**
* Gets the number of active attack signatures.
* @return int
*/
public function get_blocked_signatures_count()
{
global $wpdb;
$table_name = $wpdb->prefix . 'advaipbl_malicious_signatures';
$count = wp_cache_get('blocked_signatures_count', 'advaipbl');
if (false === $count) {
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
$count = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(id) FROM {$table_name} WHERE expires_at > %d", time()));
wp_cache_set('blocked_signatures_count', $count, 'advaipbl', 300);
}
return $count;
}
/**
* Gets the number of endpoints currently in lockdown.
* @return int
*/
public function get_blocked_endpoints_count()
{
global $wpdb;
$table_name = $wpdb->prefix . 'advaipbl_endpoint_lockdowns';
$count = wp_cache_get('blocked_endpoints_count', 'advaipbl');
if (false === $count) {
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
$wpdb->query($wpdb->prepare("DELETE FROM {$table_name} WHERE expires_at <= %d", time()));
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
$count = (int) $wpdb->get_var("SELECT COUNT(id) FROM {$table_name}");
wp_cache_set('blocked_endpoints_count', $count, 'advaipbl', 300);
}
return $count;
}
/**
* Gets the username of the current logged-in administrator.
*
* @return string The username or a default string if not available.
*/
public function get_current_admin_username()
{
$user = wp_get_current_user();
return ($user && $user->ID) ? $user->user_login : __('an unknown user', 'advanced-ip-blocker');
}
/**
* Adds an entry (IP or range) to the whitelist and ensures it is removed
* from all blocklists. This is the centralized method for this action.
*
* @param string $entry_to_whitelist The IP or range to whitelist.
* @param string $detail A description of why it was added.
* @return bool True if added successfully, false if it already existed.
*/
public function add_to_whitelist_and_unblock($entry_to_whitelist, $detail)
{
if (! $this->is_valid_ip_or_range($entry_to_whitelist)) {
return false;
}
$whitelist = get_option(self::OPTION_WHITELIST, []);
if (array_key_exists($entry_to_whitelist, $whitelist)) {
return false;
}
$whitelist[ $entry_to_whitelist ] = [ 'timestamp' => time(), 'detail' => $detail ];
update_option(self::OPTION_WHITELIST, $whitelist);
wp_cache_delete(self::OPTION_WHITELIST, 'options');
$this->desbloquear_ip($entry_to_whitelist);
if (! filter_var($entry_to_whitelist, FILTER_VALIDATE_IP)) {
$option_key_map = [
'geoblock' => self::OPTION_BLOCKED_GEO,
'honeypot' => self::OPTION_BLOCKED_HONEYPOT,
'user_agent' => self::OPTION_BLOCKED_USER_AGENT,
'manual' => self::OPTION_BLOCKED_MANUAL,
'404' => self::OPTION_BLOCKED_404,
'403' => self::OPTION_BLOCKED_403,
'login' => self::OPTION_BLOCKED_LOGIN,
'waf' => self::OPTION_BLOCKED_WAF,
'rate_limit' => self::OPTION_BLOCKED_RATE_LIMIT,
'asn' => self::OPTION_BLOCKED_ASN,
'xmlrpc_block' => self::OPTION_BLOCKED_XMLRPC,
'threat_score' => self::OPTION_BLOCKED_THREAT_SCORE,
];
foreach ($option_key_map as $type => $option_key) {
$list = get_option($option_key, []);
if (is_array($list) && array_key_exists($entry_to_whitelist, $list)) {
unset($list[ $entry_to_whitelist ]);
update_option($option_key, $list);
}
}
}
/* translators: %s is a placeholder */
$this->log_event(sprintf(__('Entry %1$s added to whitelist by %2$s.', 'advanced-ip-blocker'), $entry_to_whitelist, $this->get_current_admin_username()), 'info', is_array($entry_to_whitelist) ? wp_json_encode($entry_to_whitelist) : $entry_to_whitelist);
return true;
}
public function handle_clear_cache_action()
{
if (!isset($_GET['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'advaipbl_clear_location_cache_nonce')) {
wp_die('Invalid nonce.');
}
if (!current_user_can('advaipbl_manage_settings')) {
wp_die('Permission denied.');
}
global $wpdb;
$table_name = $wpdb->prefix . 'advaipbl_cache';
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter
$wpdb->query("TRUNCATE TABLE `{$table_name}`");
/* translators: %s is a placeholder */
$this->log_event(sprintf(__('User session location cache manually cleared by %s.', 'advanced-ip-blocker'), $this->get_current_admin_username()), 'info');
set_transient('advaipbl_admin_notice', ['message' => __('Location cache has been cleared.', 'advanced-ip-blocker'), 'type' => 'success'], 30);
wp_safe_redirect(wp_get_referer());
exit;
}
public function handle_revoke_vip_passes_action()
{
if (!isset($_GET['_wpnonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'advaipbl_revoke_vip_passes_nonce')) {
wp_die('Invalid nonce.');
}
if (!current_user_can('advaipbl_manage_settings')) {
wp_die('Permission denied.');
}
update_option('advaipbl_vip_salt_modifier', wp_generate_password(24, true, true));
/* translators: %s is a placeholder */
$this->log_event(sprintf(__('All Challenge VIP passes were manually revoked globally by %s.', 'advanced-ip-blocker'), $this->get_current_admin_username()), 'warning');
set_transient('advaipbl_admin_notice', ['message' => __('All VIP passes have been successfully revoked. Everyone will be required to pass the Security Challenge again.', 'advanced-ip-blocker'), 'type' => 'success'], 30);
wp_safe_redirect(wp_get_referer());
exit;
}
/**
* Handles the request to send a test email from the settings page.
*/
public function handle_send_test_email()
{
// 1. Security check: Nonce and user permissions.
if (! isset($_GET['_wpnonce']) || ! wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'advaipbl_send_test_email_nonce')) {
wp_die('Invalid nonce.');
}
if (! current_user_can('advaipbl_manage_settings')) {
wp_die('Permission denied.');
}
$to = ! empty($this->options['notification_email']) && is_email($this->options['notification_email']) ? $this->options['notification_email'] : get_option('admin_email');
$site_name = get_bloginfo('name');
$settings_url = admin_url('admin.php?page=advaipbl_settings_page&tab=settings&sub-tab=general_settings#section-notifications');
if (isset($this->notification_manager)) {
$sent = $this->notification_manager->send_test_email($to);
} else {
$sent = false;
}
if ($sent) {
$message = __('The setup guide has been sent successfully to your configured email address.', 'advanced-ip-blocker');
$type = 'success';
/* translators: %s is a placeholder */
$this->log_event(sprintf(__('Setup guide sent to %1$s by %2$s.', 'advanced-ip-blocker'), $to, $this->get_current_admin_username()), 'info');
} else {
$message = __('Failed to send the setup guide email. Please check your site\'s email configuration.', 'advanced-ip-blocker');
$type = 'error';
/* translators: %s is a placeholder */
$this->log_event(sprintf(__('FAILED to send setup guide to %1$s. Action by %2$s.', 'advanced-ip-blocker'), $to, $this->get_current_admin_username()), 'error');
}
set_transient('advaipbl_admin_notice', [ 'message' => $message, 'type' => $type ], 30);
wp_safe_redirect($settings_url);
exit;
}
public function handle_send_test_push()
{
check_admin_referer('advaipbl_send_test_push_nonce');
if (! current_user_can('advaipbl_manage_settings')) {
wp_die(esc_html__('Cheatin’ uh?', 'advanced-ip-blocker'));
}
$webhook_urls_str = $this->options['push_webhook_urls'] ?? '';
$webhook_urls = array_filter(array_map('trim', explode("\n", $webhook_urls_str)));
if (empty($webhook_urls)) {
wp_die(
'
';
printf(
wp_kses(
/* translators: %s is a placeholder */
__('Advanced IP Blocker: You are receiving a limited community threat feed (50,000 IPs). To increase your protection level to 100,000+ IPs, please go to the plugin settings and Register the AIB Network Integration.', 'advanced-ip-blocker'),
array('strong' => array(), 'a' => array('href' => array()))
),
esc_url(admin_url('admin.php?page=advaipbl_settings_page-settings&sub-tab=threat_intelligence#advaipbl-community-network-card'))
);
echo '
';
}
/**
* Renders a dropdown to select the number of items per page.
*
* @param int $current_per_page The currently selected number of items per page.
*/
public function render_per_page_selector($current_per_page)
{
$per_page_options = [ 20, 50, 100, 200 ];
?>
$column_key,
'order' => $next_order,
]);
$url = add_query_arg($url_params, admin_url('admin.php'));
echo '