protected function sanitize_settings() { $this->sanitize_setting('bool', 'default', __('Default Blacklist', 'better-wp-security')); $this->sanitize_setting('bool', 'enable_ban_lists', __('Ban Lists', 'better-wp-security')); $this->sanitize_setting('newline-separated-ips', 'host_list', __('Ban Hosts', 'better-wp-security')); if (is_array($this->settings['host_list'])) { require_once ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-ip-tools.php'; $whitelisted_hosts = array(); $current_ip = ITSEC_Lib::get_ip(); foreach ($this->settings['host_list'] as $host) { if (is_user_logged_in() && ITSEC_Lib_IP_Tools::intersect($current_ip, ITSEC_Lib_IP_Tools::ip_wild_to_ip_cidr($host))) { $this->set_can_save(false); /* translators: 1: input name, 2: invalid host */ $this->add_error(sprintf(__('The following host in %1$s matches your current IP and cannot be banned: %2$s', 'better-wp-security'), __('Ban Hosts', 'better-wp-security'), $host)); continue; } if (ITSEC_Lib::is_ip_whitelisted($host)) { $whitelisted_hosts[] = $host; } } if (!empty($whitelisted_hosts)) { $this->set_can_save(false); /* translators: 1: input name, 2: invalid host list */ $this->add_error(wp_sprintf(_n('The following IP in %1$s is whitelisted and cannot be banned: %2$l', 'The following IPs in %1$s are whitelisted and cannot be banned: %2$l', count($whitelisted_hosts), 'better-wp-security'), __('Ban Hosts', 'better-wp-security'), $whitelisted_hosts)); } } $this->sanitize_setting(array($this, 'sanitize_agent_list_entry'), 'agent_list', __('Ban User Agents', 'better-wp-security')); }
/** * Inserts an IP address into the htaccess ban list. * * @since 4.0 * * @param $ip * @param null $ban_list * @param null $white_list * * @return void */ public static function insert_ip($ip, $ban_list = null, $white_list = null) { $settings = get_site_option('itsec_ban_users'); $host = sanitize_text_field($ip); if ($ban_list === null) { $ban_list = isset($settings['host_list']) ? $settings['host_list'] : array(); } if ($white_list === null) { $global_settings = get_site_option('itsec_global'); $white_list = isset($global_settings['lockout_white_list']) ? $global_settings['lockout_white_list'] : array(); } if (!in_array($host, $ban_list) && !ITSEC_Lib::is_ip_whitelisted($host, $white_list)) { $ban_list[] = $host; $settings['host_list'] = $ban_list; ITSEC_Files::quick_ban($host); update_site_option('itsec_ban_users', $settings); add_site_option('itsec_rewrites_changed', true); } }
public static function get_server_config_ban_hosts_rules($server_type) { $host_list = ITSEC_Modules::get_setting('ban-users', 'host_list', array()); if (!is_array($host_list) || empty($host_list)) { return ''; } if (!class_exists('ITSEC_Lib_IP_Tools')) { require_once ITSEC_Core::get_core_dir() . '/lib/class-itsec-lib-ip-tools.php'; } $host_rules = ''; $set_env_rules = ''; $deny_rules = ''; $require_rules = ''; // process hosts list foreach ($host_list as $host) { $host = ITSEC_Lib_IP_Tools::ip_wild_to_ip_cidr(trim($host)); if (empty($host)) { continue; } if (ITSEC_Lib::is_ip_whitelisted($host)) { /** * @todo warn the user the ip to be banned is whitelisted */ continue; } if (in_array($server_type, array('apache', 'litespeed'))) { $converted_host = ITSEC_Lib_IP_Tools::ip_cidr_to_ip_regex($host); if (empty($converted_host)) { continue; } $set_env_rules .= "\tSetEnvIF REMOTE_ADDR \"^{$converted_host}\$\" DenyAccess\n"; // Ban IP $set_env_rules .= "\tSetEnvIF X-FORWARDED-FOR \"^{$converted_host}\$\" DenyAccess\n"; // Ban IP from a proxy $set_env_rules .= "\tSetEnvIF X-CLUSTER-CLIENT-IP \"^{$converted_host}\$\" DenyAccess\n"; // Ban IP from a load balancer $set_env_rules .= "\n"; $require_rules .= "\t\t\tRequire not ip {$host}\n"; $deny_rules .= "\t\tDeny from {$host}\n"; } else { if ('nginx' === $server_type) { $host_rules .= "\tdeny {$host};\n"; } } } $rules = ''; if ('apache' === $server_type) { if (!empty($set_env_rules)) { $rules .= "\n"; $rules .= "\t# " . __('Ban Hosts - Security > Settings > Banned Users', 'better-wp-security') . "\n"; $rules .= $set_env_rules; $rules .= "\t<IfModule mod_authz_core.c>\n"; $rules .= "\t\t<RequireAll>\n"; $rules .= "\t\t\tRequire all granted\n"; $rules .= "\t\t\tRequire not env DenyAccess\n"; $rules .= $require_rules; $rules .= "\t\t</RequireAll>\n"; $rules .= "\t</IfModule>\n"; $rules .= "\t<IfModule !mod_authz_core.c>\n"; $rules .= "\t\tOrder allow,deny\n"; $rules .= "\t\tAllow from all\n"; $rules .= "\t\tDeny from env=DenyAccess\n"; $rules .= $deny_rules; $rules .= "\t</IfModule>\n"; } } else { if ('litespeed' === $server_type) { if (!empty($set_env_rules)) { $rules .= "\n"; $rules .= "\t# " . __('Ban Hosts - Security > Settings > Banned Users', 'better-wp-security') . "\n"; $rules .= $set_env_rules; $rules .= "\t<IfModule mod_litespeed.c>\n"; $rules .= "\t\tOrder allow,deny\n"; $rules .= "\t\tAllow from all\n"; $rules .= "\t\tDeny from env=DenyAccess\n"; $rules .= $deny_rules; $rules .= "\t</IfModule>\n"; } } else { if ('nginx' === $server_type) { if (!empty($host_rules)) { $rules .= "\n"; $rules .= "\t# " . __('Ban Hosts - Security > Settings > Banned Users', 'better-wp-security') . "\n"; $rules .= $host_rules; } } } } return $rules; }
/** * Locks out given user or host * * @since 4.0 * * @param string $type The type of lockout (for user reference) * @param string $reason Reason for lockout, for notifications * @param string $host Host to lock out * @param int $user user id to lockout * @param string $username username to lockout * * @return void */ private function lockout($type, $reason, $host = null, $user = null, $username = null) { global $wpdb, $itsec_logger, $itsec_globals, $itsec_files; $host_expiration = null; $user_expiration = null; $username = sanitize_text_field(trim($username)); if ($itsec_files->get_file_lock('lockout_' . $host . $user . $username)) { //Do we have a good host to lock out or not if (!is_null($host) && ITSEC_Lib::is_ip_whitelisted(sanitize_text_field($host)) === false && ITSEC_Lib_IP_Tools::validate($host)) { $good_host = sanitize_text_field($host); } else { $good_host = false; } //Do we have a valid user to lockout or not if ($user !== null && ITSEC_Lib::user_id_exists(intval($user)) === true) { $good_user = intval($user); } else { $good_user = false; } //Do we have a valid username to lockout or not if ($username !== null && $username != '') { $good_username = $username; } else { $good_username = false; } $blacklist_host = false; //assume we're not permanently blcking the host //Sanitize the data for later $type = sanitize_text_field($type); $reason = sanitize_text_field($reason); //handle a permanent host ban (if needed) if (isset($itsec_globals['settings']['blacklist']) && $itsec_globals['settings']['blacklist'] === true && $good_host !== false) { //permanent blacklist $blacklist_period = isset($itsec_globals['settings']['blacklist_period']) ? $itsec_globals['settings']['blacklist_period'] * 24 * 60 * 60 : 604800; $host_count = 1 + $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `" . $wpdb->base_prefix . "itsec_lockouts` WHERE `lockout_expire_gmt` > '%s' AND `lockout_host`='%s';", date('Y-m-d H:i:s', $itsec_globals['current_time_gmt'] - $blacklist_period), $host)); if ($host_count >= $itsec_globals['settings']['blacklist_count'] && isset($itsec_globals['settings']['write_files']) && $itsec_globals['settings']['write_files'] === true) { $host_expiration = false; if (!class_exists('ITSEC_Ban_Users')) { require trailingslashit($itsec_globals['plugin_dir']) . 'core/modules/ban-users/class-itsec-ban-users.php'; } ITSEC_Ban_Users::insert_ip(sanitize_text_field($host)); //Send it to the Ban Users module for banning $blacklist_host = true; //flag it so we don't do a temp ban as well } } //We have temp bans to perform if ($good_host !== false || $good_user !== false || $good_username || $good_username !== false) { if (ITSEC_Lib::is_ip_whitelisted(sanitize_text_field($host))) { $whitelisted = true; $expiration = date('Y-m-d H:i:s', 1); $expiration_gmt = date('Y-m-d H:i:s', 1); } else { $whitelisted = false; $exp_seconds = intval($itsec_globals['settings']['lockout_period']) * 60; $expiration = date('Y-m-d H:i:s', $itsec_globals['current_time'] + $exp_seconds); $expiration_gmt = date('Y-m-d H:i:s', $itsec_globals['current_time_gmt'] + $exp_seconds); } if ($good_host !== false && $blacklist_host === false) { //temp lockout host $host_expiration = $expiration; $wpdb->insert($wpdb->base_prefix . 'itsec_lockouts', array('lockout_type' => $type, 'lockout_start' => date('Y-m-d H:i:s', $itsec_globals['current_time']), 'lockout_start_gmt' => date('Y-m-d H:i:s', $itsec_globals['current_time_gmt']), 'lockout_expire' => $expiration, 'lockout_expire_gmt' => $expiration_gmt, 'lockout_host' => sanitize_text_field($host))); $itsec_logger->log_event(__('lockout', 'better-wp-security'), 10, array('expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => $type), sanitize_text_field($host)); } if ($good_user !== false) { //blacklist host and temp lockout user $user_expiration = $expiration; $wpdb->insert($wpdb->base_prefix . 'itsec_lockouts', array('lockout_type' => $type, 'lockout_start' => date('Y-m-d H:i:s', $itsec_globals['current_time']), 'lockout_start_gmt' => date('Y-m-d H:i:s', $itsec_globals['current_time_gmt']), 'lockout_expire' => $expiration, 'lockout_expire_gmt' => $expiration_gmt, 'lockout_host' => '', 'lockout_user' => intval($user))); if ($whitelisted === false) { $itsec_logger->log_event('lockout', 10, array('expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => $type), '', '', intval($user)); } else { $itsec_logger->log_event('lockout', 10, array(__('White Listed', 'better-wp-security'), 'type' => $type), '', '', intval($user)); } } if ($good_username !== false) { //blacklist host and temp lockout username $user_expiration = $expiration; $wpdb->insert($wpdb->base_prefix . 'itsec_lockouts', array('lockout_type' => $type, 'lockout_start' => date('Y-m-d H:i:s', $itsec_globals['current_time']), 'lockout_start_gmt' => date('Y-m-d H:i:s', $itsec_globals['current_time_gmt']), 'lockout_expire' => $expiration, 'lockout_expire_gmt' => $expiration_gmt, 'lockout_host' => '', 'lockout_username' => $username)); if ($whitelisted === false) { $itsec_logger->log_event('lockout', 10, array('expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => $type), '', '', $username); } else { $itsec_logger->log_event('lockout', 10, array(__('White Listed', 'better-wp-security'), 'type' => $type), '', '', $username); } } if ($whitelisted === false) { if ($itsec_globals['settings']['email_notifications'] === true) { //send email notifications $this->send_lockout_email($good_host, $good_user, $good_username, $host_expiration, $user_expiration, $reason); } if ($good_host !== false) { $itsec_files->release_file_lock('lockout_' . $host . $user . $username); $this->execute_lock(); } else { $itsec_files->release_file_lock('lockout_' . $host . $user . $username); $this->execute_lock(true); } } } $itsec_files->release_file_lock('lockout_' . $host . $user . $username); } }
/** * Inserts an IP address into the htaccess ban list. * * @since 4.0 * * @param $ip * * @return boolean False if the IP is whitelisted, true otherwise. */ public function blacklist_ip($ip) { $ip = sanitize_text_field($ip); if (ITSEC_Lib::is_ip_blacklisted($ip)) { // Already blacklisted. return true; } if (ITSEC_Lib::is_ip_whitelisted($ip)) { // Cannot blacklist a whitelisted IP. return false; } // The following action allows modules to handle the blacklist as needed. This is primarily useful for the Ban // Users module. do_action('itsec-new-blacklisted-ip', $ip); return true; }
/** * Sanitize and validate input * * @param Array $input array of input fields * * @return Array Sanitized array */ public function sanitize_module_input($input) { if (!class_exists('ITSEC_Lib_IP_Tools')) { $itsec_core = ITSEC_Core::get_instance(); require_once dirname($itsec_core->get_plugin_file()) . '/core/lib/class-itsec-lib-ip-tools.php'; } global $itsec_globals; $has_errors = false; //Sanitize checkbox features $input['enabled'] = isset($input['enabled']) && intval($input['enabled'] == 1) ? true : false; $input['default'] = isset($input['default']) && intval($input['default'] == 1) ? true : false; if (isset($input['agent_list']) && is_string($input['agent_list'])) { $agents = preg_split('/(?<!\\r)\\n|\\r(?!\\n)|(?<!\\r)\\r\\n|\\r\\r\\n/', trim($input['agent_list'])); } else { if (isset($input['agent_list']) && is_array($input['agent_list'])) { $agents = $input['agent_list']; } else { $agents = array(); } } $good_agents = array(); foreach ($agents as $agent) { $agent = trim(sanitize_text_field($agent)); if (!empty($agent)) { $good_agents[] = $agent; } } $input['agent_list'] = array_unique($good_agents); if (isset($input['host_list']) && is_string($input['host_list'])) { $addresses = preg_split('/(?<!\\r)\\n|\\r(?!\\n)|(?<!\\r)\\r\\n|\\r\\r\\n/', trim($input['host_list'])); } else { if (isset($input['host_list']) && is_array($input['host_list'])) { $addresses = $input['host_list']; } else { $addresses = array(); } } if (!class_exists('ITSEC_Ban_Users')) { require dirname(__FILE__) . '/class-itsec-ban-users.php'; } $bad_ips = array(); $white_ips = array(); $raw_ips = array(); foreach ($addresses as $index => $address) { $address = trim($address); if (empty($address)) { continue; } //Store the original user supplied IP for use in error messages or to fill back into the list if invalid $original_address = $address; // This checks validity and converts wildcard notation to standard CIDR notation $address = ITSEC_Lib_IP_Tools::ip_wild_to_ip_cidr($address); if (!$address) { // Put the address back to the original so it's not removed from the list $address = $original_address; $bad_ips[] = trim(filter_var($address, FILTER_SANITIZE_STRING)); } if (ITSEC_Lib::is_ip_whitelisted($address, null, true)) { $white_ips[] = trim(filter_var($address, FILTER_SANITIZE_STRING)); } $raw_ips[] = trim(filter_var($address, FILTER_SANITIZE_STRING)); } $raw_ips = array_unique($raw_ips); if (!empty($bad_ips)) { $input['enabled'] = false; //disable ban users list $type = 'error'; if (!$has_errors) { $message = sprintf('%s<br /><br />', __('Note that the ban users feature has been disabled until the following errors are corrected:', 'better-wp-security')); } foreach ($bad_ips as $bad_ip) { $message .= sprintf('%s %s<br />', $bad_ip, __('is not a valid address in the ban users box.', 'better-wp-security')); } add_settings_error('itsec', esc_attr('settings_updated'), $message, $type); $has_errors = true; } if (sizeof($white_ips) > 0) { $input['enabled'] = false; //disable ban users list $type = 'error'; if (!$has_errors) { $message = sprintf('%s<br /><br />', __('Note that the ban users feature has been disabled until the following errors are corrected:', 'better-wp-security')); } foreach ($white_ips as $white_ip) { $message .= sprintf('%s %s<br />', $white_ip, __('is not a valid address as it has been white listed.', 'better-wp-security')); } add_settings_error('itsec', esc_attr('settings_updated'), $message, $type); $has_errors = true; } $input['host_list'] = $raw_ips; if (!$has_errors) { if (!isset($type) && ($input['host_list'] !== $this->settings['host_list'] || $input['enabled'] !== $this->settings['enabled'] || $input['default'] !== $this->settings['default'] || $input['agent_list'] !== $this->settings['agent_list']) || isset($itsec_globals['settings']['write_files']) && true === $itsec_globals['settings']['write_files']) { add_site_option('itsec_rewrites_changed', true); } } if (is_multisite()) { if (isset($type)) { $error_handler = new WP_Error(); $error_handler->add($type, $message); $this->core->show_network_admin_notice($error_handler); } else { $this->core->show_network_admin_notice(false); } $this->settings = $input; } return $input; }
/** * Send offending IP to IPCheck API * * @since 4.5 * * @param string|null $ip ip to report * @param int $type type of behavior to report * * @return int -1 on failure, 0 if report successful and IP not blocked, 1 if IP successful and IP blocked */ public function report_ip($ip = null, $type = 1) { global $itsec_globals, $itsec_logger; $action = 'report-ip'; /** * Switch types or return false if no valid type * * Valid types: * 1 = invalid/failed login * */ switch ($type) { case 1: $behavior = 'brute-force-login'; break; default: return -1; } //get current IP if needed if ($ip === null) { $ip = ITSEC_Lib::get_ip(); } else { $ip = trim(sanitize_text_field($ip)); } if (ITSEC_Lib::is_ip_whitelisted($ip)) { return 0; } if (ITSEC_Lib_IP_Tools::validate($ip)) { //verify IP address is valid if (!isset($this->settings['api_key']) || !isset($this->settings['api_secret'])) { return -1; //invalid key or secret } $args = json_encode(array('apikey' => $this->settings['api_key'], 'behavior' => $behavior, 'ip' => $ip, 'site' => home_url('', 'http'), 'timestamp' => $itsec_globals['current_time_gmt'])); //Build the request parameters $request = array('body' => array('request' => $args, 'signature' => $this->hmac_SHA1($this->settings['api_secret'], $action . $args))); $response = wp_remote_post($this->endpoint . $action, $request); //Make sure the request was valid and has a valid body if (!is_wp_error($response) && isset($response['body'])) { $response = json_decode($response['body'], true); if (is_array($response) && isset($response['success']) && $response['success'] == true) { if (isset($response['block']) && $response['block'] == true) { $cache = isset($response['cache_ttl']) ? absint($response['cache_ttl']) : 3600; $expiration = date('Y-m-d H:i:s', $itsec_globals['current_time'] + $cache); $expiration_gmt = date('Y-m-d H:i:s', $itsec_globals['current_time_gmt'] + $cache); $itsec_logger->log_event('lockout', 10, array('expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => 'host'), $ip); $this->cache_ip($ip, array('status' => true), $cache); return 1; //ip report success. Just return true for now } else { return 0; } } } } return -1; }