Phase 6: AIOS security plugin with conservative login lockdown config (10 attempts)

This commit is contained in:
Hanson.xyz Dev
2025-11-28 17:19:54 -06:00
parent 78a744ef06
commit abbd3502e8
430 changed files with 137111 additions and 7 deletions
@@ -0,0 +1,561 @@
<?php
if (!defined('ABSPATH')) die('No direct access allowed');
if (trait_exists('AIOWPSecurity_Brute_Force_Commands_Trait')) return;
trait AIOWPSecurity_Brute_Force_Commands_Trait {
/**
* Perform saving rename login settings
*
* @param array $data - the request data contains PHP settings
*
* @return array
*/
public function perform_rename_login_page($data) {
global $aio_wp_security;
$success = true;
$options = array();
$args = array();
$aiowps_login_page_slug = '';
$error = '';
if ('' == $data['aiowps_login_page_slug'] && isset($data["aiowps_enable_rename_login_page"])) {
$error = __('Please enter a value for your login page slug.', 'all-in-one-wp-security-and-firewall');
} elseif ('' != $data['aiowps_login_page_slug']) {
$aiowps_login_page_slug = sanitize_text_field($data['aiowps_login_page_slug']);
if ('wp-admin' == $aiowps_login_page_slug) {
$error = '<br>' . __('You cannot use the value "wp-admin" for your login page slug.', 'all-in-one-wp-security-and-firewall');
} elseif (preg_match('/[^\p{L}\p{N}_\-]/u', $aiowps_login_page_slug)) {
$error = '<br>' . __('You must use alphanumeric characters for your login page slug.', 'all-in-one-wp-security-and-firewall');
}
}
if ($error) {
$success = false;
$message = $error;
} else {
$options['aiowps_enable_rename_login_page'] = isset($data["aiowps_enable_rename_login_page"]) ? '1' : '';
$options['aiowps_login_page_slug'] = $aiowps_login_page_slug;
$this->save_settings($options);
if (get_option('permalink_structure')) {
$home_url = trailingslashit(home_url());
} else {
$home_url = trailingslashit(home_url()) . '?';
}
$message = __('The settings have been successfully updated.', 'all-in-one-wp-security-and-firewall');
$args['badges'] = array("bf-rename-login-page");
$args['content'] = array('aios-rename-login-notice' => $aio_wp_security->include_template('wp-admin/brute-force/partials/rename-login-notice.php', true, array('home_url' => $home_url)));
}
return $this->handle_response($success, $message, $args);
}
/**
* Handles the AJAX request to enable or configure cookie-based brute force prevention.
*
* @param array $data The data received from the AJAX request.
*
* @return array The response containing the status, message, and badge.
*/
public function perform_cookie_based_brute_force_prevention($data) {
global $aio_wp_security;
$options = array();
$values = array();
$info = array();
$success = true;
$message = '';
$result = '';
if (isset($data['aiowps_enable_brute_force_attack_prevention'])) {
$brute_force_feature_secret_word = sanitize_text_field($data['aiowps_brute_force_secret_word']);
$redirect_url = sanitize_text_field($data['aiowps_cookie_based_brute_force_redirect_url']);
if (empty($brute_force_feature_secret_word)) {
$brute_force_feature_secret_word = AIOS_DEFAULT_BRUTE_FORCE_FEATURE_SECRET_WORD;
$info[] = __('You entered an invalid value for the secret word.', 'all-in-one-wp-security-and-firewall'). ' ' . __('It has been set to the default value.', 'all-in-one-wp-security-and-firewall');
} elseif (!ctype_alnum($brute_force_feature_secret_word)) {
$message = '<p>' . __('Settings have not been saved - your secret word must consist only of alphanumeric characters i.e., letters and/or numbers only.', 'all-in-one-wp-security-and-firewall') . '</p>';
$success = false;
}
if (filter_var($redirect_url, FILTER_VALIDATE_URL)) {
$redirect_url = esc_url_raw($redirect_url);
} else {
$redirect_url = 'http://127.0.0.1';
$info[] = __('You entered an invalid value for the redirect url.', 'all-in-one-wp-security-and-firewall'). ' ' . __('It has been set to the default value.', 'all-in-one-wp-security-and-firewall');
}
$options['aiowps_cookie_based_brute_force_redirect_url'] = $redirect_url;
if ($success) {
$options['aiowps_enable_brute_force_attack_prevention'] = '1';
$options['aiowps_brute_force_secret_word'] = $brute_force_feature_secret_word;
$result = '<p>' . __('You have successfully enabled the cookie based brute force prevention feature', 'all-in-one-wp-security-and-firewall') . '</p>';
$result .= '<p>' . __('From now on you will need to log into your WP Admin using the following URL:', 'all-in-one-wp-security-and-firewall') . '</p>';
$result .= '<p><strong>'.AIOWPSEC_WP_URL.'/?'.esc_html($brute_force_feature_secret_word).'=1</strong></p>';
$result .= '<p>' . __('It is important that you save this URL value somewhere in case you forget it, OR,', 'all-in-one-wp-security-and-firewall') . '</p>';
$result .= '<p>' . sprintf(__('simply remember to add a "?%s=1" to your current site URL address.', 'all-in-one-wp-security-and-firewall'), esc_html($brute_force_feature_secret_word)) . '</p>';
AIOWPSecurity_Utility::set_cookie_value(AIOWPSecurity_Utility::get_brute_force_secret_cookie_name(), AIOS_Helper::get_hash($brute_force_feature_secret_word));
}
} else {
$options['aiowps_enable_brute_force_attack_prevention'] = '';
$message = __('You have successfully saved cookie based brute force prevention feature settings.', 'all-in-one-wp-security-and-firewall');
$brute_force_feature_secret_word = $aio_wp_security->configs->get_value('aiowps_brute_force_secret_word');
$redirect_url = $aio_wp_security->configs->get_value('aiowps_cookie_based_brute_force_redirect_url');
}
$options['aiowps_brute_force_attack_prevention_pw_protected_exception'] = isset($data['aiowps_brute_force_attack_prevention_pw_protected_exception']) ? '1' : '';
$options['aiowps_brute_force_attack_prevention_ajax_exception'] = isset($data['aiowps_brute_force_attack_prevention_ajax_exception']) ? '1' : '';
if ($success) {
$this->save_settings($options);
AIOWPSecurity_Configure_Settings::set_cookie_based_bruteforce_firewall_configs();
$message = __('The settings have been successfully updated.', 'all-in-one-wp-security-and-firewall');
$values['aiowps_brute_force_secret_word'] = $brute_force_feature_secret_word;
$values['aiowps_cookie_based_brute_force_redirect_url'] = $redirect_url;
}
$content = array(
'aios-brute-force-info-box' => $result
);
$badges = array("firewall-enable-brute-force-attack-prevention");
$args = array(
'badges' => $badges,
'info' => $info,
'values' => $values,
'content' => $content
);
return $this->handle_response($success, $message, $args);
}
/**
* Handles the AJAX request for performing cookie test.
*
* @return array The response containing the status, message, and badge.
*/
public function perform_cookie_test() {
global $aio_wp_security;
$success = true;
$random_suffix = AIOWPSecurity_Utility::generate_alpha_numeric_random_string(10);
$test_cookie_name = 'aiowps_cookie_test_'.$random_suffix;
$aio_wp_security->configs->set_value('aiowps_cookie_brute_test', $test_cookie_name, true);
$set_cookie = AIOWPSecurity_Utility::set_cookie_value($test_cookie_name, '1');
$aiowps_cookie_test_success = '';
$args = array();
if ($set_cookie) {
$aiowps_cookie_test_success = '1';
$message = __('The cookie test was successful, you can now enable this feature.', 'all-in-one-wp-security-and-firewall');
$result = '<div class="aio_green_box"><p>' . __('The cookie test was successful, you can now enable this feature.', 'all-in-one-wp-security-and-firewall') . '</p></div>';
} else {
$success = false;
$message = __('The cookie test failed.', 'all-in-one-wp-security-and-firewall') .' '. __('Consequently, this feature cannot be used on this site.', 'all-in-one-wp-security-and-firewall');
$result = '<div class="aio_red_box"><p>' . __('The cookie test failed on this server.', 'all-in-one-wp-security-and-firewall') .' '. __('Consequently, this feature cannot be used on this site.', 'all-in-one-wp-security-and-firewall') . '</p></div>';
}
$this->save_settings(array('aiowps_cookie_test_success' => $aiowps_cookie_test_success)); // save the value
$args['content'] = array(
'aios-perform-cookie-test-div' => $this->get_perform_cookie_test_content(),
'cookie-test-result-div' => $result
);
return $this->handle_response($success, $message, $args);
}
/**
* Handles the AJAX request to enable or configure login whitelist settings.
*
* @param array $data The data received from the AJAX request.
*
* @return array The response containing the status, message, and badge.
*/
public function perform_login_whitelist_settings($data) {
global $aio_wp_security;
$success = true;
$options = array();
$message = '';
if (!empty($data['aiowps_allowed_ip_addresses'])) {
$ip_addresses = sanitize_textarea_field(stripslashes($data['aiowps_allowed_ip_addresses']));
$ip_list_array = AIOWPSecurity_Utility_IP::create_ip_list_array_from_string_with_newline($ip_addresses);
$validated_ip_list_array = AIOWPSecurity_Utility_IP::validate_ip_list($ip_list_array, 'whitelist');
if (is_wp_error($validated_ip_list_array)) {
$result = -1;
$success = false;
$message = nl2br($validated_ip_list_array->get_error_message());
} else {
$result = 1;
$whitelist_ip_data = implode("\n", $validated_ip_list_array);
$options['aiowps_allowed_ip_addresses'] = $whitelist_ip_data;
}
} else {
$result = 1;
$options['aiowps_allowed_ip_addresses'] = ''; // Clear the IP address config value
}
if (1 == $result) {
$options['aiowps_enable_whitelisting'] = isset($data["aiowps_enable_whitelisting"]) ? '1' : '';
if ('1' == $aio_wp_security->configs->get_value('aiowps_is_login_whitelist_disabled_on_upgrade')) {
$aio_wp_security->configs->delete_value('aiowps_is_login_whitelist_disabled_on_upgrade');
}
$this->save_settings($options);
}
$args = array(
'badges' => array('whitelist-manager-ip-login-whitelisting')
);
return $this->handle_response($success, $message, $args);
}
/**
* Handles the AJAX request to enable or configure honeypot brute force settings.
*
* @param array $data The data received from the AJAX request.
*
* @return array The response containing the status, message, and badge.
*/
public function perform_honeypot_settings($data) {
$options = array();
// Save all the form values to the options
$options['aiowps_enable_login_honeypot'] = isset($data["aiowps_enable_login_honeypot"]) ? '1' : '';
$options['aiowps_enable_registration_honeypot'] = isset($data["aiowps_enable_registration_honeypot"]) ? '1' : '';
$this->save_settings($options);
$args = array(
'badges' => array('login-honeypot', 'registration-honeypot')
);
return $this->handle_response(true, '', $args);
}
/**
* Handles the AJAX request to enable or configure captcha settings.
*
* @param array $data The data received from the AJAX request.
*
* @return array The response containing the status, message, and badge.
*/
public function perform_captcha_settings($data) {
global $aio_wp_security;
$captcha_themes = $aio_wp_security->captcha_obj->get_captcha_themes();
$supported_captchas = $aio_wp_security->captcha_obj->get_supported_captchas();
$options = array();
$default_captcha = isset($data['aiowps_default_captcha']) ? sanitize_text_field($data['aiowps_default_captcha']) : '';
$default_captcha = array_key_exists($default_captcha, $supported_captchas) ? $default_captcha : 'none';
$options['aiowps_default_captcha'] = $default_captcha;
// Save all the form values to the options
$random_20_digit_string = AIOWPSecurity_Utility::generate_alpha_numeric_random_string(20); // Generate random 20 char string for use during CAPTCHA encode/decode
$options['aiowps_captcha_secret_key'] = $random_20_digit_string;
$options['aiowps_enable_login_captcha'] = isset($data["aiowps_enable_login_captcha"]) ? '1' : '';
$options['aiowps_enable_registration_page_captcha'] = isset($data["aiowps_enable_registration_page_captcha"]) ? '1' : '';
$options['aiowps_enable_comment_captcha'] = isset($data["aiowps_enable_comment_captcha"]) ? '1' : '';
$options['aiowps_enable_bp_register_captcha'] = isset($data["aiowps_enable_bp_register_captcha"]) ? '1' : '';
$options['aiowps_enable_bbp_new_topic_captcha'] = isset($data["aiowps_enable_bbp_new_topic_captcha"]) ? '1' : '';
$options['aiowps_enable_woo_login_captcha'] = isset($data["aiowps_enable_woo_login_captcha"]) ? '1' : '';
$options['aiowps_enable_woo_register_captcha'] = isset($data["aiowps_enable_woo_register_captcha"]) ? '1' : '';
$options['aiowps_enable_woo_lostpassword_captcha'] = isset($data["aiowps_enable_woo_lostpassword_captcha"]) ? '1' : '';
$options['aiowps_enable_woo_checkout_captcha'] = isset($data["aiowps_enable_woo_checkout_captcha"]) ? '1' : '';
$options['aiowps_enable_custom_login_captcha'] = isset($data["aiowps_enable_custom_login_captcha"]) ? '1' : '';
$options['aiowps_enable_lost_password_captcha'] = isset($data["aiowps_enable_lost_password_captcha"]) ? '1' : '';
$options['aiowps_enable_contact_form_7_captcha'] = isset($data["aiowps_enable_contact_form_7_captcha"]) ? '1' : '';
$options['aiowps_enable_password_protected_captcha'] = isset($data["aiowps_enable_password_protected_captcha"]) ? '1' : '';
$options['aiowps_turnstile_site_key'] = sanitize_text_field(stripslashes($data['aiowps_turnstile_site_key']));
$options['aiowps_recaptcha_site_key'] = sanitize_text_field(stripslashes($data['aiowps_recaptcha_site_key']));
$turnstile_theme = isset($data['aiowps_turnstile_theme']) ? sanitize_text_field($data['aiowps_turnstile_theme']) : '';
$turnstile_theme = array_key_exists($turnstile_theme, $captcha_themes) ? $turnstile_theme : 'auto';
$options['aiowps_turnstile_theme'] = $turnstile_theme;
// If secret key is masked then don't resave it
$turnstile_secret_key = sanitize_text_field($data['aiowps_turnstile_secret_key']);
if (strpos($turnstile_secret_key, '********') === false) {
$options['aiowps_turnstile_secret_key'] = $turnstile_secret_key;
}
// If secret key is masked then don't resave it
$recaptcha_secret_key = sanitize_text_field($data['aiowps_recaptcha_secret_key']);
if (strpos($recaptcha_secret_key, '********') === false) {
$options['aiowps_recaptcha_secret_key'] = $recaptcha_secret_key;
}
if ('google-recaptcha-v2' == $aio_wp_security->configs->get_value('aiowps_default_captcha') && false === $aio_wp_security->captcha_obj->google_recaptcha_verify_configuration($aio_wp_security->configs->get_value('aiowps_recaptcha_site_key'), $aio_wp_security->configs->get_value('aiowps_recaptcha_secret_key'))) {
$options['aios_google_recaptcha_invalid_configuration'] = '1';
} elseif ('1' == $aio_wp_security->configs->get_value('aios_google_recaptcha_invalid_configuration')) {
$aio_wp_security->configs->delete_value('aios_google_recaptcha_invalid_configuration');
}
$this->save_settings($options);
$success = false;
$message = '';
if ('cloudflare-turnstile' == $aio_wp_security->configs->get_value('aiowps_default_captcha') && false === $aio_wp_security->captcha_obj->cloudflare_turnstile_verify_configuration($aio_wp_security->configs->get_value('aiowps_turnstile_site_key'), $aio_wp_security->configs->get_value('aiowps_turnstile_secret_key'))) {
$message = __('Your Cloudflare Turnstile configuration is invalid.', 'all-in-one-wp-security-and-firewall').' '.__('Please enter the correct Cloudflare Turnstile keys below to use the Turnstile feature.', 'all-in-one-wp-security-and-firewall');
} elseif ('google-recaptcha-v2' == $aio_wp_security->configs->get_value('aiowps_default_captcha') && '1' == $aio_wp_security->configs->get_value('aios_google_recaptcha_invalid_configuration')) {
$message = __('Your Google reCAPTCHA configuration is invalid.', 'all-in-one-wp-security-and-firewall').' '.__('Please enter the correct reCAPTCHA keys below to use the reCAPTCHA feature.', 'all-in-one-wp-security-and-firewall');
} else {
$success = true;
}
$features = array(
"user-login-captcha",
"user-registration-captcha",
"lost-password-captcha",
"custom-login-captcha",
"comment-form-captcha",
"password_protected-captcha",
);
if (AIOWPSecurity_Utility::is_woocommerce_plugin_active()) {
$woocommerce_features = array(
"woo-login-captcha",
"woo-lostpassword-captcha",
"woo-register-captcha",
"woo-checkout-captcha",
);
$features = array_merge($features, $woocommerce_features);
}
if (AIOWPSecurity_Utility::is_buddypress_plugin_active()) {
$features[] = "bp-register-captcha";
}
if (AIOWPSecurity_Utility::is_bbpress_plugin_active()) {
$features[] = "bbp-new-topic-captcha";
}
if (AIOWPSecurity_Utility::is_contact_form_7_plugin_active()) {
$features[] = "contact-form-7-captcha";
}
$args = array(
'badges' => $features
);
return $this->handle_response($success, $message, $args);
}
/**
* Handles the AJAX request to enable or configure 404 detection and settings.
*
* @param array $data The data received from the AJAX request.
*
* @return array The response containing the status, message, and badge.
*/
public function perform_404_settings($data) {
$options = array();
$info = array();
$values = array();
$options['aiowps_enable_404_logging'] = isset($data["aiowps_enable_404_IP_lockout"]) ? '1' : ''; //the "aiowps_enable_404_IP_lockout" checkbox currently controls both the 404 lockout and 404 logging
$options['aiowps_enable_404_IP_lockout'] = isset($data["aiowps_enable_404_IP_lockout"]) ? '1' : '';
$lockout_time_length = isset($data['aiowps_404_lockout_time_length']) ? sanitize_text_field($data['aiowps_404_lockout_time_length']) : '';
$redirect_url = isset($data['aiowps_404_lock_redirect_url']) ? sanitize_text_field(trim($data['aiowps_404_lock_redirect_url'])) : '';
if (isset($data["aiowps_enable_404_IP_lockout"])) {
if (!is_numeric($lockout_time_length) || $lockout_time_length < 1) {
$info[] = __('You entered a non numeric or negative value for the lockout time length field.', 'all-in-one-wp-security-and-firewall'). ' ' . __('It has been set to the default value.', 'all-in-one-wp-security-and-firewall');
$lockout_time_length = '60'; // Set it to the default value for this field
}
if ('' == $redirect_url || '' == esc_url($redirect_url, array('http', 'https'))) {
$info[] = __('You entered an incorrect format for the "Redirect URL" field.', 'all-in-one-wp-security-and-firewall') . ' ' . __('It has been set to the default value.', 'all-in-one-wp-security-and-firewall');
$redirect_url = 'http://127.0.0.1';
}
}
$options['aiowps_404_lockout_time_length'] = absint($lockout_time_length);
$options['aiowps_404_lock_redirect_url'] = $redirect_url;
$this->save_settings($options);
$badges = array("firewall-enable-404-blocking");
$values['aiowps_404_lockout_time_length'] = $lockout_time_length;
$values['aiowps_404_lock_redirect_url'] = $redirect_url;
$args = array(
'badges' => $badges,
'info' => $info,
'values' => $values
);
return $this->handle_response(true, '', $args);
}
/**
* Handles the AJAX request to clear 404 logs.
*
* @return array The response containing the status, message, and badge.
*/
public function perform_delete_404_event_records() {
global $aio_wp_security, $wpdb;
$success = true;
$events_table_name = AIOWPSEC_TBL_EVENTS;
//Delete all 404 records from the events table
$where = array('event_type' => '404');
$result = $wpdb->delete($events_table_name, $where);
if (false === $result) {
$error = empty($wpdb->last_error) ? '' : $wpdb->last_error;
$aio_wp_security->debug_logger->log_debug("404 Detection Feature - Delete all 404 event logs operation failed. $error", 4);
$success = false;
$message = __('404 Detection Feature - The operation to delete all the 404 event logs failed', 'all-in-one-wp-security-and-firewall');
} else {
$message = __('All 404 event logs were deleted from the database successfully.', 'all-in-one-wp-security-and-firewall');
}
return $this->handle_response($success, $message);
}
/**
* Handles the AJAX request for 404 log item actions.
*
* @param array $data The data received from the AJAX request.
*
* @return array The response containing the status, message, and badge.
*/
public function perform_404_log_item_action($data) {
global $wpdb, $aio_wp_security, $aiowps_firewall_config;
if (empty($data['action']) || !in_array($data['action'], array('delete', 'temp_block', 'blacklist', 'unblock'))) {
return $this->handle_response(false, __('Invalid action provided for 404 log item.', 'all-in-one-wp-security-and-firewall'));
}
$action = $data['action'];
$message = false;
switch ($action) {
case 'delete':
if (!isset($data['id'])) {
return $this->handle_response(false, __('Invalid 404 event log ID provided.', 'all-in-one-wp-security-and-firewall'));
}
$events_table = AIOWPSEC_TBL_EVENTS;
$id = absint($data['id']);
//Delete single record
$delete_command = "DELETE FROM " . $events_table . " WHERE id = '" . absint($id) . "'";
$result = $wpdb->query($delete_command);
if (false === $result) {
// Error on single delete
$aio_wp_security->debug_logger->log_debug('Database error occurred when deleting rows from Events table. Database error: '.$wpdb->last_error, 4);
return $this->handle_response(false, __('The selected record(s) have failed to delete.', 'all-in-one-wp-security-and-firewall'));
} else {
$message = __('The selected record(s) has been deleted successfully.', 'all-in-one-wp-security-and-firewall');
}
break;
case 'temp_block':
if (!isset($data['ip'])) {
return $this->handle_response(false, __('Invalid IP provided.', 'all-in-one-wp-security-and-firewall'));
}
$ip = sanitize_text_field($data['ip']);
$username = isset($data['username']) ? sanitize_user($data['username']) : '';
if (AIOWPSecurity_Utility_IP::get_user_ip_address() == $ip) {
return $this->handle_response(false, __('You cannot block your own IP address:', 'all-in-one-wp-security-and-firewall') . ' ' . $ip);
}
//Block single record
if (filter_var($ip, FILTER_VALIDATE_IP)) {
AIOWPSecurity_Utility::lock_IP($ip, '404', $username);
$message = __('The selected IP address is now temporarily blocked.', 'all-in-one-wp-security-and-firewall');
} else {
$message = __('The selected entry is not a valid IP address.', 'all-in-one-wp-security-and-firewall');
return $this->handle_response(false, $message);
}
break;
case 'blacklist':
if (!isset($data['ip'])) {
return $this->handle_response(false, __('Invalid IP provided.', 'all-in-one-wp-security-and-firewall'));
}
$bl_ip_addresses = $aio_wp_security->configs->get_value('aiowps_banned_ip_addresses'); //get the currently saved blacklisted IPs
$ip_list_array = AIOWPSecurity_Utility_IP::create_ip_list_array_from_string_with_newline($bl_ip_addresses);
$ip = sanitize_text_field($data['ip']);
$ip_list_array[] = $ip;
$validated_ip_list_array = AIOWPSecurity_Utility_IP::validate_ip_list($ip_list_array, 'blacklist');
if (is_wp_error($validated_ip_list_array)) {
$response = nl2br($validated_ip_list_array->get_error_message());
return $this->handle_response(false, $response);
} else {
$banned_ip_data = implode("\n", $validated_ip_list_array);
$aio_wp_security->configs->set_value('aiowps_enable_blacklisting', '1'); // Force blacklist feature to be enabled.
$aio_wp_security->configs->set_value('aiowps_banned_ip_addresses', $banned_ip_data);
$aio_wp_security->configs->save_config();
$aiowps_firewall_config->set_value('aiowps_blacklist_ips', $validated_ip_list_array);
$message = __('The selected IP addresses have been added to the blacklist and will be permanently blocked.', 'all-in-one-wp-security-and-firewall');
}
break;
case 'unblock':
if (!isset($data['ip'])) {
return $this->handle_response(false, __('Invalid log event ID provided.', 'all-in-one-wp-security-and-firewall'));
}
$ip_range = sanitize_text_field($data['ip']);
$lockout_table = AIOWPSEC_TBL_LOGIN_LOCKOUT;
// get the latest data with that ip in the table that's locked and reason is 404
$query = $wpdb->prepare("SELECT id FROM {$lockout_table} WHERE `released` > UNIX_TIMESTAMP() AND `lock_reason` = %s and failed_login_ip = %s ORDER BY id ASC LIMIT 1", '404', $ip_range);
$id = $wpdb->get_var($query);
if (null === $id) {
return $this->handle_response(false, __('Invalid log event ID provided.', 'all-in-one-wp-security-and-firewall'));
}
$result = $wpdb->query($wpdb->prepare("UPDATE $lockout_table SET `released` = UNIX_TIMESTAMP() WHERE `id` = %d", absint($id)));
if (null != $result) {
$message = __('Access from the selected IP address has been unblocked.', 'all-in-one-wp-security-and-firewall');
} else {
return $this->handle_response(false, __('The selected IP entry could not be unlocked', 'all-in-one-wp-security-and-firewall'));
}
break;
}
return $this->handle_response(true, $message);
}
/**
* Get the content for performing a cookie test.
*
* This method checks if the cookie test is successful or if the brute-force attack prevention feature is already enabled.
* If either condition is true, it returns an empty string. Otherwise, it displays a message prompting the user to perform
* a cookie test before enabling the feature, along with a button to initiate the test.
*
* @return string The HTML content for the cookie test section.
*/
private function get_perform_cookie_test_content() {
global $aio_wp_security;
$cookie_test_value = $aio_wp_security->configs->get_value('aiowps_cookie_test_success');
if ('1' == $cookie_test_value || '1' == $aio_wp_security->configs->get_value('aiowps_enable_brute_force_attack_prevention')) {
return '';
} else {
return $aio_wp_security->include_template('wp-admin/brute-force/partials/cookie-test-container.php', true);
}
}
}
@@ -0,0 +1,221 @@
<?php
if (!defined('ABSPATH')) die('No direct access allowed');
if (trait_exists('AIOWPSecurity_Comment_Commands_Trait')) return;
trait AIOWPSecurity_Comment_Commands_Trait {
/**
* Perform the saving of comment spam prevention settings
*
* @param array $data - the request data contains the post data
*
* @return array
*/
public function perform_comment_spam_prevention($data) {
$response = array();
// Save settings
$options = array();
$info = array();
$options['aiowps_enable_spambot_detecting'] = isset($data["aiowps_enable_spambot_detecting"]) ? '1' : '';
$options['aiowps_spambot_detect_usecookies'] = isset($data["aiowps_spambot_detect_usecookies"]) ? '1' : '';
$options['aiowps_spam_comments_should'] = !empty($data["aiowps_spam_comments_should"]) ? '1' : '0';
$options['aiowps_enable_trash_spam_comments'] = isset($data['aiowps_enable_trash_spam_comments']) ? '1' : '';
if (isset($data['aiowps_trash_spam_comments_after_days'])) {
$aiowps_trash_spam_comments_after_days = sanitize_text_field($data['aiowps_trash_spam_comments_after_days']);
if (isset($data['aiowps_enable_trash_spam_comments']) && !is_numeric($aiowps_trash_spam_comments_after_days)) {
$error = __('You entered a non-numeric value for the "move spam comments to trash after number of days" field; it has been set to the default value.', 'all-in-one-wp-security-and-firewall');
//Set it to the default value for this field
$info[] = $error;
$aiowps_trash_spam_comments_after_days = 14;
}
$aiowps_trash_spam_comments_after_days = absint($aiowps_trash_spam_comments_after_days);
$options['aiowps_trash_spam_comments_after_days'] = $aiowps_trash_spam_comments_after_days;
$response['values'] = array(
'aiowps_trash_spam_comments_after_days' => $aiowps_trash_spam_comments_after_days
);
}
$response['status'] = 'success';
$response['message'] = __('The settings were successfully updated.', 'all-in-one-wp-security-and-firewall');
$response['info'] = $info;
// Commit the config settings
$this->save_settings($options);
AIOWPSecurity_Comment::trash_spam_comments();
$response['badges'] = $this->get_features_id_and_html(array('detect-spambots'));
return $response;
}
/**
* Perform the saving of comment auto block spammers ip settings
*
* @param array $data - the request data contains the post data
*
* @return array
*/
public function perform_auto_block_spam_ip($data) {
$response = array(
'status' => 'success',
'values' => array(),
'info' => array()
);
$enable_auto_block_ip = isset($data["aiowps_enable_autoblock_spam_ip"]) ? '1' : '';
$spam_ip_min_comments = sanitize_text_field($data['aiowps_spam_ip_min_comments_block']);
if (!is_numeric($spam_ip_min_comments)) {
$response['info'][] = __('You entered a non-numeric value for the "minimum number of spam comments" field; it has been set to the default value.', 'all-in-one-wp-security-and-firewall');
$spam_ip_min_comments = '3';// Set it to the default value for this field
} elseif ((int) $spam_ip_min_comments <= 0 || empty($spam_ip_min_comments)) {
$response['info'][] = __('You must enter an integer greater than zero for the "minimum number of spam comments" field; it has been set to the default value.', 'all-in-one-wp-security-and-firewall');
$spam_ip_min_comments = '3';// Set it to the default value for this field
}
// Save all the form values to the options
$options = array(
'aiowps_enable_autoblock_spam_ip' => $enable_auto_block_ip,
'aiowps_spam_ip_min_comments_block' => absint($spam_ip_min_comments),
);
$this->save_settings($options);
$response['message'] = __('The settings were successfully updated.', 'all-in-one-wp-security-and-firewall');
$response['badges'] = $this->get_features_id_and_html(array('auto-block-spam-ips'));
$response['values']['aiowps_spam_ip_min_comments_block'] = absint($spam_ip_min_comments);
return $response;
}
/**
* Perform the ip spam comment search
*
* @param array $data - the request data contains the post data
*
* @return array
*/
public function perform_ip_spam_search($data) {
$response = array(
'status' => 'success',
'info' => array()
);
$min_comments_per_ip = sanitize_text_field($data['aiowps_spam_ip_min_comments']);
$error = '';
if (!is_numeric($min_comments_per_ip)) {
$error = __('You entered a non-numeric value for the minimum spam comments per IP field; it has been set to the default value.', 'all-in-one-wp-security-and-firewall');
$min_comments_per_ip = '5'; // Set it to the default value for this field
} elseif ((int) $min_comments_per_ip <= 0 || empty($min_comments_per_ip)) {
$error = __('You must enter an integer greater than zero for the minimum spam comments per IP field; it has been set to the default value.', 'all-in-one-wp-security-and-firewall');
$min_comments_per_ip = '5'; // Set it to the default value for this field
}
$min_comments_per_ip = absint($min_comments_per_ip);
// Save all the form values to the options
$this->save_settings(array(
'aiowps_spam_ip_min_comments' => $min_comments_per_ip
));
if (!empty($error)) {
$response['message'] = $error;
}
$response['values']['aiowps_spam_ip_min_comments'] = $min_comments_per_ip;
return $response;
}
/**
* Perform the action of blocking a spam IP address.
*
* This function takes an IP address as input, checks if it is valid and not the user's own IP,
* and then attempts to add it to the block list for spam. It returns the status and message of the operation.
*
* @param array $data The data containing the IP address to block.
*
* @return array The result of the block operation, including status, message, and updated blocked comments output.
*/
public function perform_block_spam_ip($data) {
if (empty($data['ip'])) {
return array('status' => 'error', 'message' => __('Invalid IP address provided.', 'all-in-one-wp-security-and-firewall'));
}
$ip = wp_strip_all_tags($data['ip']);
if (AIOWPSecurity_Utility_IP::get_user_ip_address() == $ip) {
return array('status' => 'error', 'message' => __('You cannot block your own IP address:', 'all-in-one-wp-security-and-firewall') . ' ' . $ip);
}
$result = AIOWPSecurity_Blocking::add_ip_to_block_list($ip, 'spam');
if ($result) {
$status = 'success';
$message = __('The selected IP address is now permanently blocked.', 'all-in-one-wp-security-and-firewall');
} else {
$status = 'error';
$message = __('The selected IP address could not be blocked due to one of the following reasons:', 'all-in-one-wp-security-and-firewall');
$message .= ' ' . __('either it has already been blocked, or your user account lacks sufficient permissions to perform IP blocking.', 'all-in-one-wp-security-and-firewall');
}
return array(
'status' => $status,
'message' => $message,
'content' => array('aios-blocked-comments-output' => $this->get_blocked_comments_output())
);
}
/**
* Retrieves the output for displaying blocked comments due to spam.
*
* This function queries the database to get IP addresses that are permanently blocked due to spam.
* It returns HTML output that displays the count of IPs blocked today and the all-time total count.
*
* @global object $aio_wp_security The global instance of the aio_wp_security class.
* @global object $wpdb The global instance of the WordPress database class.
*
* @return string HTML output for the blocked comments section.
*/
private function get_blocked_comments_output() {
global $aio_wp_security, $wpdb;
$block_comments_output = '';
$min_block_comments = $aio_wp_security->configs->get_value('aiowps_spam_ip_min_comments_block');
if (!empty($min_block_comments)) {
$now_date = (new DateTime('now', new DateTimeZone('UTC')))->format('Y-m-d');
$sql = $wpdb->prepare(
"SELECT COUNT(*) AS total_count,
SUM(CASE WHEN DATE(FROM_UNIXTIME(created)) = %s THEN 1 ELSE 0 END) AS todays_blocked_count FROM ".AIOWPSEC_TBL_PERM_BLOCK." WHERE block_reason = %s",
$now_date,
'spam'
);
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery -- PCP warning. Ignore
$result = $wpdb->get_row($sql);
$block_comments_output = '<div class="aio_yellow_box">';
if (empty($result) || 0 == $result->total_count) {
$block_comments_output .= '<p><strong>'.esc_html__('You currently have no IP addresses permanently blocked due to spam.', 'all-in-one-wp-security-and-firewall').'</strong></p>';
} else {
$todays_blocked_count = $result->todays_blocked_count;
$total_count = $result->total_count;
$block_comments_output .= '<p><strong>'.esc_html__('Spammer IPs added to permanent block list today:', 'all-in-one-wp-security-and-firewall') . ' ' . esc_html($todays_blocked_count) . '</strong></p>';
$block_comments_output .= '<hr><p><strong>'.esc_html__('All time total:', 'all-in-one-wp-security-and-firewall'). ' ' . $total_count.'</strong></p>';
$block_comments_output .= '<p><a class="button" href="admin.php?page='.esc_attr(AIOWPSEC_MAIN_MENU_SLUG).'&tab=permanent-block" target="_blank">'.esc_html__('View blocked IPs', 'all-in-one-wp-security-and-firewall').'</a></p>';
}
$block_comments_output .= '</div>';
}
return $block_comments_output;
}
}
@@ -0,0 +1,280 @@
<?php
if (!defined('ABSPATH')) die('No direct access allowed');
if (trait_exists('AIOWPSecurity_File_Scan_Commands_Trait')) return;
trait AIOWPSecurity_File_Scan_Commands_Trait {
/**
* Perform the operation to save file change detection settings.
*
* @param array $data The data containing the file change detection settings.
*
* @return array An array containing the status of the operation, any relevant messages,
* and updated content.
*/
public function perform_save_file_detection_change_settings($data) {
global $aio_wp_security;
$info = array();
$content = array();
$options = array();
$errors = array();
$reset_scan_data = false;
$file_types = '';
$files = '';
$fcd_scan_frequency = sanitize_text_field($data['aiowps_fcd_scan_frequency']);
if (!is_numeric($fcd_scan_frequency)) {
$errors[] = __('You entered a non numeric value for the "backup time interval" field, it has been set to the default value.', 'all-in-one-wp-security-and-firewall');
$fcd_scan_frequency = '4'; // Set it to the default value for this field
}
if (!empty($data['aiowps_fcd_exclude_filetypes'])) {
$file_types = sanitize_textarea_field(trim($data['aiowps_fcd_exclude_filetypes']));
// Get the currently saved config value and check if this has changed. If so do another scan to reset the scan data so it omits these filetypes
if ($file_types != $aio_wp_security->configs->get_value('aiowps_fcd_exclude_filetypes')) {
$reset_scan_data = true;
}
}
if (!empty($data['aiowps_fcd_exclude_files'])) {
$files = sanitize_textarea_field(trim($data['aiowps_fcd_exclude_files']));
// Get the currently saved config value and check if this has changed. If so do another scan to reset the scan data so it omits these files/dirs
if ($files != $aio_wp_security->configs->get_value('aiowps_fcd_exclude_files')) {
$reset_scan_data = true;
}
}
// Explode by end-of-line character, then trim and filter empty lines
$email_list_array = array_filter(array_map('trim', explode("\n", $data['aiowps_fcd_scan_email_address'])), 'strlen');
foreach ($email_list_array as $key => $value) {
$email_sane = sanitize_email($value);
if (!is_email($email_sane)) {
$errors[] = __('The following address was removed because it is not a valid email address:', 'all-in-one-wp-security-and-firewall') . ' ' . htmlspecialchars($value);
unset($email_list_array[$key]);
}
}
$email_address = implode("\n", $email_list_array);
if (!empty($errors)) {
$info[] = implode('<br>', $errors);
}
// Save all the form values to the options
$options['aiowps_enable_automated_fcd_scan'] = isset($data["aiowps_enable_automated_fcd_scan"]) ? '1' : '';
$options['aiowps_fcd_scan_frequency'] = absint($fcd_scan_frequency);
$options['aiowps_fcd_scan_interval'] = sanitize_text_field($data["aiowps_fcd_scan_interval"]);
$options['aiowps_fcd_exclude_filetypes'] = $file_types;
$options['aiowps_fcd_exclude_files'] = $files;
$options['aiowps_send_fcd_scan_email'] = isset($data["aiowps_send_fcd_scan_email"]) ? '1' : '';
$options['aiowps_fcd_scan_email_address'] = $email_address;
$this->save_settings($options);
$content['aios-file-change-info-box'] = '';
// Let's check if backup interval was set to less than 24 hours
if (isset($data["aiowps_enable_automated_fcd_scan"]) && ($fcd_scan_frequency < 24) && 0 == $data["aiowps_fcd_scan_interval"]) {
$content['aios-file-change-info-box'] = '<div class="aio_yellow_box">';
$content['aios-file-change-info-box'] .= '<p>' . __('You have configured your file change detection scan to occur at least once daily.', 'all-in-one-wp-security-and-firewall') . '</p>';
$content['aios-file-change-info-box'] .= '<p>' . __('For most websites we recommended that you choose a less frequent schedule such as once every few days, once a week or once a month.', 'all-in-one-wp-security-and-firewall') . '</p>';
$content['aios-file-change-info-box'] .= '<p>' . __('Choosing a less frequent schedule will also help reduce your server load.', 'all-in-one-wp-security-and-firewall') . '</p>';
$content['aios-file-change-info-box'] .= '</div>';
}
if ($reset_scan_data) {
$aio_wp_security->scan_obj->execute_file_change_detection_scan();
$new_scan_alert = __('New scan completed: The plugin has detected that you have made changes to the "File Types To Ignore" or "Files To Ignore" fields.', 'all-in-one-wp-security-and-firewall').' '.__('In order to ensure that future scan results are accurate, the old scan data has been refreshed.', 'all-in-one-wp-security-and-firewall');
$info[] = $new_scan_alert;
}
$next_fcd_scan_time = AIOWPSecurity_Scan::get_next_scheduled_scan();
if (false == $next_fcd_scan_time) {
$next_scheduled_scan = '<span>' . esc_html__('Nothing is currently scheduled', 'all-in-one-wp-security-and-firewall') . '</span>';
} else {
$scan_time = AIOWPSecurity_Utility::convert_timestamp($next_fcd_scan_time, 'D, F j, Y H:i');
$next_scheduled_scan = '<span class="aiowps_next_scheduled_date_time">' . esc_html($scan_time) . '</span>';
}
$content['aiowps-next-files-scan-inner'] = $next_scheduled_scan;
$values = array('aiowps_fcd_scan_frequency' => absint($fcd_scan_frequency));
$badges = array('scan-file-change-detection');
$args = array(
'content' => $content,
'values' => $values,
'badges' => $badges,
'info' => $info
);
return $this->handle_response(true, '', $args);
}
/**
* Retrieves the last file scan data and returns the data to UDC.
*
* @param array $data The request data.
*
* @return array|string[]|WP_Error
*/
public function get_last_scan_data($data) {
global $aio_wp_security;
if (!AIOWPSecurity_Utility_Permissions::has_manage_cap()) {
return new WP_Error(esc_html__('Sorry, you do not have enough privilege to execute the requested action.', 'all-in-one-wp-security-and-firewall'));
}
if ($data['reset_change_detected']) {
$aio_wp_security->configs->set_value('aiowps_fcds_change_detected', false, true);
}
$fcd_data = AIOWPSecurity_Scan::get_fcd_data();
$data = $fcd_data['last_scan_result'];
foreach (array('files_added', 'files_removed', 'files_changed') as $key) {
/* Normalize missing or non-array buckets to an empty array and skip processing */
if (!isset($data[$key]) || !is_array($data[$key])) {
$data[$key] = array();
continue;
}
/* Convert last_modified for each entry */
foreach ($data[$key] as &$info) {
if (is_array($info) && array_key_exists('last_modified', $info) && is_numeric($info['last_modified'])) {
$info['last_modified'] = AIOWPSecurity_Utility::convert_timestamp($info['last_modified']);
}
}
unset($info);
}
$fcd_data['last_scan_result'] = $data;
return $this->handle_response(true, false, array('extra_args' => $fcd_data));
}
/**
* Gets the last file scan result and returns the scan result HTML template
*
* @param array $data - the request data
*
* @return array
*/
public function get_last_scan_results($data) {
global $aio_wp_security;
if ($data['reset_change_detected']) $aio_wp_security->configs->set_value('aiowps_fcds_change_detected', false, true);
$fcd_data = AIOWPSecurity_Scan::get_fcd_data();
if (!$fcd_data || !isset($fcd_data['last_scan_result'])) {
// no fcd data found
$message = __('No previous scan data was found; either run a manual scan or schedule regular file scans', 'all-in-one-wp-security-and-firewall');
return $this->handle_response(false, $message);
}
$content = array('aiowps_previous_scan_wrapper' => $aio_wp_security->include_template('wp-admin/scanner/scan-result.php', true, array('fcd_data' => $fcd_data)));
return $this->handle_response(true, false, array('content' => $content));
}
/**
* Performs a file scan and returns the scan result
*
* @return array
*/
public function perform_file_scan() {
global $aio_wp_security;
$content = array();
$extra_args = array();
$result = $aio_wp_security->scan_obj->execute_file_change_detection_scan();
if (false === $result) {
// error case
$message = __('There was an error during the file change detection scan.', 'all-in-one-wp-security-and-firewall') . ' ' . __('Please check the plugin debug logs.', 'all-in-one-wp-security-and-firewall');
return $this->handle_response(false, $message);
}
$aio_wp_security->configs->set_value('aiowps_last_scan_time', time(), true);
// If this is first scan display special message
if (1 == $result['initial_scan']) {
$extra_args['result'] = __('This is your first file change detection scan.', 'all-in-one-wp-security-and-firewall').' '.__('The details from this scan will be used for future scans.', 'all-in-one-wp-security-and-firewall'). ' <a href="#" class="aiowps_view_last_fcd_results">' . __('View the file scan results', 'all-in-one-wp-security-and-firewall') . '</a>';
$content['aiowps-previous-files-scan-inner'] = '<a href="#" class="aiowps_view_last_fcd_results">' . __('View last file scan results', 'all-in-one-wp-security-and-firewall') . '</a>';
} elseif (!$aio_wp_security->configs->get_value('aiowps_fcds_change_detected')) {
$extra_args['result'] = __('The scan is complete - There were no file changes detected.', 'all-in-one-wp-security-and-firewall');
} elseif ($aio_wp_security->configs->get_value('aiowps_fcds_change_detected')) {
$extra_args['result'] = __('The scan has detected that there was a change in your website\'s files.', 'all-in-one-wp-security-and-firewall'). ' <a href="#" class="aiowps_view_last_fcd_results">' . __('View the file scan results', 'all-in-one-wp-security-and-firewall') . '</a>';
}
$args = array(
'extra_args' => $extra_args,
'content' => $content
);
return $this->handle_response(true, false, $args);
}
/**
* Render the legacy UDC Scanner.
*
* @return array
*/
public function get_scanner_contents() {
global $aio_wp_security;
$GLOBALS['aiowps_feature_mgr'] = $this->get_feature_mgr_object();
$scanner_data = $this->get_scanner_data();
$content = $aio_wp_security->include_template('wp-admin/scanner/file-change-detect.php', true, $scanner_data);
return array(
'status' => 'success',
'content' => $content,
);
}
/**
* Return file scanner data.
*
* @return array Array of option values,
*/
public function get_scanner_data() {
global $aio_wp_security;
$fcd_data = AIOWPSecurity_Scan::get_fcd_data();
$previous_scan = isset($fcd_data['last_scan_result']);
$next_fcd_scan_time = AIOWPSecurity_Scan::get_next_scheduled_scan();
$aiowps_fcds_change_detected = $aio_wp_security->configs->get_value('aiowps_fcds_change_detected');
$aiowps_enable_automated_fcd_scan = $aio_wp_security->configs->get_value('aiowps_enable_automated_fcd_scan');
$aiowps_fcd_scan_frequency = $aio_wp_security->configs->get_value('aiowps_fcd_scan_frequency');
$aiowps_fcd_scan_interval = $aio_wp_security->configs->get_value('aiowps_fcd_scan_interval');
$aiowps_fcd_exclude_filetypes = $aio_wp_security->configs->get_value('aiowps_fcd_exclude_filetypes');
$aiowps_fcd_exclude_files = $aio_wp_security->configs->get_value('aiowps_fcd_exclude_files');
$aiowps_send_fcd_scan_email = $aio_wp_security->configs->get_value('aiowps_send_fcd_scan_email');
$aiowps_fcd_scan_email_address = $aio_wp_security->configs->get_value('aiowps_fcd_scan_email_address');
$aiowps_last_scan_time = $aio_wp_security->configs->get_value('aiowps_last_scan_time');
return array(
'previous_scan' => $previous_scan,
'next_fcd_scan_time' => false === $next_fcd_scan_time ? '' : AIOWPSecurity_Utility::convert_timestamp($next_fcd_scan_time, 'D, F j, Y H:i'),
'aiowps_fcds_change_detected' => $aiowps_fcds_change_detected,
'aiowps_enable_automated_fcd_scan' => $aiowps_enable_automated_fcd_scan,
'aiowps_fcd_scan_frequency' => $aiowps_fcd_scan_frequency,
'aiowps_fcd_scan_interval' => $aiowps_fcd_scan_interval,
'aiowps_fcd_exclude_filetypes' => $aiowps_fcd_exclude_filetypes,
'aiowps_fcd_exclude_files' => $aiowps_fcd_exclude_files,
'aiowps_send_fcd_scan_email' => $aiowps_send_fcd_scan_email,
'aiowps_fcd_scan_email_address' => $aiowps_fcd_scan_email_address,
'aiowps_last_scan_time' => AIOWPSecurity_Utility::convert_timestamp($aiowps_last_scan_time, 'D, F j, Y H:i'),
);
}
}
@@ -0,0 +1,224 @@
<?php
if (!defined('ABSPATH')) die('No direct access allowed');
if (trait_exists('AIOWPSecurity_Files_Commands_Trait')) return;
trait AIOWPSecurity_Files_Commands_Trait {
/**
* This function performs file permission fixing
*
* @param array $data - the request data contains the files items
*
* @return array
*/
public function perform_fix_permissions($data) {
global $aio_wp_security;
$files_dirs_to_check = AIOWPSecurity_Utility_File::get_files_and_dirs_to_check();
$success = true;
$message = '';
if (isset($data['aiowps_permission_chg_file'])) {
$file_found = false;
$folder_or_file = sanitize_text_field($data['aiowps_permission_chg_file']);
$rec_perm_oct_string = '';
foreach ($files_dirs_to_check as $file_or_dir) {
if ($folder_or_file == $file_or_dir['path']) {
$file_found = true;
$rec_perm_oct_string = $file_or_dir['permissions'];
}
}
if ($file_found && !empty($rec_perm_oct_string)) {
$rec_perm_dec = octdec($rec_perm_oct_string); // Convert the octal string to dec so the chmod func will accept it
$perm_result = @chmod($folder_or_file, $rec_perm_dec);
if (true === $perm_result) {
$message = sprintf(__('The permissions for %s were successfully changed to %s', 'all-in-one-wp-security-and-firewall'), htmlspecialchars($folder_or_file), htmlspecialchars($rec_perm_oct_string));
} elseif (false === $perm_result) {
$message = sprintf(__('Unable to change permissions for %s', 'all-in-one-wp-security-and-firewall'), htmlspecialchars($folder_or_file));
$success = false;
}
} else {
$message = sprintf(__('Unable to change permissions for %s : not in list of valid files', 'all-in-one-wp-security-and-firewall'), htmlspecialchars($folder_or_file));
$success = false;
}
}
$badges = array("filesystem-file-permissions");
$content = array('aios_file_permissions_table' => $aio_wp_security->include_template('wp-admin/filesystem-security/partials/file-permissions-table.php', true, array('files_dirs_to_check' => $files_dirs_to_check, 'file_utility' => new AIOWPSecurity_Utility_File())));
$args = array(
'content' => $content,
'badges' => $badges,
);
return $this->handle_response($success, $message, $args);
}
/**
* This function performs file protection settings
*
* @param array $data - the request data contains the settings
*
* @return array
*/
public function perform_file_protection_settings($data) {
global $aio_wp_security;
$success = true;
$message = '';
$options = array();
// Update settings for delete readme.html and wp-config-sample.php.
$options['aiowps_auto_delete_default_wp_files'] = isset($data['aiowps_auto_delete_default_wp_files']) ? '1' : '';
// Update settings for prevent hotlinking.
$options['aiowps_prevent_hotlinking'] = isset($data['aiowps_prevent_hotlinking']) ? '1' : '';
// Update settings for php file editing
$disable_file_editing = isset($data["aiowps_disable_file_editing"]) ? '1' : '';
$disable_file_editing_status = $disable_file_editing ? AIOWPSecurity_Utility::disable_file_edits() : AIOWPSecurity_Utility::enable_file_edits();
if ($disable_file_editing_status) {
// Save settings if no errors
$options['aiowps_disable_file_editing'] = $disable_file_editing;
} else {
$message = __('Disable PHP file editing failed: unable to modify or make a backup of the wp-config.php file.', 'all-in-one-wp-security-and-firewall');
return $this->handle_response(false, $message);
}
$this->save_settings($options);
if (AIOWPSecurity_Utility_Htaccess::write_to_htaccess() && '' !== $options['aiowps_prevent_hotlinking']) {
// Now let's write the applicable rules to the .htaccess file
$res = AIOWPSecurity_Utility_Htaccess::write_to_htaccess();
if ($res) {
$message = __('The settings have been successfully updated', 'all-in-one-wp-security-and-firewall');
} else {
$success = false;
$message = __('Could not write to the .htaccess file.', 'all-in-one-wp-security-and-firewall');
// revert options affected by .htaccess write fail
$options['aiowps_prevent_hotlinking'] = $aio_wp_security->configs->get_value('aiowps_prevent_hotlinking');
$this->save_settings($options);
}
}
$features = array(
"auto-delete-wp-files",
"prevent-hotlinking",
"filesystem-file-editing",
);
return $this->handle_response($success, $message, array('badges' => $features));
}
/**
* This function performs deleting default wp files
*
* @return array
*/
public function perform_delete_default_wp_files() {
$success = true;
$message = __('The files have been deleted successfully.', 'all-in-one-wp-security-and-firewall');
$result = AIOWPSecurity_Utility::delete_unneeded_default_files();
if (!empty($result['error'])) {
$success = false;
$message = sprintf(__('Failed to delete the %s file(s).', 'all-in-one-wp-security-and-firewall'), $result['error']) . '<br>' . __('Please try to delete them manually.', 'all-in-one-wp-security-and-firewall');
}
return $this->handle_response($success, $message, array('info' => $result['info']));
}
/**
* This function performs save copy protection settings
*
* @param array $data - the request data
*
* @return array
*/
public function perform_save_copy_protection($data) {
$this->save_settings(array('aiowps_copy_protection' => isset($data["aiowps_copy_protection"]) ? '1' : ''));
return $this->handle_response(true, '', array('badges' => array('enable-copy-protection')));
}
/**
* This function performs save frame display prevent setting
*
* @param array $data - the request data
*
* @return array
*/
public function perform_save_frame_display_prevent($data) {
$this->save_settings(array('aiowps_prevent_site_display_inside_frame' => isset($data["aiowps_prevent_site_display_inside_frame"]) ? '1' : ''));
return $this->handle_response(true, '', array('badges' => array('enable-frame-protection')));
}
/**
* This function performs host system logs
*
* @param array $data - the request data contains the lgos settings
*
* @return array
*/
public function perform_host_system_logs($data) {
$content = array();
$success = true;
$message = false;
if (isset($data['aiowps_system_log_file'])) {
if ('' != $data['aiowps_system_log_file']) {
$sys_log_file = basename(sanitize_text_field($data['aiowps_system_log_file']));
} else {
$sys_log_file = 'error_log';
}
$this->save_settings(array('aiowps_system_log_file' => $sys_log_file));
}
$logResults = AIOWPSecurity_Utility_File::recursive_file_search($sys_log_file, 0, ABSPATH);
if (empty($logResults) || '' == $logResults) {
$success = false;
$message = __('No system logs were found.', 'all-in-one-wp-security-and-firewall');
} else {
$content['aios-host-system-logs-results'] = '';
foreach ($logResults as $file) {
$content['aios-host-system-logs-results'] .= $this->display_system_logs_in_table($file);
}
}
$values = array('aiowps_system_log_file' => $sys_log_file);
$args = array(
'content' => $content,
'values' => $values
);
return $this->handle_response($success, $message, $args);
}
/**
* Displays the last 50 entries of a system log file in a table format.
*
* This function reads the contents of the specified file and returns a
* rendered template displaying the last 50 entries of the log file.
*
* @param string $filepath The path to the log file to be read.
*
* @return string The rendered HTML template displaying the log entries.
*/
private function display_system_logs_in_table($filepath) {
global $aio_wp_security;
// Get contents of the error_log file
$last_50_entries = AIOWPSecurity_Utility_File::read_file_lines($filepath, -1, 50, true);
return $aio_wp_security->include_template('wp-admin/filesystem-security/filesystem-log-result.php', true, array('filepath' => $filepath, 'last_50_entries' => $last_50_entries));
}
}
@@ -0,0 +1,787 @@
<?php
if (!defined('ABSPATH')) die('No direct access allowed');
if (trait_exists('AIOWPSecurity_Firewall_Commands_Trait')) return;
trait AIOWPSecurity_Firewall_Commands_Trait {
/**
* Perform saving php firewall settings
*
* @param array $data - the request data contains PHP settings
*
* @return array - containing a status and message
*/
public function perform_php_firewall_settings($data) {
global $aio_wp_security;
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
$options = array();
$enable_pingback = isset($data["aiowps_enable_pingback_firewall"]);
$info = array();
// Save settings
$aiowps_firewall_config->set_value('aiowps_enable_pingback_firewall', $enable_pingback);
$options['aiowps_disable_xmlrpc_pingback_methods'] = isset($data["aiowps_disable_xmlrpc_pingback_methods"]) ? '1' : ''; //this disables only pingback methods of xmlrpc but leaves other methods so that Jetpack and other apps will still work
$options['aiowps_disable_rss_and_atom_feeds'] = isset($data['aiowps_disable_rss_and_atom_feeds']) ? '1' : '';
$aiowps_firewall_config->set_value('aiowps_forbid_proxy_comments', isset($data['aiowps_forbid_proxy_comments']));
$aiowps_firewall_config->set_value('aiowps_deny_bad_query_strings', isset($data['aiowps_deny_bad_query_strings']));
$aiowps_firewall_config->set_value('aiowps_advanced_char_string_filter', isset($data['aiowps_advanced_char_string_filter']));
$block_request_methods = array_map('strtolower', AIOS_Abstracted_Ids::get_firewall_block_request_methods());
$current_request_methods_settings = $aiowps_firewall_config->get_value('aiowps_6g_block_request_methods');
$current_other_settings = array(
$aiowps_firewall_config->get_value('aiowps_6g_block_query'),
$aiowps_firewall_config->get_value('aiowps_6g_block_request'),
$aiowps_firewall_config->get_value('aiowps_6g_block_referrers'),
$aiowps_firewall_config->get_value('aiowps_6g_block_agents'),
);
$are_methods_set = !empty($current_request_methods_settings);
$are_others_set = array_reduce($current_other_settings, function($carry, $item) {
return $carry || $item;
});
if (($are_methods_set || $are_others_set) && '1' !== $aio_wp_security->configs->get_value('aiowps_enable_6g_firewall')) {
$options['aiowps_enable_6g_firewall'] = '1';
}
if (isset($data['aiowps_enable_6g_firewall'])) {
$aiowps_6g_block_request_methods = array_filter(AIOS_Abstracted_Ids::get_firewall_block_request_methods(), function($block_request_method) {
return ('PUT' != $block_request_method);
});
if (false === $are_methods_set && false === $are_others_set) {
$aiowps_firewall_config->set_value('aiowps_6g_block_request_methods', $aiowps_6g_block_request_methods);
$aiowps_firewall_config->set_value('aiowps_6g_block_query', true);
$aiowps_firewall_config->set_value('aiowps_6g_block_request', true);
$aiowps_firewall_config->set_value('aiowps_6g_block_referrers', true);
$aiowps_firewall_config->set_value('aiowps_6g_block_agents', true);
} else {
$methods = array();
foreach ($block_request_methods as $block_request_method) {
if (isset($data['aiowps_block_request_method_'.$block_request_method])) {
$methods[] = strtoupper($block_request_method);
}
}
$aiowps_firewall_config->set_value('aiowps_6g_block_request_methods', $methods);
$aiowps_firewall_config->set_value('aiowps_6g_block_query', isset($data['aiowps_block_query']));
$aiowps_firewall_config->set_value('aiowps_6g_block_request', isset($data['aiowps_block_request']));
$aiowps_firewall_config->set_value('aiowps_6g_block_referrers', isset($data['aiowps_block_refs']));
$aiowps_firewall_config->set_value('aiowps_6g_block_agents', isset($data['aiowps_block_agents']));
}
$options['aiowps_enable_6g_firewall'] = '1';
//shows the success notice
} else {
AIOWPSecurity_Configure_Settings::turn_off_all_6g_firewall_configs();
$options['aiowps_enable_6g_firewall'] = '';
}
$aiowps_firewall_config->set_value('aiowps_ban_post_blank_headers', isset($data['aiowps_ban_post_blank_headers']));
if (isset($data['aiowps_block_fake_googlebots'])) {
$validated_ip_list_array = AIOWPSecurity_Utility::get_googlebot_ip_ranges();
if (is_wp_error($validated_ip_list_array)) {
$info[] = __('The attempt to save the \'Block fake Googlebots\' settings failed, because it was not possible to validate the Googlebot IP addresses:', 'all-in-one-wp-security-and-firewall') . ' ' . $validated_ip_list_array->get_error_message();
} else {
$aiowps_firewall_config->set_value('aiowps_block_fake_googlebots', true);
$aiowps_firewall_config->set_value('aiowps_googlebot_ip_ranges', $validated_ip_list_array);
}
} else {
$aiowps_firewall_config->set_value('aiowps_block_fake_googlebots', false);
}
$options['aiowps_disallow_unauthorized_rest_requests'] = isset($data["aiowps_disallow_unauthorized_rest_requests"]) ? '1' : '';
$aios_whitelisted_rest_routes = array();
$route_namespaces = AIOWPSecurity_Utility::get_rest_namespaces();
foreach ($route_namespaces as $route_namespace) {
if (isset($data['aios_whitelisted_rest_routes_'.str_replace('-', '_', $route_namespace)])) {
$aios_whitelisted_rest_routes[] = $route_namespace;
}
}
$options['aios_whitelisted_rest_routes'] = $aios_whitelisted_rest_routes;
$aios_roles_disallowed_rest_requests = array();
$user_roles = AIOWPSecurity_Utility_Permissions::get_user_roles();
foreach ($user_roles as $id => $name) {
if (!isset($data['aios_allowed_roles_rest_requests_'.$id])) {
$aios_roles_disallowed_rest_requests[] = $id;
}
}
$options['aios_roles_disallowed_rest_requests'] = $aios_roles_disallowed_rest_requests;
// Commit the config settings
$this->save_settings($options);
$block_request_methods = array_map('strtolower', AIOS_Abstracted_Ids::get_firewall_block_request_methods());
$methods = $aiowps_firewall_config->get_value('aiowps_6g_block_request_methods');
if (empty($methods)) {
$methods = array();
}
$blocked_query = (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_query');
$blocked_request = (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_request');
$blocked_referrers = (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_referrers');
$blocked_agents = (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_agents');
$content = array('aios-6g-firewall-settings-container .aios-advanced-options-panel' => $aio_wp_security->include_template('wp-admin/firewall/partials/advanced-settings-6g.php', true, compact('methods', 'blocked_query', 'blocked_request', 'blocked_referrers', 'blocked_agents', 'block_request_methods')));
$features = array(
'firewall-pingback-rules',
'firewall-disable-rss-and-atom',
'firewall-forbid-proxy-comments',
'firewall-deny-bad-queries',
'firewall-advanced-character-string-filter',
'firewall-enable-6g',
'firewall-block-fake-googlebots',
'firewall-ban-post-blank-headers',
'disallow-unauthorised-requests',
);
$args = array(
'badges' => $features,
'content' => $content,
'info' => $info,
'extra_args' => array('xmlprc_warning' => $enable_pingback ? $aio_wp_security->include_template('wp-admin/firewall/partials/xmlrpc-warning-notice.php', true) : '')
);
return $this->handle_response(true, '', $args);
}
/**
* Perform saving .htaccess firewall settings
*
* @param array $data - the request data contains the firewall settings
*
* @return array - containing a status and message
*/
public function perform_htaccess_firewall_settings($data) {
global $aio_wp_security;
$options = array();
$info = array();
$message = '';
$success = true;
// Max file upload size in basic rules
$upload_size = absint($data['aiowps_max_file_upload_size']);
$max_allowed = apply_filters('aiowps_max_allowed_upload_config', 250); // Set a filterable limit of 250MB
$max_allowed = absint($max_allowed);
if ($upload_size > $max_allowed) {
$upload_size = $max_allowed;
} elseif (empty($upload_size) || 0 > $upload_size) {
$upload_size = AIOS_FIREWALL_MAX_FILE_UPLOAD_LIMIT_MB;
$info[] = __('Max file upload limit was set to default value, because you entered a negative or zero value');
}
// Store the current value in case the .htaccess write operation fails and we need to revert it
$original_options = array(
'aiowps_enable_basic_firewall' => $aio_wp_security->configs->get_value("aiowps_enable_basic_firewall"),
'aiowps_max_file_upload_size' => $aio_wp_security->configs->get_value('aiowps_max_file_upload_size'),
'aiowps_block_debug_log_file_access' => $aio_wp_security->configs->get_value("aiowps_block_debug_log_file_access"),
'aiowps_disable_index_views' => $aio_wp_security->configs->get_value('aiowps_disable_index_views'),
);
// Save settings
$options['aiowps_enable_basic_firewall'] = isset($data["aiowps_enable_basic_firewall"]) ? '1' : '';
$options['aiowps_max_file_upload_size'] = $upload_size;
$options['aiowps_block_debug_log_file_access'] = isset($data["aiowps_block_debug_log_file_access"]) ? '1' : '';
$options['aiowps_disable_index_views'] = isset($data['aiowps_disable_index_views']) ? '1' : '';
// Commit the config settings
$this->save_settings($options);
//Now let's write the applicable rules to the .htaccess file
$res = AIOWPSecurity_Utility_Htaccess::write_to_htaccess();
if (!$res) {
$success = false;
$message = __('Could not write to the .htaccess file', 'all-in-one-wp-security-and-firewall');
$this->save_settings($original_options);
}
$features = array(
'firewall-basic-rules',
'firewall-block-debug-file-access',
'firewall-disable-index-views',
);
$values = array('aiowps_max_file_upload_size' => $upload_size);
$args = array(
'badges' => $features,
'info' => $info,
'values' => $values
);
return $this->handle_response($success, $message, $args);
}
/**
* Save and update the 5G firewall settings, and conditionally update the .htaccess file if needed.
*
* This function handles the saving of the 5G firewall settings based on user input. It checks if
* the 5G firewall setting has been modified and writes the applicable rules to the .htaccess file
* if necessary. In case of failure to write to the .htaccess file, it returns an error message.
*
* @param array $data The data array containing the 5G firewall setting.
*
* @global object $aio_wp_security The global instance of the All-In-One WP Security & Firewall plugin.
*
* @return array An array containing the status ('success' or 'error') and a message indicating
* the result of the operation.
*/
public function perform_save_5g_settings($data) {
global $aio_wp_security;
$response = array(
'status' => 'success',
'message' => __('The settings were successfully updated.', 'all-in-one-wp-security-and-firewall')
);
$options = array();
// If the user has changed the 5G firewall checkbox settings, then there is a need to write htaccess rules again.
$is_5G_firewall_option_changed = ((isset($data['aiowps_enable_5g_firewall']) && '1' != $aio_wp_security->configs->get_value('aiowps_enable_5g_firewall')) || (!isset($data['aiowps_enable_5g_firewall']) && '1' == $aio_wp_security->configs->get_value('aiowps_enable_5g_firewall')));
// Save settings
$options['aiowps_enable_5g_firewall'] = isset($data['aiowps_enable_5g_firewall']) ? '1' : '';
$this->save_settings($options);
$res = true;
if ($is_5G_firewall_option_changed) {
$res = AIOWPSecurity_Utility_Htaccess::write_to_htaccess(); // let's write the applicable rules to the .htaccess file
}
if (!$res) {
$response['status'] = 'error';
$response['message'] = __('Could not write to the .htaccess file for the 5G firewall settings, please check the file permissions.', 'all-in-one-wp-security-and-firewall');
// revert settings
$options['aiowps_enable_5g_firewall'] = '';
$this->save_settings($options);
}
return $response;
}
/**
* Perform saving blacklist settings
*
* @param array $data - the request data contains blacklist settings
*
* @return array - containing a status, message and feature badge html
*/
public function perform_save_blacklist_settings($data) {
global $aio_wp_security;
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
$options = array();
$message = '';
$success = true;
$result = 1;
$aiowps_enable_blacklisting = isset($data["aiowps_enable_blacklisting"]) ? '1' : '';
if (!empty($data['aiowps_banned_ip_addresses'])) {
$ip_addresses = sanitize_textarea_field(stripslashes($data['aiowps_banned_ip_addresses']));
$ip_list_array = AIOWPSecurity_Utility_IP::create_ip_list_array_from_string_with_newline($ip_addresses);
$validated_ip_list_array = AIOWPSecurity_Utility_IP::validate_ip_list($ip_list_array, 'blacklist');
if (is_wp_error($validated_ip_list_array)) {
$result = -1;
$success = false;
$message = nl2br($validated_ip_list_array->get_error_message());
} else {
$banned_ip_addresses_list = preg_split('/\R/', $aio_wp_security->configs->get_value('aiowps_banned_ip_addresses')); // Historical settings where the separator may have depended on PHP_EOL.
if ($banned_ip_addresses_list !== $validated_ip_list_array) {
$banned_ip_data = implode("\n", $validated_ip_list_array);
$options['aiowps_banned_ip_addresses'] = $banned_ip_data;
$aiowps_firewall_config->set_value('aiowps_blacklist_ips', $validated_ip_list_array);
}
$data['aiowps_banned_ip_addresses'] = ''; // Clear the post variable for the banned address list.
}
} else {
$options['aiowps_banned_ip_addresses'] = ''; // Clear the IP address config value
$aiowps_firewall_config->set_value('aiowps_blacklist_ips', array());
}
if (!empty($data['aiowps_banned_user_agents'])) {
$this->validate_user_agent_list(stripslashes($data['aiowps_banned_user_agents']));
} else {
// Clear the user agent list
$options['aiowps_banned_user_agents'] = '';
$aiowps_firewall_config->set_value('aiowps_blacklist_user_agents', array());
}
if (1 == $result) {
$aio_wp_security->configs->set_value('aiowps_enable_blacklisting', $aiowps_enable_blacklisting, true);
if ('1' == $aio_wp_security->configs->get_value('aiowps_is_ip_blacklist_settings_notice_on_upgrade')) {
$aio_wp_security->configs->delete_value('aiowps_is_ip_blacklist_settings_notice_on_upgrade');
}
}
$this->save_settings($options);
$args = array(
'badges' => array("blacklist-manager-ip-user-agent-blacklisting")
);
return $this->handle_response($success, $message, $args);
}
/**
* The AJAX function for storing ips in firewall allowlist
*
* @param array $data - the request data contains data to updated
*
* @return array - containing a status and message
*/
public function perform_firewall_allowlist($data) {
$aiowps_firewall_allow_list = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::ALLOW_LIST);
$message = '';
$success = true;
$allowlist = $data['aios_firewall_allowlist'];
if (empty($allowlist)) {
$aiowps_firewall_allow_list::add_ips('');
return $this->handle_response(true, '');
}
$ips = sanitize_textarea_field(wp_unslash($allowlist));
$ips = AIOWPSecurity_Utility_IP::create_ip_list_array_from_string_with_newline($ips);
$validated_ip_list_array = AIOWPSecurity_Utility_IP::validate_ip_list($ips, 'firewall_allowlist');
if (is_wp_error($validated_ip_list_array)) {
$success = false;
$message = nl2br($validated_ip_list_array->get_error_message());
} else {
$aiowps_firewall_allow_list::add_ips($validated_ip_list_array);
}
return $this->handle_response($success, $message);
}
/**
* The AJAX function for saving PHP firewall and block and allowlists in UDC.
*
* @param array $data The data send from UDC.
*
* @return array|WP_Error
*/
public function perform_save_firewall($data) {
if (!AIOWPSecurity_Utility_Permissions::has_manage_cap()) {
return new WP_Error(esc_html__('Sorry, you do not have enough privilege to execute the requested action.', 'all-in-one-wp-security-and-firewall'));
}
$response = $this->perform_firewall_allowlist($data);
if ('error' === $response['status']) {
return $response;
}
$response = $this->perform_save_blacklist_settings($data);
if ('error' === $response['status']) {
return $response;
}
return $this->perform_php_firewall_settings($data);
}
/**
* Perform the setup process for the firewall.
*
* This function handles the setup form for the firewall and renders notices accordingly.
*
* @return array An array containing the content and message for the response.
*/
public function perform_setup_firewall() {
global $aio_wp_security;
$firewall_setup = AIOWPSecurity_Firewall_Setup_Notice::get_instance();
$content = array('aiowps-firewall-status-container' => $aio_wp_security->include_template('wp-admin/firewall/partials/firewall-set-up-button.php', true));
$firewall_setup->do_setup();
ob_start();
$firewall_setup->render_notices();
$result = ob_get_clean();
$args = array(
'content' => $content,
'extra_args' => array('info_box' => $result)
);
$message = false;
if (AIOWPSecurity_Utility_Firewall::is_firewall_setup()) {
$content['aiowps-firewall-status-container'] = $aio_wp_security->include_template('wp-admin/firewall/partials/firewall-downgrade-button.php', true);
$message = __('Firewall has been setup successfully.', 'all-in-one-wp-security-and-firewall');
$args['content'] = $content;
}
return $this->handle_response(AIOWPSecurity_Utility_Firewall::is_firewall_setup(), $message, $args);
}
/**
* Perform the downgrade process for the firewall.
*
* This function removes the firewall and returns a response indicating success.
*
* @return array An array containing the status, content, and message for the response.
*/
public function perform_downgrade_firewall() {
global $aio_wp_security;
AIOWPSecurity_Utility_Firewall::remove_firewall();
$message = AIOWPSecurity_Utility_Firewall::is_firewall_setup() ? __('Something went wrong please try again later.', 'all-in-one-wp-security-and-firewall') : __('Firewall has been downgraded successfully.', 'all-in-one-wp-security-and-firewall');
$success = true;
$downgrade_button = $aio_wp_security->include_template('wp-admin/firewall/partials/firewall-set-up-button.php', true);
$extra_args = array();
if (AIOWPSecurity_Utility_Firewall::is_firewall_setup()) {
$success = false;
$downgrade_button = $aio_wp_security->include_template('wp-admin/firewall/partials/firewall-downgrade-button.php', true);
} else {
$extra_args['info_box'] = $aio_wp_security->include_template('notices/firewall-setup-notice.php', true, array('show_dismiss' => false));
}
$args = array(
'content' => array('aiowps-firewall-status-container' => $downgrade_button),
'extra_args' => $extra_args
);
return $this->handle_response($success, $message, $args);
}
/**
* Validates posted user agent list and set, save as config.
*
* @param string $banned_user_agents - List of banned user agents
*
* @return void
*/
private function validate_user_agent_list($banned_user_agents) {
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
$submitted_agents = AIOWPSecurity_Utility::splitby_newline_trim_filter_empty($banned_user_agents);
$agents = array_unique(
array_filter(
array_map(
'sanitize_text_field',
$submitted_agents
),
'strlen'
)
);
$aiowps_firewall_config->set_value('aiowps_blacklist_user_agents', $agents);
$this->save_settings(array(
'aiowps_banned_user_agents' => implode("\n", $agents)
));
}
/**
* This function performs save upgrade unsafe http calls settings.
*
* @param array $data - The request data.
*
* @return array
*/
public function perform_save_upgrade_unsafe_http_calls_settings($data) {
$upgrade_unsafe_http_calls_url_exceptions = sanitize_textarea_field(wp_unslash($data['aiowps_upgrade_unsafe_http_calls_url_exceptions']));
$errors = '';
if (!empty($upgrade_unsafe_http_calls_url_exceptions)) {
foreach (preg_split('/\R/', $upgrade_unsafe_http_calls_url_exceptions) as $url) {
$url = sanitize_url($url);
if (empty($url)) {
continue;
}
if (0 === strpos($url, '#')) {
continue;
}
$parsed_url = parse_url($url); // phpcs:ignore WordPress.WP.AlternativeFunctions.parse_url_parse_url -- Using the same function as WordPress in order to not preclude URLs that would be allowed by WordPress.
if (empty($parsed_url['scheme'])) { // The same weak sanity check used by the WordPress wp_remote_* functions.
/* translators: %s URL entered by user. */
$errors .= "\n" . sprintf(__('%s is not a valid url.', 'all-in-one-wp-security-and-firewall'), $url);
continue;
}
}
}
if (!empty($errors)) {
return $this->handle_response(false, nl2br(trim($errors)), array('badges' => array('upgrade-unsafe-http-calls')));
}
$this->save_settings(array(
'aiowps_upgrade_unsafe_http_calls' => isset($data['aiowps_upgrade_unsafe_http_calls']) ? '1' : '',
'aiowps_upgrade_unsafe_http_calls_url_exceptions' => $upgrade_unsafe_http_calls_url_exceptions
));
return $this->handle_response(true, '', array('badges' => array('upgrade-unsafe-http-calls')));
}
/**
* Render the PHP firewall rules for the legacy UDC theme.
*
* @return array
*/
public function get_php_firewall_contents() {
global $aio_wp_security;
$GLOBALS['aiowps_feature_mgr'] = $this->get_feature_mgr_object();
$php_firewall_data = $this->get_php_firewall_data();
$content = $aio_wp_security->include_template('/wp-admin/firewall/php-firewall-rules.php', true, compact('php_firewall_data'));
return array(
'status' => 'success',
'content' => $php_firewall_data['no_firewall'] . $content,
);
}
/**
* Render the .htaccess firewall rules for the legacy UDC theme.
*
* @return array
*/
public function get_htaccess_contents() {
global $aio_wp_security;
$GLOBALS['aiowps_feature_mgr'] = $this->get_feature_mgr_object();
$htaccess_rules_data = $this->get_htaccess_rules_data();
$content = $aio_wp_security->include_template('/wp-admin/firewall/htaccess-firewall-rules.php', true, compact('htaccess_rules_data'));
return array(
'status' => 'success',
'content' => $content,
);
}
/**
* Render the Block & Allow Lists for the legacy UDC theme.
*
* @return array
*/
public function get_block_allow_lists_contents() {
global $aio_wp_security;
/* Needed for submit_button() */
require_once(ABSPATH . 'wp-admin/includes/template.php');
$GLOBALS['aiowps_feature_mgr'] = $this->get_feature_mgr_object();
$block_allowlist_data = $this->get_block_allow_lists_data();
$content = $aio_wp_security->include_template('wp-admin/firewall/block-and-allow-lists.php', true, $block_allowlist_data);
return array(
'status' => 'success',
'content' => $content,
);
}
/**
* Render the Advanced Settings for the legacy UDC theme.
*
* @return array
*/
public function get_advanced_settings_contents() {
global $aio_wp_security;
$GLOBALS['aiowps_feature_mgr'] = $this->get_feature_mgr_object();
$advanced_settings_data = $this->get_firewall_advanced_settings_data();
$content = $aio_wp_security->include_template('wp-admin/firewall/advanced-settings.php', true, compact('advanced_settings_data'));
return array(
'status' => 'success',
'content' => $content,
);
}
/**
* Return data for the advanced firewall.
*
* @return array
*/
public function get_firewall_advanced_settings_data() {
global $aio_wp_security;
$aiowps_upgrade_unsafe_http_calls = $aio_wp_security->configs->get_value('aiowps_upgrade_unsafe_http_calls');
$aiowps_upgrade_unsafe_http_calls_url_exceptions = $aio_wp_security->configs->get_value('aiowps_upgrade_unsafe_http_calls_url_exceptions');
return array(
'aiowps_upgrade_unsafe_http_calls' => $aiowps_upgrade_unsafe_http_calls,
'aiowps_upgrade_unsafe_http_calls_url_exceptions' => $aiowps_upgrade_unsafe_http_calls_url_exceptions,
);
}
/**
* Return data for the allow & block lists.
*
* @return array
*/
public function get_block_allow_lists_data() {
global $aio_wp_security;
$aiowps_enable_blacklisting = $aio_wp_security->configs->get_value('aiowps_enable_blacklisting');
$aiowps_banned_ip_addresses = $aio_wp_security->configs->get_value('aiowps_banned_ip_addresses');
$aiowps_banned_user_agents = $aio_wp_security->configs->get_value('aiowps_banned_user_agents');
$aiowps_firewall_allow_list = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::ALLOW_LIST);
$allowlist = $aiowps_firewall_allow_list::get_ips();
return array(
'aiowps_enable_blacklisting' => $aiowps_enable_blacklisting,
'aiowps_banned_ip_addresses' => $aiowps_banned_ip_addresses,
'aiowps_banned_user_agents' => $aiowps_banned_user_agents,
'allowlist' => $allowlist,
);
}
/**
* Return data for the .htaccess rules.
*
* @return array
*/
public function get_htaccess_rules_data() {
global $aio_wp_security;
$aiowps_enable_basic_firewall = $aio_wp_security->configs->get_value('aiowps_enable_basic_firewall');
$aiowps_max_file_upload_size = $aio_wp_security->configs->get_value('aiowps_max_file_upload_size');
$aiowps_block_debug_log_file_access = $aio_wp_security->configs->get_value('aiowps_block_debug_log_file_access');
$aiowps_disable_index_views = $aio_wp_security->configs->get_value('aiowps_disable_index_views');
return array(
'aiowps_enable_basic_firewall' => $aiowps_enable_basic_firewall,
'aiowps_max_file_upload_size' => $aiowps_max_file_upload_size,
'aiowps_block_debug_log_file_access' => $aiowps_block_debug_log_file_access,
'aiowps_disable_index_views' => $aiowps_disable_index_views,
);
}
/**
* Return data for the PHP firewall.
*
* @return array
*/
public function get_php_firewall_data() {
global $aio_wp_security, $aiowps_firewall_config, $aiowps_feature_mgr;
$is_udc_request = AIOS_Helper::is_updraft_central_request();
$block_request_methods = array_map('strtolower', AIOS_Abstracted_Ids::get_firewall_block_request_methods());
$no_firewall_notice = '';
$user_roles = array();
// Load required data from config
if (!empty($aiowps_firewall_config)) {
// firewall config is available
$methods = $aiowps_firewall_config->get_value('aiowps_6g_block_request_methods');
if (empty($methods)) {
$methods = array();
}
$blocked_query = (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_query');
$blocked_request = (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_request');
$blocked_referrers = (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_referrers');
$blocked_agents = (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_agents');
if (empty($methods) && (!$blocked_query && !$blocked_request && !$blocked_referrers && !$blocked_agents) && '1' == $aio_wp_security->configs->get_value('aiowps_enable_6g_firewall')) {
$aio_wp_security->configs->set_value('aiowps_enable_6g_firewall', '');
$aio_wp_security->configs->save_config();
$aiowps_feature_mgr->check_feature_status_and_recalculate_points();
}
} else {
if ($is_udc_request) {
ob_start();
}
?>
<div class="notice notice-error">
<p><strong><?php esc_html_e('All-In-One Security', 'all-in-one-wp-security-and-firewall'); ?></strong></p>
<p><?php esc_html_e('We were unable to access the firewall\'s configuration file:', 'all-in-one-wp-security-and-firewall');?></p>
<pre style="max-width: 100%;background-color: #f0f0f0;border: #ccc solid 1px;padding: 10px;white-space: pre-wrap;"><?php echo esc_html(AIOWPSecurity_Utility_Firewall::get_firewall_rules_path() . 'settings.php'); ?></pre>
<p><?php esc_html_e('As a result, the firewall will be unavailable.', 'all-in-one-wp-security-and-firewall');?></p>
<p><?php esc_html_e('Please check your PHP error log for further information.', 'all-in-one-wp-security-and-firewall');?></p>
<p><?php esc_html_e('If you\'re unable to locate your PHP log file, please contact your web hosting company to ask them where it can be found on their setup.', 'all-in-one-wp-security-and-firewall');?></p>
</div>
<?php
if ($is_udc_request) {
$no_firewall_notice .= ob_get_clean();
}
//set default variables
$methods = array();
$blocked_query = false;
$blocked_request = false;
$blocked_referrers = false;
$blocked_agents = false;
}
$aiowps_enable_6g_firewall = $aio_wp_security->configs->get_value('aiowps_enable_6g_firewall');
$advanced_options_disabled = '1' != $aiowps_enable_6g_firewall;
$settings = array_merge(array('methods' => $methods), compact('aiowps_enable_6g_firewall', 'blocked_query', 'blocked_request', 'blocked_referrers', 'blocked_agents', 'block_request_methods', 'aiowps_firewall_config', 'advanced_options_disabled'));
$aiowps_enable_pingback_firewall = $aiowps_firewall_config->get_value('aiowps_enable_pingback_firewall');
$aiowps_disable_xmlrpc_pingback_methods = $aio_wp_security->configs->get_value('aiowps_disable_xmlrpc_pingback_methods');
$aiowps_disable_rss_and_atom_feeds = $aio_wp_security->configs->get_value('aiowps_disable_rss_and_atom_feeds');
$aiowps_forbid_proxy_comments = $aiowps_firewall_config->get_value('aiowps_forbid_proxy_comments');
$aiowps_deny_bad_query_strings = $aiowps_firewall_config->get_value('aiowps_deny_bad_query_strings');
$aiowps_advanced_char_string_filter = $aiowps_firewall_config->get_value('aiowps_advanced_char_string_filter');
$aiowps_disallow_unauthorized_rest_requests = $aio_wp_security->configs->get_value('aiowps_disallow_unauthorized_rest_requests');
$aios_roles_disallowed_rest_requests = $aio_wp_security->configs->get_value('aios_roles_disallowed_rest_requests');
$aios_whitelisted_rest_routes = $aio_wp_security->configs->get_value('aios_whitelisted_rest_routes');
$aiowps_block_fake_googlebots = $aiowps_firewall_config->get_value('aiowps_block_fake_googlebots');
$aiowps_ban_post_blank_headers = $aiowps_firewall_config->get_value('aiowps_ban_post_blank_headers');
$wp_user_roles = AIOWPSecurity_Utility_Permissions::get_user_roles();
foreach ($wp_user_roles as $role => $role_name) {
$user_roles[] = $role;
}
return array(
'aiowps_enable_pingback_firewall' => $aiowps_enable_pingback_firewall,
'aiowps_disable_xmlrpc_pingback_methods' => $aiowps_disable_xmlrpc_pingback_methods,
'aiowps_disable_rss_and_atom_feeds' => $aiowps_disable_rss_and_atom_feeds,
'aiowps_forbid_proxy_comments' => $aiowps_forbid_proxy_comments,
'aiowps_deny_bad_query_strings' => $aiowps_deny_bad_query_strings,
'aiowps_advanced_char_string_filter' => $aiowps_advanced_char_string_filter,
'aiowps_disallow_unauthorized_rest_requests' => $aiowps_disallow_unauthorized_rest_requests,
'aios_roles_disallowed_rest_requests' => $aios_roles_disallowed_rest_requests,
'aios_whitelisted_rest_routes' => $aios_whitelisted_rest_routes,
'user_roles' => $user_roles,
'aiowps_block_fake_googlebots' => $aiowps_block_fake_googlebots,
'aiowps_ban_post_blank_headers' => $aiowps_ban_post_blank_headers,
'ng_settings' => $settings,
'no_firewall' => $no_firewall_notice,
);
}
}
@@ -0,0 +1,133 @@
<?php
if (!defined('ABSPATH')) die('No direct access allowed');
if (trait_exists('AIOWPSecurity_Ip_Commands_Trait')) return;
trait AIOWPSecurity_Ip_Commands_Trait {
/**
* Unlocks an IP.
*
* @param array $data Contains the IP address to be unlocked.
*
* @return array
*/
public function unlock_ip($data) {
if (!isset($data['ip'])) {
return $this->handle_response(false, __('No IP provided.', 'all-in-one-wp-security-and-firewall'));
}
if (!filter_var($data['ip'], FILTER_VALIDATE_IP)) {
return $this->handle_response(false, __('Invalid IP provided.', 'all-in-one-wp-security-and-firewall'));
}
if (!AIOWPSecurity_Utility::unlock_ip($data['ip'])) {
return $this->handle_response(false, __('Failed to unlock the selected IP address.', 'all-in-one-wp-security-and-firewall'));
} else {
return $this->handle_response(true, __('The selected IP address was unlocked successfully.', 'all-in-one-wp-security-and-firewall'));
}
}
/**
* Unblacklists an IP.
*
* @param array $data Contains the IP address to be unblacklisted.
*
* @return array
*/
public function unblacklist_ip($data) {
if (!isset($data['ip'])) {
return $this->handle_response(false, __('No IP provided.', 'all-in-one-wp-security-and-firewall'));
}
if (!filter_var($data['ip'], FILTER_VALIDATE_IP)) {
return $this->handle_response(false, __('Invalid IP provided.', 'all-in-one-wp-security-and-firewall'));
}
if (!AIOWPSecurity_Utility::unblacklist_ip($data['ip'])) {
return $this->handle_response(false, __('Failed to unblacklist the selected IP address.', 'all-in-one-wp-security-and-firewall'));
} else {
return $this->handle_response(true, __('The selected IP address was unblacklisted successfully.', 'all-in-one-wp-security-and-firewall'));
}
}
/**
* Unblocks an IP by permanent block record ID.
*
* @param array $data Contains the ID of the entry in the AIOWPSEC_TBL_PERM_BLOCK table.
*
* @return array
*/
public function blocked_ip_list_unblock_ip($data) {
if (!isset($data['id'])) {
return $this->handle_response(false, __('Invalid blocked IP ID provided.', 'all-in-one-wp-security-and-firewall'));
}
include_once AIO_WP_SECURITY_PATH . '/admin/wp-security-list-permanent-blocked-ip.php'; // For rendering the AIOWPSecurity_List_Table
$blocked_ip_list = new AIOWPSecurity_List_Blocked_IP(); // For rendering the AIOWPSecurity_List_Table
$result = $blocked_ip_list->unblock_ip_address($data['id']);
if (false === $result) {
$message = __('Failed to unblock and delete the selected record(s).', 'all-in-one-wp-security-and-firewall');
} else {
$message = __('Successfully unblocked and deleted the selected record(s).', 'all-in-one-wp-security-and-firewall');
}
return $this->handle_response(true, $message);
}
/**
* Locks an IP.
*
* @param array $data Contains the IP address to be locked.
*
* @return array
*/
public function lock_ip($data) {
if (!isset($data['ip'])) {
return $this->handle_response(false, __('No IP provided.', 'all-in-one-wp-security-and-firewall'));
}
if (!filter_var($data['ip'], FILTER_VALIDATE_IP)) {
return $this->handle_response(false, __('Invalid IP provided.', 'all-in-one-wp-security-and-firewall'));
}
if (!isset($data['lock_reason'])) {
return $this->handle_response(false, __('No lockout reason provided.', 'all-in-one-wp-security-and-firewall'));
}
AIOWPSecurity_Utility::lock_ip($data['ip'], $data['lock_reason']);
return $this->handle_response(true, __('The selected IP address is now temporarily locked.', 'all-in-one-wp-security-and-firewall'));
}
/**
* Blacklists an IP.
*
* @param array $data Contains the IP address to be blacklisted.
*
* @return array
*/
public function blacklist_ip($data) {
if (!isset($data['ip'])) {
return $this->handle_response(false, __('No IP provided.', 'all-in-one-wp-security-and-firewall'));
}
if (!filter_var($data['ip'], FILTER_VALIDATE_IP)) {
return $this->handle_response(false, __('Invalid IP provided.', 'all-in-one-wp-security-and-firewall'));
}
$result = AIOWPSecurity_Utility::blacklist_ip($data['ip']);
if (is_wp_error($result)) {
return $this->handle_response(false, nl2br($result->get_error_message()));
} else {
return $this->handle_response(true, __('The selected IP address has been added to the blacklist.', 'all-in-one-wp-security-and-firewall'));
}
}
}
@@ -0,0 +1,345 @@
<?php
if (!defined('ABSPATH')) die('No direct access allowed');
if (trait_exists('AIOWPSecurity_Log_Commands_Trait')) return;
trait AIOWPSecurity_Log_Commands_Trait {
/**
* Deletes an audit log.
*
* @param array $data Contains the ID of the log to be deleted.
*
* @return array
*/
public function delete_audit_log($data) {
if (!isset($data['id'])) {
return $this->handle_response(false, AIOWPSecurity_Admin_Menu::show_msg_error_st(__('No audit log ID provided.', 'all-in-one-wp-security-and-firewall'), true));
}
include_once AIO_WP_SECURITY_PATH.'/admin/wp-security-list-audit.php';
$audit_log_list = new AIOWPSecurity_List_Audit_Log();
return $this->handle_response(true, $audit_log_list->delete_audit_event_records($data['id']));
}
/**
* Deletes an IP lockout record.
*
* @param array $data Contains the ID of the entry in the AIOWPSEC_TBL_LOGIN_LOCKOUT table.
*
* @return array
*/
public function delete_locked_ip_record($data) {
if (!isset($data['id'])) {
return $this->handle_response(false, AIOWPSecurity_Admin_Menu::show_msg_error_st(__('No locked IP record ID provided.', 'all-in-one-wp-security-and-firewall'), true));
}
include_once AIO_WP_SECURITY_PATH . '/admin/wp-security-list-locked-ip.php';
$locked_ip_list = new AIOWPSecurity_List_Locked_IP();
$result = $locked_ip_list->delete_lockout_records($data['id']);
return $this->handle_response(true, $result);
}
/**
* Clear debug logs
*
* @return array
*/
public function clear_debug_logs() {
global $aio_wp_security;
$ret = $aio_wp_security->debug_logger->clear_logs();
if (is_wp_error($ret)) {
return $this->handle_response(false, AIOWPSecurity_Admin_Menu::show_msg_error_st(esc_html($ret->get_error_message()).'<p>'.esc_html($ret->get_error_data()).'</p>', true));
} else {
return $this->handle_response(true, AIOWPSecurity_Admin_Menu::show_msg_updated_st(__('The debug logs have been cleared.', 'all-in-one-wp-security-and-firewall'), true));
}
}
/**
* Renders the audit log tab content.
*
* This function handles the rendering of the audit log tab content based on the
* provided data via AJAX request. The data is used to filter the audit log or perform actions
*
* @access public
* @return void
*/
public function render_audit_log_tab() {
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- PCP warning. Nonce checked in previous function.
if (empty($_POST['data'])) return;
// phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput -- PCP warning. Nonce checked in previous function and sanitization done at later.
$data = wp_unslash($_POST['data']);
// Needed for rendering the audit log table
include_once(AIO_WP_SECURITY_PATH.'/admin/wp-security-list-audit.php');
$audit_log_list = new AIOWPSecurity_List_Audit_Log($data);
$audit_log_list->ajax_response();
}
/**
* Exports the audit logs as a CSV file and sends the data as an AJAX response.
*
* This function retrieves audit log data, prepares it for export, and generates a CSV string.
* The CSV data is then sent back as part of an AJAX response, along with the filename for the CSV file.
*
* @return array
*/
public function export_audit_logs() {
// Needed for rendering the audit log table
include_once(AIO_WP_SECURITY_PATH.'/admin/wp-security-list-audit.php');
$audit_log_list = new AIOWPSecurity_List_Audit_Log();
$audit_log_list->prepare_items(true);
$export_keys = array(
'id' => 'ID',
'created' => __('Date and time', 'all-in-one-wp-security-and-firewall'),
'level' => __('Level', 'all-in-one-wp-security-and-firewall'),
'network_id' => __('Network ID', 'all-in-one-wp-security-and-firewall'),
'site_id' => __('Site ID', 'all-in-one-wp-security-and-firewall'),
'username' => __('Username', 'all-in-one-wp-security-and-firewall'),
'ip' => __('IP', 'all-in-one-wp-security-and-firewall'),
'event_type' => __('Event', 'all-in-one-wp-security-and-firewall'),
'details' => __('Details', 'all-in-one-wp-security-and-firewall'),
'stacktrace' => __('Stack trace', 'all-in-one-wp-security-and-firewall')
);
$title = 'audit_event_logs.csv';
ob_start();
AIOWPSecurity_Admin_Init::aiowps_output_csv($audit_log_list->items, $export_keys, $title);
$data = ob_get_clean();
return array(
'title' => $title,
'data' => $data
);
}
/**
* Initializing the WP List API, since UDC commands do not load all parts of WP.
*
* @return void
*/
private function init_wp_list() {
if (!function_exists('submit_button')) {
require_once(ABSPATH . 'wp-admin/includes/template.php');
}
if (!function_exists('render_screen_reader_content')) {
require_once(ABSPATH . 'wp-admin/includes/class-wp-screen.php');
}
if (!function_exists('get_column_headers')) {
require_once(ABSPATH . 'wp-admin/includes/screen.php');
}
}
/**
* Returns the data for downloading the audit log.
*
* @return array|WP_Error
*/
public function process_audit_log_export() {
if (!AIOWPSecurity_Utility_Permissions::has_manage_cap()) {
return new WP_Error(esc_html__('Sorry, you do not have enough privilege to execute the requested action.', 'all-in-one-wp-security-and-firewall'));
}
$this->init_wp_list();
return $this->export_audit_logs();
}
/**
* Returns the HTML for the audit log.
*
* @return array
*/
public function get_audit_log_contents() {
global $aio_wp_security;
$this->init_wp_list();
// Needed for rendering the audit log table
include_once AIO_WP_SECURITY_PATH . '/admin/wp-security-list-audit.php';
$data = array();
// phpcs:disable WordPress.Security.NonceVerification.Recommended -- PCP warning. Processing form data without nonce verification. No nonce.
if (isset($_GET['event-filter'])) $data['event-filter'] = sanitize_text_field(wp_unslash($_GET['event-filter'])); // Failed logins and logins only to show as audit log
$audit_log_list = new AIOWPSecurity_List_Audit_Log($data);
$tab = isset($_GET["tab"]) ? sanitize_text_field(wp_unslash($_GET["tab"])) : '';
$page = isset($_GET['page']) ? sanitize_text_field(wp_unslash($_GET['page'])) : '';
// phpcs:enable WordPress.Security.NonceVerification.Recommended -- PCP warning. Processing form data without nonce verification. No nonce.
$content = $aio_wp_security->include_template('wp-admin/dashboard/audit-logs.php', true, array('audit_log_list' => $audit_log_list, 'page' => $page, 'tab' => $tab));
return array(
'status' => 'success',
'content' => $content,
);
}
/**
* Deletes entry from audit log.
*
* @param array $data Table config data.
*
* @return array|WP_Error
*/
public function do_delete_audit_log($data) {
if (!AIOWPSecurity_Utility_Permissions::has_manage_cap()) {
return new WP_Error(esc_html__('Sorry, you do not have enough privilege to execute the requested action.', 'all-in-one-wp-security-and-firewall'));
}
$this->init_wp_list();
if (!class_exists('AIOWPSecurity_Admin_Menu')) {
include_once AIO_WP_SECURITY_PATH . '/admin/wp-security-admin-menu.php';
}
return $this->delete_audit_log($data);
}
/**
* Renders audit log after actions (delete/orderby, block/unblock, etc.)
*
* @param array $data Table config data.
*
* @return array
*/
public function do_render_audit_log_tab($data) {
$this->init_wp_list();
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- PCP warning. Nonce checked in previous function.
if (empty($data)) return array();
// phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput -- PCP warning. Nonce checked in previous function and sanitization done at later.
$data = wp_unslash($data);
if (!class_exists('AIOWPSecurity_Admin_Menu')) {
include_once AIO_WP_SECURITY_PATH . '/admin/wp-security-admin-menu.php';
}
// Needed for rendering the audit log table
include_once(AIO_WP_SECURITY_PATH.'/admin/wp-security-list-audit.php');
$audit_log_list = new AIOWPSecurity_List_Audit_Log($data);
return $audit_log_list->ajax_response(true);
}
/**
* Parses raw audit log data for human-readable output.
*
* @param AIOWPSecurity_List_Audit_Log $audit_log_list Audit log object.
* @param array $data Raw audit log data.
*
* @return array
*/
private function parse_audit_log_data($audit_log_list, $data) {
$items = array();
foreach ($data as $db_item) {
if (empty($db_item)) {
continue;
}
$item = array();
foreach ($db_item as $key => $value) {
switch ($key) {
case 'created':
$item[$key] = AIOWPSecurity_Utility::convert_timestamp($value);
break;
case 'event_type':
$item[$key] = $audit_log_list->column_event_type($db_item);
break;
case 'details':
$item[$key] = $audit_log_list->column_details($db_item);
break;
case 'stacktrace':
$item[$key] = $audit_log_list->column_stacktrace($db_item);
break;
default:
$item[$key] = $value;
break;
}
}
$items[] = $item;
}
return $items;
}
/**
* Returns the data for the audit log table.
*
* @param array $data Configuration data.
*
* @return array
*/
public function get_audit_log_data($data) {
// phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput -- PCP warning. Nonce checked in previous function and sanitization done at later.
$data = isset($data) ? wp_unslash($data) : array();
$data = isset($data['data']) ? $data['data'] : $data;
$this->init_wp_list();
$final_column = array();
// Needed for rendering the audit log table
include_once(AIO_WP_SECURITY_PATH.'/admin/wp-security-list-audit.php');
$audit_log_list = new AIOWPSecurity_List_Audit_Log($data);
$audit_log_list->prepare_items();
list($columns, $hidden) = $audit_log_list->get_column_info();
foreach ($columns as $column_key => $column_display_name) {
if ('cb' !== $column_key) {
if (!in_array($column_key, $hidden, true)) {
$final_column[$column_key] = array('label' => $column_display_name);
}
}
}
$audit_log_items = isset($audit_log_list->items) ? $audit_log_list->items : array();
foreach ($audit_log_items as $key => $item) {
$ip = isset($item['ip']) ? $item['ip'] : '';
if ('' !== $ip) {
$audit_log_items[$key]['is_ip_locked'] = AIOWPSecurity_Utility::check_locked_ip($ip, 'audit-log');
$audit_log_items[$key]['is_ip_blacklisted'] = AIOWPSecurity_Utility::check_blacklist_ip($ip);
}
}
$items = $this->parse_audit_log_data($audit_log_list, $audit_log_items);
$bulk_actions = $audit_log_list->get_bulk_actions();
$paged = !empty($data['paged']) ? (int) $data['paged'] : 1;
AIOWPSecurity_Audit_Events::setup_event_types();
return array(
'audit_log_data' => array(
'bulk_actions' => $bulk_actions,
'event_types' => AIOWPSecurity_Audit_Events::$event_types,
'log_levels' => AIOWPSecurity_Audit_Events::$log_levels,
'columns' => $final_column,
'items' => $items,
'is_multisite' => is_multisite(),
'pagination' => array('page' => $paged, 'pages' => $audit_log_list->get_pagination_arg('total_pages'), 'results' => $audit_log_list->get_pagination_arg('total_items')),
),
);
}
}
@@ -0,0 +1,562 @@
<?php
if (!defined('ABSPATH')) die('No direct access allowed');
if (trait_exists('AIOWPSecurity_Settings_Commands_Trait')) return;
trait AIOWPSecurity_Settings_Commands_Trait {
/**
* Performs the action to disable all security features.
*
* This method disables all security features provided by the AIOWPSecurity_Settings_Tasks class.
*
* @return array An associative array containing the status and messages of the operation.
* - 'status' : (string) The status of the operation, either 'success' or 'error'.
* - 'messages' : (array) An array of messages generated during the operation.
* Each message is represented as a string.
*/
public function perform_disable_all_features() {
$msg = AIOWPSecurity_Settings_Tasks::disable_all_security_features();
$success = true;
$info = array();
$message = '';
if (isset($msg['updated'])) {
$message = $msg['updated'];
}
if (isset($msg['error'])) {
$message = __('Some of the security features could not be disabled.', 'all-in-one-wp-security-and-firewall');
$success = false;
foreach ($msg['error'] as $error_message) {
$info[] = $error_message;
}
}
return $this->handle_response($success, $message, array('info' => $info));
}
/**
* Performs the action to disable all firewall rules.
*
* This method disables all firewall rules provided by the AIOWPSecurity_Settings_Tasks class.
*
* @return array An associative array containing the status and message of the operation.
* - 'status' : (string) The status of the operation, either 'success' or 'error'.
* - 'message' : (string) A message indicating the outcome of the operation.
* If the operation is successful, it contains an update message.
* If there is an error, it contains an error message.
*/
public function perform_disable_all_firewall_rules() {
$msg = AIOWPSecurity_Settings_Tasks::disable_all_firewall_rules();
$success = true;
$message = '';
if (isset($msg['updated'])) {
$message = $msg['updated'];
} elseif (isset($msg['error'])) {
$message = $msg['error'];
$success = false;
}
return $this->handle_response($success, $message);
}
/**
* Performs the action to reset all settings.
*
* This method resets all settings provided by the AIOWPSecurity_Settings_Tasks class to their default values.
*
* @return array An associative array containing the status and message of the operation.
* - 'status' : (string) The status of the operation, either 'success' or 'error'.
* - 'message' : (string) A message indicating the outcome of the operation.
* If the operation is successful, it contains an update message.
* If there is an error, it contains an error message.
*/
public function perform_reset_all_settings() {
$msg = AIOWPSecurity_Settings_Tasks::reset_all_settings();
$success = true;
$message = '';
if (isset($msg['updated'])) {
$message = $msg['updated'];
} elseif (isset($msg['error'])) {
$message = $msg['error'];
$success = false;
}
return $this->handle_response($success, $message);
}
/**
* Performs the action to save debug settings.
*
* This method updates the debug settings in the AIOWPSecurity_Configs instance based on the provided data.
*
* @param array $data An associative array containing the data to update the debug settings.
* - 'aiowps_enable_debug': (bool) Indicates whether debug mode should be enabled.
* @return array An associative array containing the status and message of the operation.
* - 'status' : (string) The status of the operation, which is always 'success'.
* - 'message' : (string) A message indicating that the settings have been updated successfully.
*/
public function perform_save_debug_settings($data) {
global $aio_wp_security;
$aio_wp_security->configs->set_value('aiowps_enable_debug', '1' === $data["aiowps_enable_debug"] ? '1' : '', true);
return $this->handle_response(true);
}
/**
* Performs the action to backup the .htaccess file.
*
* This method creates a backup of the .htaccess file and renames it with a random prefix.
* It also provides a message indicating the outcome of the backup operation.
*
* @global object $aio_wp_security The global instance of the All-in-One WP Security & Firewall plugin.
* @return array An associative array containing the status and message of the backup operation.
* - 'status' : (string) The status of the operation, which can be 'success' or 'error'.
* - 'message' : (string) A message indicating the outcome of the backup operation.
*/
public function perform_backup_htaccess_file() {
global $aio_wp_security;
$home_path = AIOWPSecurity_Utility_File::get_home_path();
$htaccess_path = $home_path . '.htaccess';
$result = AIOWPSecurity_Utility_File::backup_and_rename_htaccess($htaccess_path); //Backup the htaccess file
$extra_args = array();
if ($result) {
$aiowps_backup_dir = WP_CONTENT_DIR.'/'.AIO_WP_SECURITY_BACKUPS_DIR_NAME;
$success = true;
$message = __('Your .htaccess file was successfully backed up.', 'all-in-one-wp-security-and-firewall');
$extra_args['data'] = file_get_contents($aiowps_backup_dir.'/'. $result .'.txt');
$extra_args['title'] = $result;
} else {
$aio_wp_security->debug_logger->log_debug("htaccess - Backup operation failed!", 4);
$success = false;
$message = __('htaccess backup failed.', 'all-in-one-wp-security-and-firewall');
}
return $this->handle_response($success, $message, array('extra_args' => $extra_args));
}
/**
* Performs the action to restore the .htaccess file.
*
* This method restores the .htaccess file using the provided data, which includes the contents of the .htaccess file.
* It also verifies that the file chosen has valid contents relevant to the .htaccess file.
*
* @global object $aio_wp_security The global instance of the All-in-One WP Security & Firewall plugin.
* @param array $data An associative array containing the data needed to restore the .htaccess file.
* - 'aiowps_htaccess_file' : (string) The name of the .htaccess file to restore from.
* - 'aiowps_htaccess_file_contents' : (string) The contents of the .htaccess file to restore.
* @return array An associative array containing the status and message of the restore operation.
* - 'status' : (string) The status of the operation, which can be 'success' or 'error'.
* - 'message' : (string) A message indicating the outcome of the restore operation.
*/
public function perform_restore_htaccess_file($data) {
global $aio_wp_security;
$success = true;
$home_path = AIOWPSecurity_Utility_File::get_home_path();
$htaccess_path = $home_path . '.htaccess';
if (empty($data['aiowps_htaccess_file']) && empty($data['aiowps_htaccess_file_contents'])) {
$message = __('Please choose a valid .htaccess to restore from.', 'all-in-one-wp-security-and-firewall');
$success = false;
} else {
$htaccess_file_contents = trim(stripslashes($data['aiowps_htaccess_file_contents']));
//Verify that file chosen has contents which are relevant to .htaccess file
$is_htaccess = AIOWPSecurity_Utility_Htaccess::check_if_htaccess_contents($htaccess_file_contents);
if (1 == $is_htaccess) {
if (!file_put_contents($htaccess_path, $htaccess_file_contents)) {
//Failed to make a backup copy
$aio_wp_security->debug_logger->log_debug("htaccess - Restore from .htaccess operation failed.", 4);
$message = __('The restoration of the .htaccess file failed; please attempt to restore the .htaccess file manually using FTP.', 'all-in-one-wp-security-and-firewall');
$success = false;
} else {
$message = __('Your .htaccess file has successfully been restored.', 'all-in-one-wp-security-and-firewall');
}
} else {
$aio_wp_security->debug_logger->log_debug("htaccess restore failed - Contents of restore file appear invalid.", 4);
$success = false;
$message = __('The restoration .htaccess file has failed, please check the contents of the file you are trying to restore from.', 'all-in-one-wp-security-and-firewall');
}
}
return $this->handle_response($success, $message);
}
/**
* Performs the action to restore the wp-config.php file.
*
* This method restores the wp-config.php file using the provided data, which includes the contents of the wp-config.php file.
* It also verifies that the file chosen is a valid wp-config.php file.
*
* @global object $aio_wp_security The global instance of the All-in-One WP Security & Firewall plugin.
* @param array $data An associative array containing the data needed to restore the wp-config.php file.
* - 'aiowps_wp_config_file' : (string) The name of the wp-config.php file to restore from.
* - 'aiowps_wp_config_file_contents' : (string) The contents of the wp-config.php file to restore.
* @return array An associative array containing the status and message of the restore operation.
* - 'status' : (string) The status of the operation, which can be 'success' or 'error'.
* - 'message' : (string) A message indicating the outcome of the restore operation.
*/
public function perform_restore_wp_config_file($data) {
global $aio_wp_security;
$success = true;
if (empty($data['aiowps_wp_config_file']) && empty($data['aiowps_wp_config_file_contents'])) {
$message = __('Please choose a wp-config.php file to restore from.', 'all-in-one-wp-security-and-firewall');
$success = false;
} else {
$wp_config_file_contents = trim(stripslashes($data['aiowps_wp_config_file_contents']));
//Verify that file chosen is a wp-config.file
$is_wp_config = AIOWPSecurity_Utility_File::check_if_wp_config_contents($wp_config_file_contents);
if ($is_wp_config) {
$active_root_wp_config = AIOWPSecurity_Utility_File::get_wp_config_file_path();
if (!file_put_contents($active_root_wp_config, $wp_config_file_contents)) {
//Failed to make a backup copy
$aio_wp_security->debug_logger->log_debug("wp-config.php - Restore from backed up wp-config operation failed.", 4);
$message = __('The restoration of the wp-config.php file failed, please attempt to restore this file manually using FTP.', 'all-in-one-wp-security-and-firewall');
$success = false;
} else {
$message =__('Your wp-config.php file has successfully been restored.', 'all-in-one-wp-security-and-firewall');
}
} else {
$aio_wp_security->debug_logger->log_debug("wp-config.php restore failed - Contents of restore file appear invalid.", 4);
$message = __('The restoration of the wp-config.php file failed, please check the contents of the file you are trying to restore from.', 'all-in-one-wp-security-and-firewall');
$success = false;
}
}
return $this->handle_response($success, $message);
}
/**
* Performs the action to delete plugin settings.
*
* This method deletes specific plugin settings based on the provided data.
*
* @param array $data An associative array containing the data needed to delete plugin settings.
* - 'aiowps_on_uninstall_delete_db_tables' : (string) Indicates whether to delete plugin database tables on uninstallation.
* - 'aiowps_on_uninstall_delete_configs' : (string) Indicates whether to delete plugin configuration settings on uninstallation.
* @return array An associative array containing the status and message of the delete operation.
* - 'status' : (string) The status of the operation, which can be 'success'.
* - 'message' : (string) A message indicating that the plugin settings have been successfully deleted.
*/
public function perform_delete_plugin_settings($data) {
$options = array();
//Save settings
$options['aiowps_on_uninstall_delete_db_tables'] = isset($data['aiowps_on_uninstall_delete_db_tables']) ? '1' : '';
$options['aiowps_on_uninstall_delete_configs'] = isset($data['aiowps_on_uninstall_delete_configs']) ? '1' : '';
$this->save_settings($options);
return $this->handle_response(true);
}
/**
* Performs the action to remove WordPress version information settings.
*
* This method sets the option to remove WordPress version information meta tags based on the provided data.
*
* @param array $data An associative array containing the data needed to configure the removal of WordPress version information.
* - 'aiowps_remove_wp_generator_meta_info' : (string) Indicates whether to remove WordPress version information meta tags.
* @return array An associative array containing the status, message, and additional badges related to the removal of WordPress version information.
* - 'status' : (string) The status of the operation, which can be 'success'.
* - 'message' : (string) A message indicating that the settings have been successfully updated.
* - 'badges' : (array) An array containing feature IDs and HTML for additional badges.
*/
public function perform_remove_wp_version_info_settings($data) {
global $aio_wp_security;
$aio_wp_security->configs->set_value('aiowps_remove_wp_generator_meta_info', '1' === $data["aiowps_remove_wp_generator_meta_info"] ? '1' : '', true);
return $this->handle_response(true, '', array('badges' => array('wp-generator-meta-tag')));
}
/**
* Performs the action to restore AIOWPS settings from an imported file.
*
* This method restores AIOWPS settings from the provided data representing an imported file.
*
* @param array $data An associative array containing the data needed to restore AIOWPS settings.
* - 'aiowps_import_settings_file' : (string) The name of the file containing the AIOWPS settings.
* - 'aiowps_import_settings_file_contents': (string) The contents of the file containing the AIOWPS settings.
* @return array An associative array containing the status and messages related to the restoration of AIOWPS settings.
* - 'status' : (string) The status of the operation, which can be 'success' or 'error'.
* - 'messages' : (array) An array of messages indicating the outcome of the restoration process.
* - 'redirect_url' : (string|null) The URL to redirect to after the restoration process, if applicable.
*/
public function perform_restore_aiowps_settings($data) {
global $aio_wp_security, $simba_two_factor_authentication;
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
$success = true;
$info = array();
$extra_args = array();
$msg_updated = __('Your AIOS settings were successfully imported.', 'all-in-one-wp-security-and-firewall');
$msg_error = sprintf(__('Could not write to the %s file.', 'all-in-one-wp-security-and-firewall'), AIOWPSecurity_Utility_File::get_home_path().'.htaccess') . ' ' . __('Please check the file permissions.', 'all-in-one-wp-security-and-firewall');
if (empty($data['aiowps_import_settings_file']) && empty($data['aiowps_import_settings_file_contents'])) {
$success = false;
$message = __('Please choose a file to import your settings from.', 'all-in-one-wp-security-and-firewall');
} else {
// Let's get the uploaded import file contents
$import_file_contents = trim($data['aiowps_import_settings_file_contents']); // stripslashes not required wp_unslash applied already AIOWPSecurity_Ajax::set_data
// Verify that file chosen has valid AIOS settings contents
$aiowps_settings_file_contents = AIOWPSecurity_Utility_File::check_if_valid_aiowps_settings_content($import_file_contents);
if ($aiowps_settings_file_contents) {
$is_enabled_cookie_bruteforce_before_import = $aio_wp_security->configs->get_value('aiowps_enable_brute_force_attack_prevention');
// Apply the settings
$settings_array = json_decode($import_file_contents, true);
if (array_key_exists('general', $settings_array)) {
$aiowps_settings_applied = update_option('aio_wp_security_configs', $settings_array['general']);
if (!$aiowps_settings_applied && get_option('aio_wp_security_configs') === $settings_array['general']) {
$aiowps_settings_applied = true;
}
if (is_main_site() && is_super_admin()) {
if (array_key_exists('tfa', $settings_array) && true == $simba_two_factor_authentication->is_tfa_integrated) {
$tfa_settings_applied = $simba_two_factor_authentication->set_configs($settings_array['tfa']);
if (!$tfa_settings_applied && $simba_two_factor_authentication->get_configs() !== $settings_array['tfa']) {
$aiowps_settings_applied = false;
}
}
if (array_key_exists('firewall', $settings_array)) {
$aiowps_settings_applied = $aiowps_settings_applied && $aiowps_firewall_config->set_contents($settings_array['firewall']);
}
}
} else {
$aiowps_settings_applied = update_option('aio_wp_security_configs', $settings_array);
if (!$aiowps_settings_applied && get_option('aio_wp_security_configs') === $settings_array) {
$aiowps_settings_applied = true;
}
}
if (!$aiowps_settings_applied) {
// Failed to import settings
$aio_wp_security->debug_logger->log_debug('Import AIOS settings operation failed.', 4);
$success = false;
$message = __('Import AIOS settings operation failed.', 'all-in-one-wp-security-and-firewall');
} else {
$aio_wp_security->configs->load_config(); // Refresh the configs global variable
//Just in case user submits partial config settings
//Run add_option_values to make sure any missing config items are at least set to default
AIOWPSecurity_Configure_Settings::add_option_values();
$res = true;
if (AIOWPSecurity_Utility::allow_to_write_to_htaccess()) $res = AIOWPSecurity_Utility_Htaccess::write_to_htaccess();
// Now let's refresh the .htaccess file with any modified rules if applicable
$is_enabled_cookie_bruteforce = $aio_wp_security->configs->get_value('aiowps_enable_brute_force_attack_prevention');
if ($is_enabled_cookie_bruteforce_before_import != $is_enabled_cookie_bruteforce && 1 == $is_enabled_cookie_bruteforce) {
$url = 'admin.php?page='.AIOWPSEC_SETTINGS_MENU_SLUG."&tab=settings-file-operations&success=import_settings";
$url .= empty($aio_wp_security->configs->get_value('aiowps_brute_force_secret_word')) ? '' : '&'.$aio_wp_security->configs->get_value('aiowps_brute_force_secret_word').'=1';
$url .= $res ? '' : '&error=write_htaccess';
$extra_args['redirect_url'] = admin_url(sanitize_url($url));
}
$message = $msg_updated;
if (!$res) {
$info[] = $msg_error;
}
}
} else {
// Invalid settings file
$aio_wp_security->debug_logger->log_debug("The contents of your settings file are invalid.", 4);
$success = false;
$message = __('The contents of your settings file are invalid, please check the contents of the file you are trying to import settings from.', 'all-in-one-wp-security-and-firewall');
}
}
$args = array(
'info' => $info,
'extra_args' => $extra_args
);
return $this->handle_response($success, $message, $args);
}
/**
* Performs the action to save IP settings.
*
* This method saves the IP settings based on the provided data.
*
* @param array $data An associative array containing the data needed to save IP settings.
* - 'aiowps_ip_retrieve_method': (string) The ID of the IP retrieval method.
* @return array An associative array containing the status and message related to saving IP settings.
* - 'status': (string) The status of the operation, which can be 'success' or 'error'.
* - 'message': (string|null) A message indicating the outcome of saving IP settings, or null if no message is provided.
*/
public function perform_save_ip_settings($data) {
global $wpdb, $aio_wp_security;
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
$ip_retrieve_method_id = sanitize_text_field($data["aiowps_ip_retrieve_method"]);
$message = false;
if (in_array($ip_retrieve_method_id, array_keys(AIOS_Abstracted_Ids::get_ip_retrieve_methods()))) {
$aio_wp_security->configs->set_value('aiowps_ip_retrieve_method', $ip_retrieve_method_id, true);
$aiowps_firewall_config->set_value('aios_ip_retrieve_method', $ip_retrieve_method_id);
$logged_in_users_table = AIOWPSEC_TBL_LOGGED_IN_USERS;
//Clear logged in list because it might be showing wrong addresses
if (AIOWPSecurity_Utility::is_multisite_install()) {
$current_blog_id = get_current_blog_id();
$wpdb->query($wpdb->prepare("DELETE FROM `{$logged_in_users_table}` WHERE site_id = %d", $current_blog_id));
}
$wpdb->query("DELETE FROM `{$logged_in_users_table}`");
$message = '';
}
return $this->handle_response(true, $message);
}
/**
* Perform saving the wp-config.php file.
*
* This method backs up the wp-config.php file and retrieves its content.
* It returns the status of the operation, the file content, and the backup title.
*
* @return array An array containing the status, file content, and backup title.
*/
public function perform_save_wp_config() {
$wp_config_path = AIOWPSecurity_Utility_File::get_wp_config_file_path();
AIOWPSecurity_Utility_File::backup_and_rename_wp_config($wp_config_path); // Backup the wp_config.php file
$title = "wp-config-backup.txt";
$file_content = file_get_contents($wp_config_path);
$extra_args = array(
'data' => $file_content,
'title' => $title
);
return $this->handle_response(true, false, array('extra_args' => $extra_args));
}
/**
* Perform exporting All-In-One Security settings.
*
* This method exports general settings, firewall settings, and two-factor authentication settings
* if applicable. It then returns the exported data in JSON format along with a title for the export.
*
* @return array An array containing the status, exported data in JSON format, and a title for the export.
*/
public function perform_export_aios_settings() {
global $simba_two_factor_authentication;
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
$config_data = array();
$config_data['general'] = get_option('aio_wp_security_configs');
if (is_main_site() && is_super_admin()) {
$config_data['firewall'] = $aiowps_firewall_config->get_contents();
if (true == $simba_two_factor_authentication->is_tfa_integrated) {
$config_data['tfa'] = $simba_two_factor_authentication->get_configs();
}
}
$output = json_encode($config_data);
$extra_args = array(
'data' => $output,
'title' => 'aiowps_' . current_time('Y-m-d_H-i') . '.txt'
);
return $this->handle_response(true, false, array('extra_args' => $extra_args));
}
/**
* Render the Import/Export settings UI for legacy UDC.
*
* @return array
*/
public function get_import_export_contents() {
global $aio_wp_security;
$content = $aio_wp_security->include_template('wp-admin/settings/settings-file-operations.php', true, array());
return array(
'status' => 'success',
'content' => $content,
);
}
/**
* Render the reset settings UI for legacy UDC.
*
* @return array
*/
public function get_reset_contents() {
global $aio_wp_security;
$content = $aio_wp_security->include_template('wp-admin/settings/general-settings.php', true, array());
return array(
'status' => 'success',
'content' => $content,
);
}
/**
* Return ip address detection data for the advanced settings.
*
* @return array
*/
public function get_ip_address_detection_data() {
global $aio_wp_security;
$ip_retrieve_methods_postfixes = array(
'REMOTE_ADDR' => __('Default - if correct, then this is the best option', 'all-in-one-wp-security-and-firewall'),
'HTTP_CF_CONNECTING_IP' => __("Only use if you're using Cloudflare.", 'all-in-one-wp-security-and-firewall'),
);
$ip_retrieve_methods = array();
foreach (AIOS_Abstracted_Ids::get_ip_retrieve_methods() as $id => $ip_method) {
$ip_retrieve_methods[$id]['ip_method'] = $ip_method;
if (isset($_SERVER[$ip_method])) {
/* translators: %s: IP Method */
$ip_retrieve_methods[$id]['ip_method'] .= ' ' . sprintf(__('(current value: %s)', 'all-in-one-wp-security-and-firewall'), sanitize_text_field(wp_unslash($_SERVER[$ip_method])));
$ip_retrieve_methods[$id]['is_enabled'] = true;
} else {
$ip_retrieve_methods[$id]['ip_method'] .= ' (' . __('no value (i.e. empty) on your server', 'all-in-one-wp-security-and-firewall') . ')';
$ip_retrieve_methods[$id]['is_enabled'] = false;
}
if (!empty($ip_retrieve_methods_postfixes[$ip_method])) {
$ip_retrieve_methods[$id]['ip_method'] .= ' (' . $ip_retrieve_methods_postfixes[$ip_method] . ')';
}
}
return array(
'is_localhost' => AIOWPSecurity_Utility::is_localhost(),
'current_ip_retrieve_method' => $aio_wp_security->configs->get_value('aiowps_ip_retrieve_method'),
'ip_retrieve_methods' => $ip_retrieve_methods,
'server_suitable_ip_methods' => AIOWPSecurity_Utility_IP::get_server_suitable_ip_methods()
);
}
}
@@ -0,0 +1,216 @@
<?php
if (!defined('ABSPATH')) die('No direct access allowed');
if (trait_exists('AIOWPSecurity_Tfa_Commands_Trait')) return;
trait AIOWPSecurity_Tfa_Commands_Trait {
/**
* Init TFA for UDC.
*
* @return AIO_WP_Security_Simba_Two_Factor_Authentication_Plugin
*/
private function init_tfa() {
include_once AIO_WP_SECURITY_PATH . '/classes/wp-security-two-factor-login.php';
$tfa = new AIO_WP_Security_Simba_Two_Factor_Authentication_Plugin();
/* Needed to run hook-dependent code in TFA, or there are Divide by Zero errors. */
do_action('plugins_loaded');
return $tfa;
}
/**
* Saves the TFA algorithm setting.
*
* @param array $data Passed arguments.
*
* @return array|WP_Error
*/
public function save_algorithm_setting($data) {
if (!AIOWPSecurity_Utility_Permissions::has_manage_cap()) {
return new WP_Error(esc_html__('Sorry, you do not have enough privilege to execute the requested action.', 'all-in-one-wp-security-and-firewall'));
}
global $current_user;
$tfa = $this->init_tfa();
$controller = $tfa->get_controller();
$old_algorithm = $controller->get_user_otp_algorithm($current_user->ID);
if ($old_algorithm != $data['tfa_algorithm_type']) {
$controller->changeUserAlgorithmTo($current_user->ID, $data['tfa_algorithm_type']);
}
return array(
'status' => 'success',
);
}
/**
* Saves the TFA activation setting.
*
* @param array $data Passed arguments.
*
* @return array|WP_Error
*/
public function save_activation_setting($data) {
if (!AIOWPSecurity_Utility_Permissions::has_manage_cap()) {
return new WP_Error(esc_html__('Sorry, you do not have enough privilege to execute the requested action.', 'all-in-one-wp-security-and-firewall'));
}
global $current_user;
$tfa = $this->init_tfa();
$tfa->change_tfa_enabled_status($current_user->ID, $data['tfa_enable_tfa']);
return array(
'status' => 'success',
);
}
/**
* Updates the TFA private key.
*
* @return array|WP_Error
*/
public function update_private_key() {
if (!AIOWPSecurity_Utility_Permissions::has_manage_cap()) {
return new WP_Error(esc_html__('Sorry, you do not have enough privilege to execute the requested action.', 'all-in-one-wp-security-and-firewall'));
}
global $current_user;
$user_id = $current_user->ID;
delete_user_meta($user_id, 'tfa_priv_key_64');
delete_user_meta($user_id, 'simba_tfa_emergency_codes_64');
return array(
'status' => 'success',
);
}
/**
* Updates the TFA OTP Code.
*
* @param array $data Passed arguments.
*
* @return array|WP_Error
*/
public function update_otp_code($data) {
if (!AIOWPSecurity_Utility_Permissions::has_manage_cap()) {
return new WP_Error(esc_html__('Sorry, you do not have enough privilege to execute the requested action.', 'all-in-one-wp-security-and-firewall'));
}
global $current_user;
$tfa = $this->init_tfa();
if ('refreshotp' == $data['subaction']) {
$code = $tfa->get_controller()->get_current_code($current_user->ID);
if (false === $code) {
return array(
'status' => 'error',
'code' => '',
);
}
return array(
'status' => 'success',
'code' => $code,
);
} elseif ('untrust_device' == $data['subaction']) {
global $current_user;
$trusted_devices = $tfa->user_get_trusted_devices();
$trusted_device = $trusted_devices[wp_unslash($data['device_id'])];
if (isset($trusted_device)) {
unset($trusted_device);
}
$current_user_id = $current_user->ID;
$tfa->user_set_trusted_devices($current_user_id, $trusted_devices);
$trusted_list = $tfa->include_template('trusted-devices-inner-box.php', array('trusted_devices' => $tfa->user_get_trusted_devices()), true);
return array(
'status' => 'success',
'trusted_list' => $trusted_list,
);
}
exit;
}
/**
* Renders the TFA UI.
*
* @return array
*/
public function get_tfa_contents() {
if (!function_exists('submit_button')) {
require_once(ABSPATH . 'wp-admin/includes/template.php');
}
$tfa = $this->init_tfa();
$content = $tfa->include_template('user-settings.php', array('simba_tfa' => $tfa), true);
return array(
'status' => 'success',
'content' => $content,
);
}
/**
* Get the TFA settings data for the new UDC theme.
*
* @return array
*/
public function get_tfa_data() {
$tfa = $this->init_tfa();
return array(
'tfa_required_administrator' => $tfa->get_option('tfa_required_administrator'),
'tfa_administrator' => $tfa->get_option('tfa_administrator'),
);
}
/**
* Save the TFA settings data for the new UDC theme.
*
* @param array $data The data to save.
*
* @return array|WP_Error
*/
public function perform_save_tfa($data) {
if (!AIOWPSecurity_Utility_Permissions::has_manage_cap()) {
return new WP_Error(esc_html__('Sorry, you do not have enough privilege to execute the requested action.', 'all-in-one-wp-security-and-firewall'));
}
$success = false;
$message = '';
$tfa = $this->init_tfa();
$value = isset($data["tfa_required_administrator"]) ? '1' : '';
if ($tfa->update_option('tfa_required_administrator', $value)) {
$tfa->update_option('tfa_administrator', $value);
$success = true;
}
return $this->handle_response($success, $message);
}
}
@@ -0,0 +1,253 @@
<?php
if (!defined('ABSPATH')) die('No direct access allowed');
if (trait_exists('AIOWPSecurity_Tools_Commands_Trait')) return;
trait AIOWPSecurity_Tools_Commands_Trait {
/**
* Perform a WHOIS lookup for the provided IP address or domain.
*
* @param array $data The data containing the IP address or domain for the WHOIS lookup.
* The data should include the key 'aiowps_whois_ip_or_domain'.
* @return array An array containing the status of the operation and the WHOIS lookup result content.
* The 'status' key indicates whether the operation was successful.
* The 'content' key contains the result of the WHOIS lookup.
*/
public function perform_whois_lookup($data) {
global $aio_wp_security;
$ip_or_domain = trim(stripslashes($data['aiowps_whois_ip_or_domain']));
$invalid_domain = false;
if (empty($ip_or_domain)) {
$invalid_domain = true;
} elseif (version_compare(phpversion(), '5.6', '>')) {
if (!(filter_var($ip_or_domain, FILTER_VALIDATE_IP) || filter_var($ip_or_domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME))) $invalid_domain = true; // phpcs:ignore PHPCompatibility.Constants.NewConstants.filter_validate_domainFound -- This code only runs on php 7.0+ so ignore the warning
}
if ($invalid_domain) {
$result = __('Please enter a valid IP address or domain name to look up.', 'all-in-one-wp-security-and-firewall');
$result .= __('Nothing to show.', 'all-in-one-wp-security-and-firewall');
} else {
$result = $this->whois_lookup($ip_or_domain);
if (is_wp_error($result)) {
$result = htmlspecialchars($result->get_error_message());
$result .= __('Nothing to show.', 'all-in-one-wp-security-and-firewall');
} else {
$result = htmlspecialchars($result);
}
}
$args = array(
'content' => array('aios-who-is-lookup-result-container' => $aio_wp_security->include_template('wp-admin/tools/partials/who-is-lookup-result.php', true, array('result' => $result, 'ip_or_domain' => $ip_or_domain)))
);
return $this->handle_response(true, false, $args);
}
/**
* Store custom .htaccess settings provided by the user.
*
* @param array $data The data containing the custom .htaccess settings.
* It should include keys 'aiowps_enable_custom_rules', 'aiowps_custom_rules',
* and 'aiowps_place_custom_rules_at_top' if applicable.
* @return array An array containing the status of the operation and any relevant messages.
* The 'status' key indicates whether the operation was successful.
* The 'message' key contains any informational or error messages.
*/
public function perform_store_custom_htaccess_settings($data) {
global $aio_wp_security;
$success = true;
$message = '';
$options = array();
// Save settings
if (isset($data["aiowps_enable_custom_rules"]) && empty($data['aiowps_custom_rules'])) {
$message = __('You must enter some .htaccess directives in the text box below', 'all-in-one-wp-security-and-firewall');
return $this->handle_response(false, $message);
} else {
if (!empty($data['aiowps_custom_rules'])) {
// Sanitize textarea shoud not be used as <filesMatch "\.(js|css|html)$"> etc rules gets removed.
// Escape textarea should not be used the & becomes &amp;.
// Here stripslashes as old version 5.3.0 not required, AIOWPSecurity_Ajax::set_data applies wp_unslash for ajax data.
// So the .htacces rule having index\.php backslashes removed if used stripslashes below.
$options['aiowps_custom_rules'] = $data['aiowps_custom_rules'];
} else {
$options['aiowps_custom_rules'] = ''; //Clear the custom rules config value
}
$aiowps_custom_rules = $aio_wp_security->configs->get_value('aiowps_custom_rules');
$aiowps_place_custom_rules_at_top = $aio_wp_security->configs->get_value('aiowps_place_custom_rules_at_top');
$options['aiowps_enable_custom_rules'] = isset($data["aiowps_enable_custom_rules"]) ? '1' : '';
$options['aiowps_place_custom_rules_at_top'] = isset($data["aiowps_place_custom_rules_at_top"]) ? '1' : '';
$this->save_settings($options); // Save the configuration
$write_result = AIOWPSecurity_Utility_Htaccess::write_to_htaccess(); //now let's write to the .htaccess file
if (!$write_result) {
$options['aiowps_enable_custom_rules'] = $aiowps_custom_rules;
$options['aiowps_place_custom_rules_at_top'] = $aiowps_place_custom_rules_at_top;
$this->save_settings($options);
$success = false;
$message = __('The plugin was unable to write to the .htaccess file, please edit file manually.', 'all-in-one-wp-security-and-firewall');
$aio_wp_security->debug_logger->log_debug("Custom Rules feature - The plugin was unable to write to the .htaccess file.");
}
}
return $this->handle_response($success, $message);
}
/**
* Perform the general visitor lockout settings operation.
*
* @param array $data The data containing the general visitor lockout settings.
* It should include keys 'aiowps_site_lockout' and 'aiowps_site_lockout_msg'.
* @return array An array containing the status of the operation and any relevant messages.
* The 'status' key indicates whether the operation was successful.
* The 'message' key contains an informational message about the outcome of the operation.
*/
public function perform_general_visitor_lockout($data) {
$options = array();
// Save settings
$options['aiowps_site_lockout'] = isset($data["aiowps_site_lockout"]) ? '1' : '';
$maint_msg = wp_kses_post(wp_unslash($data['aiowps_site_lockout_msg']));
$options['aiowps_site_lockout_msg'] = $maint_msg; // Text area/msg box
$this->save_settings($options);
do_action('aiowps_site_lockout_settings_saved');
return array(
'status' => 'success',
'message' => __('The settings have been successfully updated.', 'all-in-one-wp-security-and-firewall')
);
}
/**
* Perform the general visitor lockout setting operation for the dashboard widget.
*
* @param array $data The data containing the general visitor lockout setting.
* It should include the 'aiowps_site_lockout' key.
* @return array An array containing the status of the operation and any relevant messages.
* The 'status' key indicates whether the operation was successful.
* The 'message' key contains an informational message about the outcome of the operation.
*/
public function perform_general_visitor_lockout_dashboard_widget($data) {
$options = array();
// Save settings
$options['aiowps_site_lockout'] = isset($data["aiowps_site_lockout"]) ? '1' : '';
$this->save_settings($options);
do_action('aiowps_site_lockout_settings_saved');
return $this->handle_response(true);
}
/**
* Checks a password against the HIBP database.
*
* @param array $data Contains the password to be checked.
*
* @return array
*/
public function hibp_check_password($data) {
return array(
'status' => 'success',
'pwned' => AIOS_HIBP::password_is_pwned($data['password']),
);
}
/**
* Does a WHOIS lookup on an IP address or domain name and then returns the result.
*
* @param String $search - IP address or domain name to do a WHOIS lookup on
* @param Integer $timeout - connection timeout for fsockopen
*
* @return String|WP_Error - returns preformatted WHOIS lookup result or WP_Error
*/
private function whois_lookup($search, $timeout = 10) {
$fp = @fsockopen('whois.iana.org', 43, $errno, $errstr, $timeout);
if (!$fp) {
return new WP_Error('whois_lookup_failed', 'whois.iana.org: Socket Error '.$errno.' - '.$errstr);
}
$queries = sprintf(__('Querying %s: %s', 'all-in-one-wp-security-and-firewall'), 'whois.iana.org', $search)."\n";
fputs($fp, $search."\r\n");
$out = '';
while (!feof($fp)) {
$line = fgets($fp);
if (preg_match('/refer: +(\S+)/', $line, $matches)) {
$referral_server = $matches[1];
$queries .= sprintf(__('Redirected to %s', 'all-in-one-wp-security-and-firewall'), $referral_server)."\n";
break;
}
$out .= $line;
}
fclose($fp);
if (!isset($referral_server) && filter_var($search, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && preg_match('/whois: +(\S+)/', $out, $matches)) {
$referral_server = $matches[1];
$queries .= sprintf(__('Redirected to %s', 'all-in-one-wp-security-and-firewall'), $referral_server)."\n";
}
$referrals = array();
while (isset($referral_server)) {
$referrals[] = $referral_server;
$fp = @fsockopen($referral_server, 43, $errno, $errstr, $timeout);
if (!$fp) {
return new WP_Error('whois_lookup_failed', $referral_server.': Socket Error '.$errno.' - '.$errstr);
}
if ('whois.arin.net' == $referral_server) {
$formatted_search = 'n + '.$search;
} elseif ('whois.denic.de' == $referral_server) {
$formatted_search = '-T dn,ace '.$search;
} elseif ('whois.dk-hostmaster.dk' == $referral_server) {
$formatted_search = '--charset=utf-8 --show-handles '.$search;
} elseif ('whois.nic.ad.jp' == $referral_server || 'whois.jprs.jp' == $referral_server) {
$formatted_search = $search.'/e';
} else {
$formatted_search = $search;
}
$queries .= sprintf(__('Querying %s: %s', 'all-in-one-wp-security-and-firewall'), $referral_server, $formatted_search)."\n";
$referral_server = null;
fputs($fp, $formatted_search."\r\n");
$out = '';
while (!feof($fp)) {
$line = fgets($fp);
if (preg_match('/Registrar WHOIS Server: +(\S+)/', $line, $matches)
|| preg_match('/% referto: +whois -h (\S+)/', $line, $matches)
|| preg_match('/% referto: +(\S+)/', $line, $matches)
|| preg_match('/ReferralServer: +rwhois:\/\/(\S+)/', $line, $matches)
|| preg_match('/ReferralServer: +whois:\/\/(\S+)/', $line, $matches)
) {
if (!in_array($matches[1], $referrals)) {
$referral_server = $matches[1];
$queries .= sprintf(__('Redirected to %s', 'all-in-one-wp-security-and-firewall'), $referral_server)."\n";
break;
}
}
$out .= $line;
}
fclose($fp);
}
return $queries."\n".$out;
}
}
@@ -0,0 +1,716 @@
<?php
if (!defined('ABSPATH')) die('No direct access allowed');
if (trait_exists('AIOWPSecurity_User_Security_Commands_Trait')) return;
trait AIOWPSecurity_User_Security_Commands_Trait {
/**
* Saves user account security settings.
*
* This function updates security settings related to user enumeration prevention
* and strong password enforcement in the AIO WP Security plugin.
*
* @param array $data An associative array containing the security settings:
* - 'aiowps_prevent_users_enumeration' (optional): Set to '1' to prevent user enumeration.
* - 'aiowps_enforce_strong_password' (optional): Set to '1' to enforce strong passwords.
*
* @return array The response array containing:
* - 'badges' (array): A list of applied security badges.
*/
public function perform_save_user_account_settings($data) {
global $aio_wp_security;
// Save settings
$aio_wp_security->configs->set_value('aiowps_prevent_users_enumeration', isset($data["aiowps_prevent_users_enumeration"]) ? '1' : '', true);
$aio_wp_security->configs->set_value('aiowps_enforce_strong_password', isset($data['aiowps_enforce_strong_password']) ? '1' : '', true);
$badges = array('enforce-strong-password', 'disable-users-enumeration');
return $this->handle_response(true, '', array('badges' => $badges));
}
/**
* Performs the action to change the admin username.
*
* @param array $data An array containing the data for changing the admin username.
* The array may contain the following keys:
* - 'aiowps_new_user_name': The new username to be set for the admin.
* @return array Returns an array containing the status of the operation ('success' or 'error'),
* and a message indicating the result of the operation.
* If the operation is successful, it also includes a badge representing the updated feature details.
*/
public function perform_change_admin_username($data) {
global $wpdb, $aio_wp_security;
$response = array(
'status' => 'success',
'content' => array()
);
$error = '';
if (!empty($data['aiowps_new_user_name'])) {
$new_username = sanitize_text_field($data['aiowps_new_user_name']);
if (validate_username($new_username)) {
if (AIOWPSecurity_Utility::check_user_exists($new_username)) {
$response['status'] = 'error';
$error = sprintf(__('Username: %s already exists, please enter another value.', 'all-in-one-wp-security-and-firewall'), $new_username);
} else {
// let's check if currently logged in username is 'admin'
$user = wp_get_current_user();
$user_login = $user->user_login;
if ('admin' == strtolower($user_login)) {
$username_is_admin = true;
} else {
$username_is_admin = false;
}
// Now let's change the username
$sql = $wpdb->prepare("UPDATE `" . $wpdb->users . "` SET user_login = '" . esc_sql($new_username) . "' WHERE user_login=%s", "admin");
$result = $wpdb->query($sql);
if (false === $result) {
// There was an error updating the users table
$user_update_error = __('The database update operation of the user account failed.', 'all-in-one-wp-security-and-firewall');
$response['status'] = 'error';
$response['message'] = $user_update_error;
$aio_wp_security->debug_logger->log_debug($user_update_error . ' ' . $wpdb->last_error, 4);
return $response;
}
// multisite considerations
if (is_multisite()) { // process sitemeta if we're in a multi-site situation
$oldAdmins = $wpdb->get_var("SELECT meta_value FROM `" . $wpdb->sitemeta . "` WHERE meta_key = 'site_admins'");
$newAdmins = str_replace('5:"admin"', strlen($new_username) . ':"' . esc_sql($new_username) . '"', $oldAdmins);
$wpdb->query("UPDATE `" . $wpdb->sitemeta . "` SET meta_value = '" . esc_sql($newAdmins) . "' WHERE meta_key = 'site_admins'");
}
// If user is logged in with username "admin" then log user out and send to login page so they can login again
if ($username_is_admin) {
// Lets logout the user
$aio_wp_security->debug_logger->log_debug("Logging user out with login ".$user_login. " because they changed their username.");
$after_logout_url = AIOWPSecurity_Utility::get_current_page_url();
$after_logout_payload = array('redirect_to' => $after_logout_url, 'msg' => $aio_wp_security->user_login_obj->key_login_msg.'=admin_user_changed');
//Save some of the logout redirect data to a transient
is_multisite() ? set_site_transient('aiowps_logout_payload', $after_logout_payload, 30 * 60) : set_transient('aiowps_logout_payload', $after_logout_payload, 30 * 60);
$logout_url = AIOWPSEC_WP_URL.'?aiowpsec_do_log_out=1';
$logout_url = AIOWPSecurity_Utility::add_query_data_to_url($logout_url, 'al_additional_data', '1');
$response['logout_user'] = true;
$response['logout_url'] = $logout_url;
}
}
} else { // An invalid username was entered
$error = __('You entered an invalid username, please enter another value.', 'all-in-one-wp-security-and-firewall');
}
} else { // No username value was entered
$response['status'] = 'error';
$error = __('Please enter a value for your username.', 'all-in-one-wp-security-and-firewall');
}
if (!empty($error)) { // We have some validation or other error
$response['message'] = $error;
} else {
$response['message'] = __('The username has been successfully changed.', 'all-in-one-wp-security-and-firewall');
$response['badges'] = $this->get_features_id_and_html(array('user-accounts-change-admin-user'));
$response['content']['change-admin-username-content'] = $aio_wp_security->include_template('wp-admin/user-security/partials/wp-username-content.php', true);
}
return $response;
}
/**
* Performs the action to save the login lockout settings.
*
* @param array $data An array containing the data to be saved.
*
* @return array Returns an array containing the status of the operation ('success' or 'error'),
* a message indicating the result of the operation,
* and a badge representing the updated feature details.
*/
public function perform_save_login_lockout_settings($data) {
$response = array(
'status' => 'success',
'values' => array(),
'info' => array()
);
$invalid_fields = array();
$max_login_attempt_val = sanitize_text_field($data['aiowps_max_login_attempts']);
if (!is_numeric($max_login_attempt_val) || 1 > $max_login_attempt_val) {
$invalid_fields[] = 'max login attempts';
$max_login_attempt_val = '3'; // Set it to the default value for this field
}
$login_retry_time_period = sanitize_text_field($data['aiowps_retry_time_period']);
if (!is_numeric($login_retry_time_period) || 1 > $login_retry_time_period) {
$invalid_fields[] = 'login retry time period';
$login_retry_time_period = '5'; // Set it to the default value for this field
}
$lockout_time_length = sanitize_text_field($data['aiowps_lockout_time_length']);
if (!is_numeric($lockout_time_length) || 1 > $lockout_time_length) {
$invalid_fields[] = 'minimum lockout time length';
$lockout_time_length = '5'; // Set it to the default value for this field
}
$max_lockout_time_length = sanitize_text_field($data['aiowps_max_lockout_time_length']);
if (!is_numeric($max_lockout_time_length) || 1 > $max_lockout_time_length) {
$invalid_fields[] = 'maximum lockout time length';
$max_lockout_time_length = '60'; // Set it to the default value for this field
}
if ($lockout_time_length >= $max_lockout_time_length) {
$invalid_fields[] = 'minimum lockout time length';
$lockout_time_length = '5'; // Set it to the default value for this field
$max_lockout_time_length = '60'; // Set it to the default value for this field
}
$email_addresses = isset($data['aiowps_email_address']) ? stripslashes($data['aiowps_email_address']) : get_bloginfo('admin_email');
$email_addresses_trimmed = AIOWPSecurity_Utility::explode_trim_filter_empty($email_addresses, "\n");
// Read into array, sanitize, filter empty and keep only unique usernames.
$email_address_list = array_unique(
array_filter(
array_map(
'sanitize_email',
$email_addresses_trimmed
),
'is_email'
)
);
if (isset($data['aiowps_enable_email_notify']) && 1 == $data['aiowps_enable_email_notify'] && 0 == count($email_addresses_trimmed)) {
$invalid_fields[] = 'email addresses';
} elseif (isset($data['aiowps_enable_email_notify']) && 1 == $data['aiowps_enable_email_notify'] && (0 == count($email_address_list) || count($email_address_list) != count($email_addresses_trimmed))) {
$invalid_fields[] = 'email addresses';
}
if (isset($data['aiowps_enable_email_notify']) && 0 == count($email_address_list)) {
$email_address_list[] = get_bloginfo('admin_email');
}
// Instantly lockout specific usernames
$instantly_lockout_specific_usernames = isset($data['aiowps_instantly_lockout_specific_usernames']) ? $data['aiowps_instantly_lockout_specific_usernames'] : '';
// Read into array, sanitize, filter empty and keep only unique usernames.
$instantly_lockout_specific_usernames = array_unique(
array_filter(
array_map(
'sanitize_user',
AIOWPSecurity_Utility::explode_trim_filter_empty($instantly_lockout_specific_usernames)
),
'strlen'
)
);
$response['message'] = __('The settings have been successfully updated.', 'all-in-one-wp-security-and-firewall');
if (!empty($invalid_fields)) {
$invalid_fields = array_unique($invalid_fields);
$invalid_fields = implode(", ", $invalid_fields);
$response['info'][] = sprintf(__('The following options had invalid values and have been set to the defaults: %s', 'all-in-one-wp-security-and-firewall'), $invalid_fields);
}
$options = array();
// Save all the form values to the options
$random_20_digit_string = AIOWPSecurity_Utility::generate_alpha_numeric_random_string(20); // Generate random 20 char string for use during CAPTCHA encode/decode
$options['aiowps_unlock_request_secret_key'] = $random_20_digit_string;
$options['aiowps_enable_login_lockdown'] = isset($data["aiowps_enable_login_lockdown"]) ? '1' : '';
$options['aiowps_allow_unlock_requests'] = isset($data["aiowps_allow_unlock_requests"]) ? '1' : '';
$options['aiowps_max_login_attempts'] = absint($max_login_attempt_val);
$options['aiowps_retry_time_period'] = absint($login_retry_time_period);
$options['aiowps_lockout_time_length'] = absint($lockout_time_length);
$options['aiowps_max_lockout_time_length'] = absint($max_lockout_time_length);
$options['aiowps_set_generic_login_msg'] = isset($data["aiowps_set_generic_login_msg"]) ? '1' : '';
$options['aiowps_enable_invalid_username_lockdown']= isset($data["aiowps_enable_invalid_username_lockdown"]) ? '1' : '';
$options['aiowps_instantly_lockout_specific_usernames'] = $instantly_lockout_specific_usernames;
$options['aiowps_enable_email_notify'] = isset($data["aiowps_enable_email_notify"]) ? '1' : '';
$options['aiowps_enable_php_backtrace_in_email'] = isset($data['aiowps_enable_php_backtrace_in_email']) ? '1' : '';
$options['aiowps_email_address'] = $email_address_list;
$this->save_settings($options);
$response['values']['aiowps_max_login_attempts'] = absint($max_login_attempt_val);
$response['values']['aiowps_retry_time_period'] = absint($login_retry_time_period);
$response['values']['aiowps_lockout_time_length'] = absint($lockout_time_length);
$response['values']['aiowps_max_lockout_time_length'] = absint($max_lockout_time_length);
$response['values']['aiowps_email_address'] = implode("\n", $email_address_list);
$response['badges'] = $this->get_features_id_and_html(array('user-login-login-lockdown'));
return $response;
}
/**
* Performs the action to save the login lockout whitelist settings.
*
* @param array $data An array containing the data to be saved.
* The array may contain the following keys:
* - 'aiowps_lockdown_enable_whitelisting': A boolean indicating whether whitelisting is enabled.
* - 'aiowps_lockdown_allowed_ip_addresses': The allowed IP addresses for whitelisting.
* @return array Returns an array containing the status of the operation ('success' or 'error'),
* a message indicating the result of the operation,
* and a badge representing the updated feature details.
*/
public function perform_save_login_lockout_whitelist_settings($data) {
global $aio_wp_security;
$response = array(
'status' => 'success'
);
$options = array();
$result = 1;
if (!empty($data['aiowps_lockdown_allowed_ip_addresses'])) {
$ip_addresses = sanitize_textarea_field(wp_unslash($data['aiowps_lockdown_allowed_ip_addresses']));
$ip_list_array = AIOWPSecurity_Utility_IP::create_ip_list_array_from_string_with_newline($ip_addresses);
$validated_ip_list_array = AIOWPSecurity_Utility_IP::validate_ip_list($ip_list_array, 'whitelist');
if (is_wp_error($validated_ip_list_array)) {
$result = -1;
$response['status'] = 'error';
$response['message'] = AIOWPSecurity_Admin_Menu::show_msg_error_st(nl2br($validated_ip_list_array->get_error_message()), true);
} else {
$allowed_ip_data = implode("\n", $validated_ip_list_array);
$options['aiowps_lockdown_allowed_ip_addresses'] = $allowed_ip_data;
}
} else {
$options['aiowps_lockdown_allowed_ip_addresses'] = ''; //Clear the IP address config value
}
if (1 == $result) {
$aio_wp_security->configs->set_value('aiowps_lockdown_enable_whitelisting', isset($data["aiowps_lockdown_enable_whitelisting"]) ? '1' : '', true);
$response['message'] = __('The settings have been successfully updated.', 'all-in-one-wp-security-and-firewall');
$response['badges'] = $this->get_features_id_and_html(array('user-login-lockout-ip-whitelisting'));
}
$this->save_settings($options);
return $response;
}
/**
* Performs the action to force logout users.
*
* @param array $data An array containing the data to be saved.
* The array may contain the following keys:
* - 'aiowps_logout_time_period': The time period (in minutes) for logout.
* - 'aiowps_enable_forced_logout': A boolean indicating whether forced logout is enabled.
* @return array Returns an array containing the status of the operation ('success' or 'error'),
* an array of messages indicating the result of the operation,
* the content representing the logout time period,
* and a badge representing the updated feature details.
*/
public function perform_force_logout($data) {
global $aio_wp_security;
$response = array(
'status' => 'success',
'info' => array(),
'values' => array()
);
$options = array();
$logout_time_period = sanitize_text_field($data['aiowps_logout_time_period']);
if (isset($data["aiowps_enable_forced_logout"]) && (!is_numeric($logout_time_period) || $logout_time_period < 1)) {
$response['info'][] = __('You entered a non numeric or negative value for the logout time period field, it has been set to the default value.', 'all-in-one-wp-security-and-firewall');
$logout_time_period = '60'; // Set it to the default value for this field
}
// Save all the form values to the options
$options['aiowps_logout_time_period'] = absint($logout_time_period);
$options['aiowps_enable_forced_logout'] = isset($data["aiowps_enable_forced_logout"]) ? '1' : '';
$this->save_settings($options);
$response['values']['aiowps_logout_time_period'] = absint($logout_time_period);
$response['badges'] = $this->get_features_id_and_html(array('user-login-force-logout'));
$response['message'] = __('The settings have been successfully updated.', 'all-in-one-wp-security-and-firewall');
if ('1' === $options['aiowps_enable_forced_logout']) {
$response['logout_user'] = $this->check_logout_user();
$response['logout_url'] = $aio_wp_security->user_login_obj->aiowps_force_logout_action_handler(true);
}
return $response;
}
/**
* Performs the action to save the HIBP settings.
*
* @param array $data An array containing the data to be saved.
*
* @return array Returns an array containing the status of the operation ('success' or 'error'),
* a message indicating the result of the operation,
* and a badge representing the updated feature details.
*/
public function perform_save_hibp_settings($data) {
global $aio_wp_security;
$aio_wp_security->configs->set_value('aiowps_hibp_user_profile_update', isset($data['aiowps_hibp_user_profile_update']) ? '1' : '', true);
$aio_wp_security->configs->set_value('aiowps_http_password_reset', isset($data['aiowps_http_password_reset']) ? '1' : '', true);
$aio_wp_security->configs->save_config();
return $this->handle_response(true, false, array('badges' => array('hibp')));
}
/**
* Performs the action to disable application password.
*
* @param array $data An array containing the data to be saved.
* The array may contain the following key:
* - 'aiowps_disable_application_password': A boolean indicating whether application password is disabled.
* @return array Returns an array containing the status of the operation ('success' or 'error'),
* a message indicating the result of the operation,
* and a badge representing the updated feature details.
*/
public function perform_disable_application_password($data) {
global $aio_wp_security;
// Save all the form values to the options
$aio_wp_security->configs->set_value('aiowps_disable_application_password', isset($data['aiowps_disable_application_password']) ? '1' : '', true);
return array(
'status' => 'success',
'message' => __('The settings have been successfully updated.', 'all-in-one-wp-security-and-firewall'),
'badges' => $this->get_features_id_and_html(array('disable-application-password'))
);
}
/**
* Performs the action to add salt postfix.
*
* @param array $data An array containing the data to be saved.
* The array may contain the following key:
* - 'aiowps_enable_salt_postfix': A boolean indicating whether salt postfix is enabled.
* @return array Returns an array containing the status of the operation ('success' or 'error'),
* a message indicating the result of the operation,
* and a badge representing the updated feature details.
*/
public function perform_add_salt_postfix($data) {
global $aio_wp_security;
$response = array(
'status' => 'success'
);
// Save settings
$aiowps_enable_salt_postfix = isset($data['aiowps_enable_salt_postfix']) ? '1' : '';
if ($aiowps_enable_salt_postfix == $aio_wp_security->configs->get_value('aiowps_enable_salt_postfix')) {
$is_setting_changed = true;
} else {
$is_setting_changed = false;
}
$aio_wp_security->configs->set_value('aiowps_enable_salt_postfix', $aiowps_enable_salt_postfix, true);
$ret_schedule = $this->schedule_change_auth_keys_and_salt();
if (is_wp_error($ret_schedule)) {
$aio_wp_security->debug_logger->log_debug($ret_schedule->get_error_message(), 4);
}
if ('1' == $aiowps_enable_salt_postfix && $is_setting_changed) {
AIOWPSecurity_Utility::change_salt_postfixes();
}
$response['message'] = __('The settings have been successfully updated.', 'all-in-one-wp-security-and-firewall');
$response['badges'] = $this->get_features_id_and_html(array('enable-salt-postfix'));
return $response;
}
/**
* Performs actions on logged-in users.
*
* @param array $data An array containing the data for the action to be performed.
* The array may contain the following keys:
* - 'action': The action to be performed on logged-in users (e.g., 'force_user_logout').
* - 'logged_in_id': The ID of the logged-in user on which the action will be performed.
* @return array Returns an array containing the status of the operation ('success' or 'error'),
* and a message indicating the result of the operation.
*/
public function perform_logged_in_user_action($data) {
global $aio_wp_security;
include_once AIO_WP_SECURITY_PATH.'/admin/wp-security-list-logged-in-users.php'; // For rendering the AIOWPSecurity_List_Table
$user_list = new AIOWPSecurity_List_Logged_In_Users();
$response = array(
'status' => 'success'
);
if (empty($data['action']) || !in_array($data['action'], array('force_user_logout'))) { // more actions can be added
return array(
'status' => 'error',
'message' => __('Invalid action provided for logged in user.', 'all-in-one-wp-security-and-firewall')
);
}
if ('force_user_logout' == $data['action']) {
if (empty($data['logged_in_id'])) {
return array(
'status' => 'error',
'message' => __('No user ID was provided', 'all-in-one-wp-security-and-firewall')
);
}
$user_id = strip_tags($data['logged_in_id']);
$error = '';
if (!is_numeric($user_id)) {
$error = __("Invalid user ID provided.", 'all-in-one-wp-security-and-firewall');
} elseif (get_current_user_id() == $user_id) {
$error = __("You cannot log yourself out", 'all-in-one-wp-security-and-firewall');
} elseif (is_super_admin($user_id)) {
$error = __("Super admins cannot be logged out.", 'all-in-one-wp-security-and-firewall');
} elseif (!AIOWPSecurity_Utility::is_user_member_of_blog($user_id)) {
$error = __("You cannot log out a user from a different subsite.", 'all-in-one-wp-security-and-firewall');
}
if ($error) {
return array(
'message' => $error,
'status' => 'error'
);
}
$users = esc_sql($user_id);
$result = $aio_wp_security->user_login_obj->delete_logged_in_user($users);
if ($result) {
$user_list->logout_user($users);
$response['message'] = __('The selected user has been logged out successfully.', 'all-in-one-wp-security-and-firewall');
} else {
$response['message'] = __('Failed to log out the selected user.', 'all-in-one-wp-security-and-firewall');
$response['status'] = 'error';
}
}
return $response;
}
/**
* Performs the action to configure manual registration approval settings.
*
* @param array $data An array containing the data to be saved.
* The array may contain the following key:
* - 'aiowps_enable_manual_registration_approval': A boolean indicating whether manual registration approval is enabled.
* @return array Returns an array containing the status of the operation ('success' or 'error'),
* a message indicating the result of the operation,
* and a badge representing the updated feature details.
*/
public function perform_manual_approval_settings($data) {
global $aio_wp_security;
// Save settings
$aio_wp_security->configs->set_value('aiowps_enable_manual_registration_approval', isset($data["aiowps_enable_manual_registration_approval"]) ? '1' : '', true);
return array(
'status' => 'success',
'message' => __('The settings have been successfully updated.', 'all-in-one-wp-security-and-firewall'),
'badges' => $this->get_features_id_and_html(array('manually-approve-registrations'))
);
}
/**
* Performs actions on manual approval items (e.g., approve account, delete account, block IP).
*
* @param array $data An array containing the data for the action to be performed.
* The array may contain the following keys:
* - 'action': The action to be performed on the manual approval item (e.g., 'approve_acct', 'delete_acct', 'block_ip').
* - 'user_id': The ID of the user for whom the action will be performed (applicable for 'approve_acct' and 'delete_acct' actions).
* - 'ip_address': The IP address to be blocked (applicable for 'block_ip' action).
* @return array Returns an array containing the status of the operation ('success' or 'error'),
* and a message indicating the result of the operation.
*/
public function perform_manual_approval_item_action($data) {
global $aio_wp_security;
include_once AIO_WP_SECURITY_PATH.'/admin/wp-security-list-registered-users.php'; // For rendering the AIOWPSecurity_List_Table
$user_list = new AIOWPSecurity_List_Registered_Users();
$status = 'error';
$valid_actions = array('approve_acct', 'delete_acct', 'block_ip');
if (empty($data['action']) || !in_array($data['action'], $valid_actions)) { // more actions can be added
return array(
'status' => 'error',
'message' => __('Invalid action provided for registered user.', 'all-in-one-wp-security-and-firewall')
);
}
switch ($data['action']) {
case 'approve_acct':
if (empty($data['user_id'])) {
return array(
'status' => 'error',
'message' => __('No valid user ID was provided', 'all-in-one-wp-security-and-firewall')
);
}
$user_id = esc_sql(strip_tags($data['user_id']));
$meta_key = 'aiowps_account_status';
$meta_value = 'approved'; // set account status
// Approve single account
$result = update_user_meta($user_id, $meta_key, $meta_value);
if ($result) {
$user = get_user_by('id', $user_id);
$user_list->send_email_upon_account_activation($user);
$message = __('The selected account was approved successfully.', 'all-in-one-wp-security-and-firewall');
$status = 'success';
} elseif (false === $result) {
$aio_wp_security->debug_logger->log_debug("could not approve account ID: $user_id", 4);
$message = __('The selected account could not be approved.', 'all-in-one-wp-security-and-firewall');
}
break;
case 'delete_acct':
if (empty($data['user_id'])) {
return array(
'status' => 'error',
'message' => __('No valid user ID was provided', 'all-in-one-wp-security-and-firewall')
);
}
$user_id = esc_sql(strip_tags($data['user_id']));
// Delete single account
$result = wp_delete_user($user_id);
if (true === $result) {
$message = __('The selected account was deleted successfully.', 'all-in-one-wp-security-and-firewall');
$status = 'success';
} else {
$aio_wp_security->debug_logger->log_debug("could not delete account ID: $user_id", 4);
$message = __('The selected account could not be deleted.', 'all-in-one-wp-security-and-firewall');
}
break;
case 'block_ip':
if (empty($data['ip_address'])) {
return array(
'status' => 'error',
'message' => __('No valid IP address was provided', 'all-in-one-wp-security-and-firewall')
);
}
$ip = esc_sql(strip_tags($data['ip_address']));
if (AIOWPSecurity_Utility_IP::get_user_ip_address() == $ip) {
$message = __('You cannot block your own IP address:', 'all-in-one-wp-security-and-firewall') . ' ' . $ip;
break;
}
// Block single IP
$result = AIOWPSecurity_Blocking::add_ip_to_block_list($ip, 'registration_spam');
if (true === $result) {
$message = __('The selected IP was successfully added to the permanent block list.', 'all-in-one-wp-security-and-firewall');
$message .= ' <a href="admin.php?page='.AIOWPSEC_MAIN_MENU_SLUG.'&tab=permanent-block" target="_blank">'.__('View Blocked IPs', 'all-in-one-wp-security-and-firewall').'</a>';
$status = 'success';
} else {
$aio_wp_security->debug_logger->log_debug("AIOWPSecurity_List_Registered_Users::block_selected_ips() - could not block IP: $ip", 4);
$message = __('The selected IP could not be added to the permanent block list.', 'all-in-one-wp-security-and-firewall');
}
break;
}
return array(
'status' => $status,
'message' => $message
);
}
/**
* Schedule weekly aios_change_auth_keys_and_salt cron event.
*
* @return Boolean|WP_Error True if event successfully scheduled. False or WP_Error on failure.
*/
private function schedule_change_auth_keys_and_salt() {
$previous_time = wp_next_scheduled('aios_change_auth_keys_and_salt');
if (false !== $previous_time) {
// Clear schedule so that we don't stack up scheduled backups
wp_clear_scheduled_hook('aios_change_auth_keys_and_salt');
}
$gmt_offset_in_seconds = floatval(get_option('gmt_offset')) * 3600;
$first_time = strtotime('next Sunday '.apply_filters('aios_salt_change_schedule_time', '3:00 am')) + $gmt_offset_in_seconds;
return wp_schedule_event($first_time, 'weekly', 'aios_change_auth_keys_and_salt');
}
/**
* Checks if the current user should be automatically logged out based on last login time.
*
* This method compares the current time with the last login time of the user and determines
* if the user should be logged out based on a configured logout time period.
*
* @return bool Returns true if the user should be logged out, false otherwise.
*/
private function check_logout_user() {
global $aio_wp_security;
// Get the current user
$current_user = wp_get_current_user();
$user_id = $current_user->ID;
// Get the current and last login times
$current_time = current_time('mysql', true);
$login_time = $aio_wp_security->user_login_obj->get_wp_user_aiowps_last_login_time($user_id);
// Return false if login time is empty (no last login recorded)
if (empty($login_time)) {
return false;
}
// Calculate the time difference between current time and last login time
$diff = strtotime($current_time) - strtotime($login_time);
// Get the configured logout time period in seconds
$logout_time_interval_value = $aio_wp_security->configs->get_value('aiowps_logout_time_period');
$logout_time_interval_val_seconds = $logout_time_interval_value * 60;
// Return true if the time difference exceeds the logout time interval, indicating the user should be logged out
return $diff > $logout_time_interval_val_seconds;
}
/**
* Whitelists user's IP address
*
* @return array Returns an array containing the status of the operation ('success' or 'error'),
* a message indicating the result of the operation,
* and a badge representing the updated feature details.
*/
public function perform_whitelist_user_ip() {
$response = array(
'status' => 'success'
);
if (!AIOWPSecurity_Utility_Permissions::has_manage_cap()) {
$response['status'] = 'error';
$response['message'] = __('You don\'t have enough permissions to whitelist your IP address.', 'all-in-one-wp-security-and-firewall');
return $response;
}
$aiowps_firewall_allow_list = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::ALLOW_LIST);
$whitelisted_ips = $aiowps_firewall_allow_list::get_ips();
$is_whitelisted = $aiowps_firewall_allow_list::is_ip_allowed();
if ($is_whitelisted) {
$response['status'] = 'error';
$response['message'] = __('Your IP address is already whitelisted.', 'all-in-one-wp-security-and-firewall');
return $response;
} else {
$user_ip = AIOWPSecurity_Utility_IP::get_user_ip_address();
if (empty($user_ip)) {
$response['status'] = 'error';
$response['message'] = __('Your IP address could not be detected.', 'all-in-one-wp-security-and-firewall');
return $response;
}
$whitelisted_ips .= (empty($whitelisted_ips) ? '' : "\n") . $user_ip;
if (!$aiowps_firewall_allow_list::add_ips($whitelisted_ips)) {
$response['status'] = 'error';
$response['message'] = __('There was an error whitelisting your IP address, please try again.', 'all-in-one-wp-security-and-firewall');
return $response;
}
$response['message'] = __('Your IP address has been whitelisted successfully.', 'all-in-one-wp-security-and-firewall');
return $response;
}
}
}