200]); } // Chặn và điều hướng về trang chủ wp_safe_redirect(home_url()); exit; } // Nếu truy cập wp-admin khi chưa đăng nhập if (is_admin() && !is_user_logged_in() && !defined('DOING_AJAX')) { wp_safe_redirect(home_url()); exit; } } // Cập nhật lại các URL đăng nhập được sinh ra bởi WP 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(trim($aio_hide_login_url, '/')); if (!empty($redirect)) { $login_url = add_query_arg('redirect_to', urlencode($redirect), $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 ($path === 'wp-login.php' || strpos($path, 'wp-login.php') === 0) { return home_url(trim($aio_hide_login_url, '/') . substr($path, 12)); } return $url; } } /* ========================================================================== 2. ANTI-SPAM (CAPTCHA) ========================================================================== */ $aio_captcha_provider = aio_get_option('optCaptchaProvider', 'turnstile'); $aio_captcha_site = aio_get_option('optCaptchaSiteKey', ''); $aio_captcha_secret = aio_get_option('optCaptchaSecretKey', ''); $aio_captcha_login = !empty(aio_get_option('optCaptchaForLogin')); $aio_captcha_cf7 = !empty(aio_get_option('optCaptchaForCF7')); if (!empty($aio_captcha_site) && !empty($aio_captcha_secret)) { // Hàm render HTML Captcha function aio_render_captcha_html() { global $aio_captcha_provider, $aio_captcha_site; if ($aio_captcha_provider === 'turnstile') { echo ''; echo '
'; } else { echo ''; echo '
'; } } // Hàm xác thực Captcha function aio_verify_captcha_response($response_field) { global $aio_captcha_provider, $aio_captcha_secret; $response = isset($_POST[$response_field]) ? sanitize_text_field($_POST[$response_field]) : ''; if (empty($response)) { return false; } $verify_url = ($aio_captcha_provider === 'turnstile') ? 'https://challenges.cloudflare.com/turnstile/v0/siteverify' : 'https://www.google.com/recaptcha/api/siteverify'; $api_response = wp_remote_post($verify_url, array( 'body' => array( 'secret' => $aio_captcha_secret, 'response' => $response, 'remoteip' => $_SERVER['REMOTE_ADDR'] ) )); if (is_wp_error($api_response)) { return false; } $body = wp_remote_retrieve_body($api_response); $result = json_decode($body); return isset($result->success) && $result->success; } // 2.1 Tích hợp cho Login / Register 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'); // Xác thực khi Đăng nhập add_filter('wp_authenticate_user', 'aio_verify_captcha_login', 10, 2); function aio_verify_captcha_login($user, $password) { global $aio_captcha_provider; $field = ($aio_captcha_provider === 'turnstile') ? 'cf-turnstile-response' : 'g-recaptcha-response'; if (!isset($_POST['log'])) return $user; // Bỏ qua nếu không phải là post form login if (!aio_verify_captcha_response($field)) { return new WP_Error('captcha_error', '' . esc_html__('Error:', 'aio') . ' ' . esc_html__('CAPTCHA verification failed.', 'aio')); } return $user; } // Xác thực khi Đăng ký add_filter('registration_errors', 'aio_verify_captcha_register', 10, 3); function aio_verify_captcha_register($errors, $sanitized_user_login, $user_email) { global $aio_captcha_provider; $field = ($aio_captcha_provider === 'turnstile') ? 'cf-turnstile-response' : 'g-recaptcha-response'; if (!aio_verify_captcha_response($field)) { $errors->add('captcha_error', '' . esc_html__('Error:', 'aio') . ' ' . esc_html__('CAPTCHA verification failed.', 'aio')); } return $errors; } } // 2.2 Tích hợp cho Contact Form 7 if ($aio_captcha_cf7 && defined('WPCF7_VERSION')) { // Gắn html vào cuối form CF7 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(); // Chèn captcha vào trước thẻ submit $elements = preg_replace('/(]+type="submit"[^>]*>)/i', $captcha_html . '$1', $elements); return $elements; } // Xác thực form CF7 add_filter('wpcf7_validate', 'aio_cf7_verify_captcha', 20, 2); function aio_cf7_verify_captcha($result, $tags) { global $aio_captcha_provider; $field = ($aio_captcha_provider === 'turnstile') ? 'cf-turnstile-response' : 'g-recaptcha-response'; if (!aio_verify_captcha_response($field)) { $result->invalidate(new WPCF7_FormTag(array('name' => 'captcha', 'type' => 'captcha', 'basetype' => 'captcha')), esc_html__('Please verify CAPTCHA.', 'aio')); } return $result; } } } /* ========================================================================== 3. TWO-FACTOR AUTHENTICATION (2FA) ========================================================================== */ // Hiển thị trường nhập mã 2FA ở màn hình đăng nhập add_action('login_form', 'aio_render_2fa_field'); function aio_render_2fa_field() { ?> ID, '_aio_2fa_enabled', true); if ($is_2fa_enabled) { $required = true; } } wp_send_json_success(array('required' => $required)); } // Kiểm tra mã 2FA khi đăng nhập add_filter('wp_authenticate_user', 'aio_verify_2fa_login', 20, 2); function aio_verify_2fa_login($user, $password) { if ( is_wp_error($user) ) { return $user; } $is_2fa_enabled = get_user_meta($user->ID, '_aio_2fa_enabled', true); if ( !$is_2fa_enabled ) { return $user; // Bỏ qua nếu user chưa bật 2FA } $secret = get_user_meta($user->ID, '_aio_2fa_secret', true); if ( empty($secret) ) { return $user; // Lỗi logic, cho qua } $code = isset($_POST['aio_2fa_code']) ? sanitize_text_field($_POST['aio_2fa_code']) : ''; if ( empty($code) ) { return new WP_Error('aio_2fa_empty', '' . esc_html__('Error:', 'aio') . ' ' . esc_html__('This account requires a 2FA security code.', 'aio')); } if ( !class_exists('AIO_TOTP') ) { require_once plugin_dir_path(dirname(__FILE__)) . '../core/class-aio-totp.php'; } if ( !AIO_TOTP::verify_code($secret, $code) ) { return new WP_Error('aio_2fa_invalid', '' . esc_html__('Error:', 'aio') . ' ' . esc_html__('Invalid 2FA security code.', 'aio')); } return $user; } /* ========================================================================== 4. LIMIT LOGIN ATTEMPTS ========================================================================== */ if (!empty(aio_get_option('optEnableLimitLogin'))) { function aio_get_client_ip() { $ip = ''; if (!empty($_SERVER['HTTP_CF_CONNECTING_IP'])) { $ip = $_SERVER['HTTP_CF_CONNECTING_IP']; // Cloudflare } elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) { $ip = $_SERVER['HTTP_X_REAL_IP']; // Nginx Proxy } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $ip = trim($ips[0]); } else { $ip = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''; } // Validate IP format to prevent header injection/spoofing with invalid payloads if (!filter_var($ip, FILTER_VALIDATE_IP)) { $ip = !empty($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '127.0.0.1'; } return sanitize_text_field($ip); } add_filter('authenticate', 'aio_check_login_limit', 30, 3); function aio_check_login_limit($user, $username, $password) { $ip = aio_get_client_ip(); $lockout_key = 'aio_lockout_' . md5($ip); if (get_transient($lockout_key)) { $duration = (int) aio_get_option('optLimitLoginDuration', '15'); return new WP_Error('too_many_retries', sprintf(__('ERROR: Too many failed login attempts. Please try again in %d minutes.', 'aio'), $duration)); } return $user; } add_action('wp_login_failed', 'aio_login_failed'); function aio_login_failed($username) { $ip = aio_get_client_ip(); $attempts_key = 'aio_login_attempts_' . md5($ip); $lockout_key = 'aio_lockout_' . md5($ip); $max_attempts = (int) aio_get_option('optLimitLoginMax', '3'); $duration = (int) aio_get_option('optLimitLoginDuration', '15'); $attempts = (int) get_transient($attempts_key); $attempts++; if ($attempts >= $max_attempts) { set_transient($lockout_key, true, $duration * MINUTE_IN_SECONDS); delete_transient($attempts_key); // Reset counter // Log to wp_aio_lockouts for UI global $wpdb; $lockouts_table = $wpdb->prefix . 'aio_lockouts'; $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(); $attempts_key = 'aio_login_attempts_' . md5($ip); delete_transient($attempts_key); } } /* ========================================================================== 5. AUDIT LOG (NHẬT KÝ HỆ THỐNG) ========================================================================== */ 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 ($user_id === null) { $user = wp_get_current_user(); $user_id = $user->ID; $user_login = $user->user_login; } if (empty($user_login)) { $user_login = 'System/Guest'; } $ip = function_exists('aio_get_client_ip') ? aio_get_client_ip() : $_SERVER['REMOTE_ADDR']; $wpdb->insert($table_name, [ 'time' => current_time('mysql'), 'user_id' => $user_id, 'user_login' => $user_login, 'event' => $event, 'details' => $details, 'ip_address' => $ip ]); } 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) { $this->insert_log('Failed Login', "Failed login attempt for username: {$username}", 0, $username); } public function log_deleted_post($post_id) { $post = get_post($post_id); if ($post) { $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(); } /* ========================================================================== 9. DISABLE FILE EDITING ========================================================================== */ 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'])) { $caps[] = 'do_not_allow'; } return $caps; } } /* ========================================================================== 6. FILE INTEGRITY MONITOR (QUÉT TOÀN VẸN) ========================================================================== */ 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={$wp_version}&locale={$locale}"; $response = wp_remote_get($api_url); if (is_wp_error($response)) { return ['success' => false, 'message' => __('Cannot connect to WordPress API.', 'aio')]; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (empty($data['checksums']) || !is_array($data['checksums'])) { // Try fallback to en_US $api_url = "https://api.wordpress.org/core/checksums/1.0/?version={$wp_version}&locale=en_US"; $response = wp_remote_get($api_url); $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); } if (empty($data['checksums']) || !is_array($data['checksums'])) { return ['success' => false, 'message' => __('No checksum data found for this version.', 'aio')]; } $checksums = $data['checksums']; $modified_files = []; foreach ($checksums as $file => $expected_md5) { $local_file = ABSPATH . $file; // Skip wp-content as it's meant to be modified if (strpos($file, 'wp-content/') === 0) continue; // Skip wp-config-sample.php as it's often deleted or modified if ($file === 'wp-config-sample.php') continue; if (!file_exists($local_file)) { $modified_files[] = ['file' => $file, 'status' => 'missing']; continue; } $local_md5 = md5_file($local_file); if ($local_md5 !== $expected_md5) { $modified_files[] = ['file' => $file, 'status' => 'modified']; } } return ['success' => true, 'modified' => $modified_files]; } }