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; } // phpcs:ignore WordPress.Security.NonceVerification.Missing $challenge_type = sanitize_key($_POST['_advaipbl_challenge_type'] ?? 'signature'); $cookie_duration = 4 * HOUR_IN_SECONDS; // Default 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; } elseif ($challenge_type === 'endpoint') { $cookie_duration = 1 * HOUR_IN_SECONDS; } $this->verify_challenge('advaipbl_js_verified', $cookie_duration); } /** * 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_key($_POST['_advaipbl_js_token'] ?? ''); // phpcs:ignore WordPress.Security.NonceVerification.Missing $response = (int) sanitize_text_field(wp_unslash($_POST['_advaipbl_js_response'] ?? 0)); $transient_key = 'advaipbl_challenge_' . $token; // Fetch and immediately delete the transient for single-use security $correct_answer = get_transient($transient_key); delete_transient($transient_key); // Ensures the token is used only once $ip = $this->plugin->get_client_ip(); // Verification check if ($correct_answer !== false && $response === (int) $correct_answer) { 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'] ?? '')); if (defined('ADVAIPBL_EDGE_MODE') && ADVAIPBL_EDGE_MODE) { // En Edge Mode, limpiamos el buffer y usamos una redirección 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, '1', $cookie_options); wp_safe_redirect($request_uri, 303); exit; } else { // En modo normal de WordPress, usamos las funciones de WordPress. setcookie($cookie_name, '1', $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'). */ public function serve_challenge($challenge_type) { // 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; $token = wp_generate_password(32, false); set_transient('advaipbl_challenge_' . $token, $answer, 120); 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'); $main_text = esc_html__('To proceed, please prove you are human. This verification protects the website from automated attacks.', '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'); $js_script = ""; $html = '
' . esc_html($site_host) . ' ' . $site_msg . '
' . $main_text . '
' . $expired_message . '
Performance & security by Advanced IP Blocker