plugin = $plugin_instance; } /** * Verifica la respuesta de un desafío JS si se ha enviado. * Esta función se ejecuta antes que las reglas de bloqueo para procesar * la respuesta de un desafío y establecer la cookie de verificación. */ public function verify_submission() { // phpcs:ignore WordPress.Security.NonceVerification.Missing if (!isset($_POST['_advaipbl_js_token'])) { return; } // Si la IP goza de inmunidad global (Bot legítimo, ASN en lista blanca, IP en lista blanca o Regla Avanzada ALLOW), // ignoramos el submission del challenge fallido o caducado y le dejamos pasar. if ( !empty($this->plugin->request_is_asn_whitelisted) || $this->plugin->is_whitelisted($this->plugin->get_client_ip()) || !empty($this->plugin->is_advanced_rule_allowed) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Missing $challenge_type = sanitize_key($_POST['_advaipbl_challenge_type'] ?? 'signature'); $global_duration_hours = (int)($this->plugin->options['global_challenge_cookie_duration'] ?? 4); $cookie_duration = ($global_duration_hours > 0) ? $global_duration_hours * HOUR_IN_SECONDS : 0; if ($challenge_type === 'geo_challenge') { $duration_hours = (int)($this->plugin->options['geo_challenge_cookie_duration'] ?? 24); $cookie_duration = ($duration_hours > 0) ? $duration_hours * HOUR_IN_SECONDS : 0; } $this->verify_challenge('advaipbl_js_verified', $cookie_duration); } /** * Valida un Pase VIP criptografico (Cookie HMAC) * @param string $ip * @return bool */ public function is_vip_pass_valid($ip = null) { if (!$ip) { $ip = $this->plugin->get_client_ip(); } $cookie_name = 'advaipbl_js_verified'; if (!isset($_COOKIE[$cookie_name])) { return false; } $cookie_value = sanitize_text_field(wp_unslash($_COOKIE[$cookie_name])); // Rechazar cookies antiguas tipo '1' por seguridad (fuerza un nuevo desafio) if ($cookie_value === '1') { return false; } $decoded = base64_decode($cookie_value); if ($decoded === false || strpos($decoded, '|') === false) { return false; } list($expiration, $signature) = explode('|', $decoded, 2); // Check si ha expirado (0 = sesion) if ((int)$expiration !== 0 && time() > (int)$expiration) { return false; } // Verificar firma $salt = function_exists('wp_salt') ? wp_salt('auth') : (defined('AUTH_SALT') ? AUTH_SALT : 'fallback_salt_advaipbl'); $modifier = get_option('advaipbl_vip_salt_modifier', ''); $expected_signature = hash_hmac('sha256', $ip . $expiration, $salt . $modifier); return hash_equals($expected_signature, $signature); } /** * Verifica el token del desafío y establece la cookie si es correcto. * * @param string $cookie_name Nombre de la cookie a establecer. * @param int $cookie_duration Duración de la cookie en segundos. */ public function verify_challenge($cookie_name, $cookie_duration) { // Sanitize input // phpcs:ignore WordPress.Security.NonceVerification.Missing $token = sanitize_text_field(wp_unslash($_POST['_advaipbl_js_token'] ?? '')); // phpcs:ignore WordPress.Security.NonceVerification.Missing $response = (int) sanitize_text_field(wp_unslash($_POST['_advaipbl_js_response'] ?? 0)); $ip = $this->plugin->get_client_ip(); // Sanitizar el modo reportado // phpcs:ignore WordPress.Security.NonceVerification.Missing $mode_reported = sanitize_key($_POST['_advaipbl_challenge_mode'] ?? 'managed'); // Verification check $is_valid = false; $correct_answer = null; // Decode HMAC Token (Stateless Verification) if (!empty($token)) { $decoded = base64_decode(strtr($token, '-_', '+/')); if ($decoded !== false && strpos($decoded, '::') !== false) { list($payload, $signature) = explode('::', $decoded, 2); $salt = function_exists('wp_salt') ? wp_salt('auth') : (defined('AUTH_SALT') ? AUTH_SALT : 'fallback_salt_advaipbl'); $expected_signature = hash_hmac('sha256', $payload, $salt); if (hash_equals($expected_signature, $signature)) { $parts = explode('|', $payload); if (count($parts) === 2) { $parsed_answer = (int) $parts[0]; $expiration = (int) $parts[1]; if (time() <= $expiration) { $correct_answer = $parsed_answer; } } } } } if ($correct_answer !== null && $response === $correct_answer) { // Si el cliente reporta que es un challenge 'managed', verificamos el checkbox if ($mode_reported === 'managed') { // phpcs:ignore WordPress.Security.NonceVerification.Missing if (isset($_POST['human_check'])) { $is_valid = true; } } else { // Automático $is_valid = true; } } if ($is_valid) { set_transient('advaipbl_grace_pass_' . md5($ip), true, 15); $expiration = ($cookie_duration > 0) ? time() + $cookie_duration : 0; $request_uri = esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'] ?? '')); // Generar Pase VIP HMAC $salt = function_exists('wp_salt') ? wp_salt('auth') : (defined('AUTH_SALT') ? AUTH_SALT : 'fallback_salt_advaipbl'); $modifier = get_option('advaipbl_vip_salt_modifier', ''); $signature = hash_hmac('sha256', $ip . $expiration, $salt . $modifier); $cookie_value = base64_encode($expiration . '|' . $signature); if (defined('ADVAIPBL_EDGE_MODE') && ADVAIPBL_EDGE_MODE) { // En Edge Mode, limpiamos el buffer y usamos una redireccin de PHP nativa. if (ob_get_level()) { ob_end_clean(); } $cookie_options = [ 'expires' => $expiration, 'path' => '/', 'domain' => defined('COOKIE_DOMAIN') && COOKIE_DOMAIN ? COOKIE_DOMAIN : '', 'secure' => is_ssl(), 'httponly' => true, 'samesite' => 'Lax' ]; setcookie($cookie_name, $cookie_value, $cookie_options); wp_safe_redirect($request_uri, 303); exit; } else { // En modo normal de WordPress, usamos las funciones de WordPress. setcookie($cookie_name, $cookie_value, $expiration, '/', defined('COOKIE_DOMAIN') ? COOKIE_DOMAIN : '', is_ssl(), true); wp_safe_redirect($request_uri); exit; } } if ( ! defined( 'DONOTCACHEPAGE' ) ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound define( 'DONOTCACHEPAGE', true ); } header( 'Cache-Control: no-store, no-cache, must-revalidate, max-age=0' ); $this->plugin->log_event('JS challenge verification failed.', 'warning', ['ip' => $ip, 'token' => $token, 'response' => $response]); wp_die( esc_html__('Verification failed. Please ensure JavaScript and cookies are enabled in your browser.', 'advanced-ip-blocker'), esc_html__('Access Denied', 'advanced-ip-blocker'), ['response' => 403] ); } /** * Muestra la página del desafío JavaScript, optimizada para seguridad, UX y responsividad. * * @param string $challenge_type Un identificador para el desafío (ej. 'signature', 'geo_challenge'). * @param string $challenge_mode El modo del desafío ('managed' o 'automatic'). */ public function serve_challenge($challenge_type, $challenge_mode = 'managed') { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound if (!defined('DONOTCACHEPAGE')) define('DONOTCACHEPAGE', true); if (headers_sent()) { return; } // 1. Headers estándar HTTP/1.1 y HTTP/1.0 header('Cache-Control: private, no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0'); header('Pragma: no-cache'); header('Expires: Thu, 01 Jan 1970 00:00:01 GMT'); // 2. Headers específicos para proveedores de Caché/CDN conocidos // Cubre: LiteSpeed, Cloudflare, Fastly, Varnish header('X-LiteSpeed-Cache-Control: no-cache'); header('Cloudflare-CDN-Cache-Control: no-store'); header('CDN-Cache-Control: no-store'); header('Surrogate-Control: no-store'); // Cubre: NGINX FastCGI Cache (FlyingPress, WP Rocket NGINX mode, etc.) header('X-Accel-Expires: 0'); // Cubre: SG Optimizer y otros sistemas basados en headers header('X-Cache-Enabled: False'); // Nota: La constante DONOTCACHEPAGE (definida arriba) maneja: // WP Rocket, W3 Total Cache, WP Super Cache, WP Fastest Cache, Cache Enabler. // 3. Seguridad Standard header('X-Frame-Options: DENY'); header('X-Content-Type-Options: nosniff'); header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"); $num1 = wp_rand(1, 9); $num2 = wp_rand(1, 9); $answer = $num1 + $num2; // Stateless verification token (HMAC) to bypass Cache and Database limitations $expiration = time() + 300; $payload = $answer . '|' . $expiration; $salt = function_exists('wp_salt') ? wp_salt('auth') : (defined('AUTH_SALT') ? AUTH_SALT : 'fallback_salt_advaipbl'); $signature = hash_hmac('sha256', $payload, $salt); $token_raw = $payload . '::' . $signature; // URL-safe Base64 $token = strtr(base64_encode($token_raw), '+/', '-_'); status_header(503); header('Content-Type: text/html; charset=utf-8'); header('Retry-After: 10'); $protocol = is_ssl() ? 'https://' : 'http://'; $host = sanitize_text_field(wp_unslash($_SERVER['HTTP_HOST'] ?? '')); $uri = esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'] ?? '/')); $action_url = esc_url($protocol . $host . $uri); $site_host = wp_parse_url(home_url(), PHP_URL_HOST); $site_msg = esc_html__('needs to review the security of your connection before proceeding.', 'advanced-ip-blocker'); $site_title = get_bloginfo('name', 'display'); $page_title = esc_html__('Verifying your connection...', 'advanced-ip-blocker'); $main_heading = esc_html__('Security Check Required', 'advanced-ip-blocker'); $timer_text = esc_html__('Time remaining: ', 'advanced-ip-blocker'); $noscript_text = esc_html__('Please enable JavaScript to continue.', 'advanced-ip-blocker'); $button_text = esc_html__('Verify and Continue', 'advanced-ip-blocker'); $checkbox_label = esc_html__('I am not a robot.', 'advanced-ip-blocker'); $expired_heading = esc_html__('Session Expired', 'advanced-ip-blocker'); $expired_message = esc_html__('The security challenge has expired. Click the button below to get a new challenge.', 'advanced-ip-blocker'); $reload_button_text = esc_html__('Start New Challenge', 'advanced-ip-blocker'); if ($challenge_mode === 'automatic') { $main_text = esc_html__('Please wait while we verify your connection. This process is automatic and protects the website from automated attacks.', 'advanced-ip-blocker'); $js_script = ""; $form_content = '
'; } else { // Managed mode $main_text = esc_html__('To proceed, please prove you are human. This verification protects the website from automated attacks.', 'advanced-ip-blocker'); $js_script = ""; $form_content = ' '; } $html = '' . esc_html($site_host) . ' ' . $site_msg . '
' . $main_text . '
' . $expired_message . '
Performance & security by Advanced IP Blocker