Phase 6: AIOS security plugin with conservative login lockdown config (10 attempts)
This commit is contained in:
Executable
+561
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+221
@@ -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;
|
||||
}
|
||||
}
|
||||
+280
@@ -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'),
|
||||
);
|
||||
}
|
||||
}
|
||||
+224
@@ -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));
|
||||
}
|
||||
}
|
||||
+787
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
Executable
+133
@@ -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'));
|
||||
}
|
||||
}
|
||||
}
|
||||
wp-content/plugins/all-in-one-wp-security-and-firewall/classes/commands/wp-security-log-commands.php
Executable
+345
@@ -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')),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
+562
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
wp-content/plugins/all-in-one-wp-security-and-firewall/classes/commands/wp-security-tfa-commands.php
Executable
+216
@@ -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);
|
||||
}
|
||||
}
|
||||
+253
@@ -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 &.
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
+716
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Our list of families
|
||||
*/
|
||||
return array(
|
||||
array('name' => '6G', 'priority' => 10),
|
||||
array('name' => 'Blacklist', 'priority' => 1),
|
||||
array('name' => 'Bruteforce', 'priority' => 0),
|
||||
array('name' => 'General', 'priority' => 20),
|
||||
array('name' => 'Bots', 'priority' => 2),
|
||||
);
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Builds all our families
|
||||
*/
|
||||
class Family_Builder {
|
||||
|
||||
/**
|
||||
* Get our families sorted by priority
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_families() {
|
||||
|
||||
$family_list = include(AIOWPS_FIREWALL_DIR.'/family/wp-security-firewall-families.php');
|
||||
|
||||
//Prioritise the families
|
||||
usort($family_list, function($member, $member2) {
|
||||
if ($member['priority'] == $member2['priority']) {
|
||||
return 0;
|
||||
}
|
||||
return ($member['priority'] > $member2['priority']) ? 1 : -1;
|
||||
});
|
||||
|
||||
$families = array();
|
||||
foreach ($family_list as $member) {
|
||||
$families[strtolower($member['name'])] = new Family($member['name'], $member['priority']);
|
||||
}
|
||||
|
||||
return $families;
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Holds all our families
|
||||
*/
|
||||
class Family_Collection {
|
||||
|
||||
/**
|
||||
* Holds our families
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $families;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs our family collection object
|
||||
*
|
||||
* @param array $families - The sorted families to contain
|
||||
*/
|
||||
public function __construct($families = array()) {
|
||||
$this->families = $families;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generator method to iterate over the families
|
||||
*
|
||||
* @return iterable
|
||||
*/
|
||||
public function get_family() {
|
||||
foreach ($this->families as $family) {
|
||||
yield $family;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new rule to a family member
|
||||
*
|
||||
* @param Rule $rule - an active rule to add to its family
|
||||
* @return void
|
||||
*/
|
||||
public function add_rule_to_member(Rule $rule) {
|
||||
$key = strtolower($rule->family);
|
||||
if (array_key_exists($key, $this->families)) {
|
||||
$this->families[$key]->add_rule($rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Represents a family (a grouping of rules)
|
||||
*/
|
||||
class Family {
|
||||
|
||||
/**
|
||||
* Name of the family
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* Priority of the family (0 is the highest)
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $priority;
|
||||
|
||||
/**
|
||||
* List of rules to apply
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $rules;
|
||||
|
||||
/**
|
||||
* Builds our family object
|
||||
*
|
||||
* @param string $name
|
||||
* @param integer $priority
|
||||
*/
|
||||
public function __construct($name, $priority = 999999) {
|
||||
$this->name = $name;
|
||||
$this->priority = $priority;
|
||||
$this->rules = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a rule to the family
|
||||
*
|
||||
* @param Rule $rule
|
||||
* @return void
|
||||
*/
|
||||
public function add_rule(Rule $rule) {
|
||||
$this->rules[] = $rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies all the rules in the family
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function apply_all() {
|
||||
|
||||
if (empty($this->rules)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//ensure the rules are ordered by priority
|
||||
usort($this->rules, function(Rule $rule, Rule $rule2) {
|
||||
if ($rule->priority == $rule2->priority) {
|
||||
return 0;
|
||||
}
|
||||
return ($rule->priority > $rule2->priority) ? 1 : -1;
|
||||
});
|
||||
|
||||
foreach ($this->rules as $rule) {
|
||||
$rule->apply();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the family name if used as a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
trait File_Prefix_Trait {
|
||||
|
||||
/**
|
||||
* Get the file's prefix content. N.B. Some code assumes that this doesn't change, so review all consumers of this method before changing its output.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_file_content_prefix() {
|
||||
$prefix = "<?php __halt_compiler();\n";
|
||||
$prefix .= "/**\n";
|
||||
$prefix .= " * This file was created by All In One Security (AIOS) plugin.\n";
|
||||
$prefix .= self::get_prefix_description();
|
||||
$prefix .= " */\n";
|
||||
return $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description of the file
|
||||
* You can override this method for each file that needs a file prefix in order to give it its own description
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_prefix_description() {
|
||||
return " * The file is required for storing and retrieving your firewall's settings.\n";
|
||||
}
|
||||
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
class Allow_List {
|
||||
|
||||
/**
|
||||
* Include a file prefix when the file is created
|
||||
*/
|
||||
use File_Prefix_Trait;
|
||||
|
||||
/**
|
||||
* Holds the path to the allow list
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $path;
|
||||
|
||||
/**
|
||||
* Overwrite the prefix description from File_Prefix_Trait
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_prefix_description() {
|
||||
return " * The file is required for storing and retrieving your firewall's allow list.\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the user's IP address is in the allow list
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_ip_allowed() {
|
||||
|
||||
$ips = self::get_ips();
|
||||
|
||||
if (empty($ips)) return false;
|
||||
|
||||
$ips = explode("\n", $ips);
|
||||
|
||||
return \AIOS_Helper::is_user_ip_address_within_list($ips);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of IP addresses in the allow list
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_ips() {
|
||||
|
||||
clearstatcache();
|
||||
if (!file_exists(self::$path)) return '';
|
||||
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Cannot use WP API. Firewall is loaded independent of WP.
|
||||
$contents = file_get_contents(self::$path, false, null, strlen(self::get_file_content_prefix()));
|
||||
|
||||
return (false !== $contents ? trim($contents) : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the path of the allow list
|
||||
*
|
||||
* @param string $path
|
||||
* @return void
|
||||
*/
|
||||
public static function set_path($path) {
|
||||
self::$path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add IPs to the allow list
|
||||
* This overwrites the whole allow list with the given IPs
|
||||
*
|
||||
* @param mixed $ips - A string of IPs; one per line or an array of individual IPs
|
||||
* @return bool
|
||||
*/
|
||||
public static function add_ips($ips) {
|
||||
|
||||
if (is_array($ips)) $ips = implode("\n", $ips);
|
||||
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Cannot use WP API. Firewall is loaded independent of WP.
|
||||
return (false !== file_put_contents(self::$path, self::get_file_content_prefix().$ips));
|
||||
}
|
||||
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
// phpcs:disable WordPress.WP.AlternativeFunctions -- WP isn't loaded here. WP API is unavailable
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Gives us access to our firewall's config
|
||||
*/
|
||||
class Config {
|
||||
|
||||
use File_Prefix_Trait;
|
||||
|
||||
/**
|
||||
* The path to our config file
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* Constructs object
|
||||
*
|
||||
* @param string $path
|
||||
*/
|
||||
public function __construct($path) {
|
||||
$this->path = $path;
|
||||
$this->init_file();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the file if it doesn't exist
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function init_file() {
|
||||
clearstatcache();
|
||||
if (!file_exists($this->path)) {
|
||||
|
||||
$dir = dirname($this->path);
|
||||
if (!file_exists($dir)) Utility::wp_mkdir_p($dir);
|
||||
|
||||
file_put_contents($this->path, self::get_file_content_prefix() . json_encode(array()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the config file with the new prefix whenever the prefix changes.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function update_prefix() {
|
||||
|
||||
$valid_prefix = self::get_file_content_prefix();
|
||||
$current_prefix = file_get_contents($this->path, false, null, 0, strlen($valid_prefix));
|
||||
|
||||
if ($current_prefix === $valid_prefix) return; // prefix is valid
|
||||
|
||||
$contents = file_get_contents($this->path);
|
||||
|
||||
$matches = array();
|
||||
if (preg_match('/\{.*\}/', $contents, $matches)) {
|
||||
//update settings
|
||||
file_put_contents($this->path, $valid_prefix . $matches[0]);
|
||||
} else {
|
||||
//reset settings
|
||||
file_put_contents($this->path, $valid_prefix . json_encode(array()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value from the config array
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function get_value($key) {
|
||||
|
||||
$contents = $this->get_contents();
|
||||
|
||||
if (null === $contents) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isset($contents[$key])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $contents[$key];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value in our config array
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return boolean
|
||||
*/
|
||||
public function set_value($key, $value) {
|
||||
|
||||
$contents = $this->get_contents();
|
||||
|
||||
if (null === $contents) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$contents[$key] = $value;
|
||||
|
||||
return (false !== file_put_contents($this->path, self::get_file_content_prefix() . json_encode($contents), LOCK_EX));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the config array from file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_contents() {
|
||||
|
||||
clearstatcache();
|
||||
if (!file_exists($this->path)) $this->init_file();
|
||||
|
||||
// __COMPILER_HALT_OFFSET__ doesn't define in a few PHP versions. It's a PHP bug.
|
||||
// https://bugs.php.net/bug.php?id=70164
|
||||
$contents = file_get_contents($this->path, false, null, strlen(self::get_file_content_prefix()));
|
||||
|
||||
if (false === $contents) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (empty($contents)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return json_decode($contents, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets entire firewall config from array.
|
||||
*
|
||||
* @param Array $contents
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function set_contents($contents) {
|
||||
|
||||
if (null === $contents) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (false !== file_put_contents($this->path, self::get_file_content_prefix() . json_encode($contents), LOCK_EX));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// phpcs:enable WordPress.WP.AlternativeFunctions -- WP isn't loaded here. WP API is unavailable
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* A class for accessing constants (including from wp-config) from the firewall
|
||||
* Only supports parsing 'defines' that have scalar types: int, float, boolean, string and null
|
||||
*/
|
||||
class Constants implements \ArrayAccess, \IteratorAggregate {
|
||||
|
||||
/**
|
||||
* The list of constants parsed
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $constants;
|
||||
|
||||
/**
|
||||
* The token part of the token identifier
|
||||
*
|
||||
* @see https://www.php.net/manual/en/function.token-get-all#refsect1-function.token-get-all-returnvalues
|
||||
*/
|
||||
const TOKEN = 0;
|
||||
|
||||
/**
|
||||
* The string content of the token identifier
|
||||
*
|
||||
* @see https://www.php.net/manual/en/function.token-get-all#refsect1-function.token-get-all-returnvalues
|
||||
*/
|
||||
const CONTENT = 1;
|
||||
|
||||
/**
|
||||
* The line number of the token identifier
|
||||
*
|
||||
* @see https://www.php.net/manual/en/function.token-get-all#refsect1-function.token-get-all-returnvalues
|
||||
*/
|
||||
const LINE = 2;
|
||||
|
||||
/**
|
||||
* Offset for define's name [ define(NAME, VALUE); ]
|
||||
*/
|
||||
const DEFINE_NAME_OFFSET = 2;
|
||||
|
||||
/**
|
||||
* Offset for define's value [ define(NAME, VALUE); ]
|
||||
*/
|
||||
const DEFINE_VALUE_OFFSET = 4;
|
||||
|
||||
/**
|
||||
* Constructs our object
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->constants = array();
|
||||
$this->populate_constants();
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates our internal constant array with the defines from wp-config
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function populate_constants() {
|
||||
|
||||
$wpconfig = Utility::get_wpconfig_path();
|
||||
|
||||
clearstatcache();
|
||||
if (!file_exists($wpconfig)) return;
|
||||
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- WP isn't loaded. WP_Filesystem cannot be used.
|
||||
$source = file_get_contents($wpconfig);
|
||||
|
||||
if (false === $source) return;
|
||||
|
||||
$tokens = token_get_all($source);
|
||||
|
||||
//Filter out any unwanted tokens
|
||||
$tokens = array_values(array_filter($tokens, function($token) {
|
||||
|
||||
//All tokens that are not arrays are allowed
|
||||
if (!is_array($token)) return true;
|
||||
|
||||
$unwanted_tokens = array(
|
||||
'T_COMMENT',
|
||||
'T_WHITESPACE',
|
||||
'T_DOC_COMMENT',
|
||||
);
|
||||
|
||||
return (!in_array(token_name($token[self::TOKEN]), $unwanted_tokens));
|
||||
}));
|
||||
|
||||
$token_count = count($tokens);
|
||||
for ($i = 0; $i < $token_count; $i++) {
|
||||
|
||||
$current = $tokens[$i];
|
||||
|
||||
if (!is_array($current)) continue;
|
||||
|
||||
if ('T_STRING' === token_name($current[self::TOKEN]) && 'define' === strtolower($current[self::CONTENT])) {
|
||||
|
||||
// Name of the define without the surrounding quotes
|
||||
$name = substr($tokens[$i + self::DEFINE_NAME_OFFSET][self::CONTENT], 1, -1);
|
||||
|
||||
// Grabs the value of the define
|
||||
$value = $tokens[$i + self::DEFINE_VALUE_OFFSET];
|
||||
|
||||
if (!is_array($value)) continue;
|
||||
|
||||
// We need to interpret the data type of the define's value
|
||||
switch (token_name($value[self::TOKEN])) {
|
||||
case 'T_CONSTANT_ENCAPSED_STRING':
|
||||
$this->constants[$name] = substr($value[self::CONTENT], 1, -1);
|
||||
break;
|
||||
case 'T_LNUMBER':
|
||||
$this->constants[$name] = intval($value[self::CONTENT]);
|
||||
break;
|
||||
case 'T_DNUMBER':
|
||||
$this->constants[$name] = floatval($value[self::CONTENT]);
|
||||
break;
|
||||
case 'T_STRING':
|
||||
$this->constants[$name] = filter_var($value[self::CONTENT], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
|
||||
break;
|
||||
default:
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the constants as properties
|
||||
*
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name) {
|
||||
return $this[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over the constants
|
||||
*
|
||||
* @return iterable
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator() {
|
||||
foreach ($this->constants as $name => $value) yield $name => $value;
|
||||
foreach (get_defined_constants() as $name => $value) yield $name => $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives us array access to the constants
|
||||
*
|
||||
* @param mixed $offset
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($offset) {
|
||||
if (defined($offset)) {
|
||||
return constant($offset);
|
||||
} elseif (isset($this->constants[$offset])) {
|
||||
return $this->constants[$offset];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the constant exists
|
||||
*
|
||||
* @param mixed $offset
|
||||
* @return boolean
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetExists($offset) {
|
||||
return defined($offset) || isset($this->constants[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if constant exists
|
||||
*
|
||||
* @param string $name
|
||||
* @return boolean
|
||||
*/
|
||||
public function __isset($name) {
|
||||
return $this->offsetExists($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the constant. This is disabled as we want it read-only
|
||||
*
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
* @throws \Exception - Throws an exception if called to ensure it's read-only.
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetSet($offset, $value) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Needed for ArrayAccess interface but not used by us as we require read-only
|
||||
throw new \Exception('Constants are read-only.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets the constant. This is disabled as we want it read-only
|
||||
*
|
||||
* @param mixed $offset
|
||||
* @return void
|
||||
* @throws \Exception - Throws an exception if called to ensure it's read-only.
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetUnset($offset) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Needed for ArrayAccess interface but not used by us as we require read-only
|
||||
throw new \Exception('Constants are read-only.');
|
||||
}
|
||||
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Class to help debug the firewall
|
||||
*/
|
||||
class Debug {
|
||||
|
||||
/**
|
||||
* Constructs our object
|
||||
*/
|
||||
public function __construct() {
|
||||
//Capture the events that relate to the firewall's rules
|
||||
Event::capture('rule_triggered', array($this, 'rule_debug'));
|
||||
Event::capture('rule_not_triggered', array($this, 'rule_debug'));
|
||||
Event::capture('rule_active', array($this, 'rule_debug'));
|
||||
Event::capture('rule_not_active', array($this, 'rule_debug'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures the firewall's events for debugging rules
|
||||
*
|
||||
* @global Constants $aiowps_firewall_constants
|
||||
* @global Message_Store $aiowps_firewall_message_store
|
||||
*
|
||||
* @param string $event
|
||||
* @param Rule $rule
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rule_debug($event, Rule $rule) {
|
||||
global $aiowps_firewall_constants, $aiowps_firewall_message_store;
|
||||
if (!$aiowps_firewall_constants->AIOS_FIREWALL_DEBUG && 'rule_triggered' !== $event) return;
|
||||
|
||||
$details = array(
|
||||
'name' => $rule->name,
|
||||
'family' => $rule->family,
|
||||
'ip' => \AIOS_Helper::get_user_ip_address(),
|
||||
'time' => time(),
|
||||
);
|
||||
|
||||
// Get any user information
|
||||
foreach ($_COOKIE as $key => $value) {
|
||||
if (preg_match('/^wordpress_logged_in_/', $key)) {
|
||||
$details['potential_user'] = stripslashes($value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$details['request'] = $_SERVER;
|
||||
unset($details['request']['HTTP_COOKIE']);
|
||||
|
||||
// Uncomment when the firewall log issues have been resolved
|
||||
//$aiowps_firewall_message_store->set($event, $details);
|
||||
|
||||
// Remove when the firewall log issues have been resolved
|
||||
$aiowps_firewall_message_store->clear_message_store();
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
class Event {
|
||||
|
||||
/**
|
||||
* Stores our events
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $events = array();
|
||||
|
||||
/**
|
||||
* Captures an event
|
||||
*
|
||||
* @param string $name - Name of the event
|
||||
* @param callable $callback - Callback to execute when the event is raised
|
||||
* @return void
|
||||
*/
|
||||
public static function capture($name, callable $callback) {
|
||||
$name = strtolower($name);
|
||||
|
||||
if (!isset(self::$events[$name])) {
|
||||
self::$events[$name] = array();
|
||||
}
|
||||
|
||||
self::$events[$name][] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Raises the event
|
||||
*
|
||||
* All the callbacks in a given name are executed
|
||||
*
|
||||
* @param string $name - Name of the event to raise
|
||||
* @param array ...$args - Variable list of arguments to pass to the callback
|
||||
* @return void
|
||||
*/
|
||||
public static function raise($name, ...$args) {
|
||||
$name = strtolower($name);
|
||||
|
||||
if (empty(self::$events[$name])) return;
|
||||
|
||||
array_unshift($args, $name);
|
||||
|
||||
foreach (self::$events[$name] as $event) {
|
||||
call_user_func_array($event, $args);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Use this when throwing an exception if you want to also exit the request
|
||||
*/
|
||||
class Exit_Exception extends \Exception {
|
||||
}
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
class Message_Store {
|
||||
|
||||
/**
|
||||
* Makes this class a singleton
|
||||
*/
|
||||
use Singleton_Trait;
|
||||
|
||||
/**
|
||||
* Internal store of the messages
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $messages;
|
||||
|
||||
/**
|
||||
* Holds the name of the message store's table
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $table_name;
|
||||
|
||||
/**
|
||||
* A key should only be loaded from the database once per request; this keeps track of them
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $keys_loaded;
|
||||
|
||||
/**
|
||||
* Constructs our object
|
||||
*/
|
||||
private function __construct() {
|
||||
Event::capture('action_before_exit', array($this, 'dump'));
|
||||
$this->messages = array();
|
||||
$this->keys_loaded = array();
|
||||
$this->table_name = 'aiowps_message_store';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets internal message store
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function set($key, $value) {
|
||||
|
||||
if (!is_string($key)) return;
|
||||
|
||||
if (!isset($this->messages[$key])) {
|
||||
$this->messages[$key] = array();
|
||||
}
|
||||
|
||||
$this->messages[$key][] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the messages associated with a key
|
||||
*
|
||||
* @param string $key
|
||||
* @return array
|
||||
*/
|
||||
public function get($key) {
|
||||
|
||||
$is_key_loaded = in_array($key, $this->keys_loaded);
|
||||
$can_check_database = isset($GLOBALS['wpdb']) && !$is_key_loaded && class_exists('Updraft_Semaphore_3_0');
|
||||
|
||||
//Load requested messages from the database
|
||||
if ($can_check_database) {
|
||||
|
||||
$lock = new \Updraft_Semaphore_3_0('aios_message_store_lock_'.$key, 60);
|
||||
$to_delete = array();
|
||||
|
||||
if ($lock->lock()) {
|
||||
|
||||
try {
|
||||
global $wpdb;
|
||||
|
||||
$table = $this->get_table();
|
||||
|
||||
// If we can't get the table to check the DB, still check our internal store for the key
|
||||
if (empty($table)) return isset($this->messages[$key]) ? $this->messages[$key] : array();
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery -- PCP error. Ignore.
|
||||
$rows = $wpdb->get_results($wpdb->prepare("SELECT id, message_value FROM `{$table}` WHERE message_key = %s", $key));
|
||||
|
||||
if (!empty($rows)) {
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$values = json_decode($row->message_value, true);
|
||||
|
||||
foreach ($values as $value) $this->set($key, $value);
|
||||
|
||||
$to_delete[] = $row->id;
|
||||
}
|
||||
|
||||
$this->keys_loaded[] = $key;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- PCP warning. Necessary for AIOS error reporting system.
|
||||
error_log("AIOS: Error getting database entries for key '{$key}': {$e->getMessage()}");
|
||||
|
||||
} catch (\Error $e) { // phpcs:ignore PHPCompatibility.Classes.NewClasses.errorFound -- this won't run on PHP 5.6 so we still want to catch it on other versions
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- PCP warning. Necessary for AIOS error reporting system.
|
||||
error_log("AIOS: Error getting database entries for key '{$key}': {$e->getMessage()}");
|
||||
} finally {
|
||||
|
||||
//Delete IDs of loaded messages
|
||||
if (!empty($to_delete)) {
|
||||
$ids = implode(',', $to_delete);
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery -- PCP error. Ignore.
|
||||
$wpdb->query("DELETE FROM `{$table}` WHERE id IN ({$ids})");
|
||||
}
|
||||
|
||||
$lock->release();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return isset($this->messages[$key]) ? $this->messages[$key] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps the message store to the database
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function dump() {
|
||||
//No point saving if there are no messages
|
||||
if (empty($this->messages)) return;
|
||||
|
||||
if (!Utility::attempt_to_access_wpdb()) throw new Exit_Exception('Unable to save the message store to the database: wpdb is inaccessible.');
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$table = $this->get_table();
|
||||
|
||||
if (empty($table)) throw new Exit_Exception('Unable to save messages store to the database: unable to get the correct table.');
|
||||
|
||||
$statement = "INSERT INTO `{$table}` (message_key, message_value, created) VALUES ";
|
||||
$values = array();
|
||||
|
||||
foreach ($this->messages as $key => $value) {
|
||||
$statement .= '(%s, %s, %s),';
|
||||
$values[] = $table;
|
||||
$values[] = $key;
|
||||
$values[] = json_encode($value); // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode -- This method runs outside the WordPress environment and therefore cannot use WordPress functions.
|
||||
$values[] = time();
|
||||
}
|
||||
|
||||
$statement = rtrim($statement, ',');
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery -- PCP error. Prepared above.
|
||||
$wpdb->query($wpdb->prepare($statement, $values));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the table name if it exists
|
||||
*
|
||||
* @return string - Table name on success; blank string otherwise
|
||||
*/
|
||||
private function get_table() {
|
||||
global $wpdb;
|
||||
|
||||
if (!$wpdb) return '';
|
||||
|
||||
$table = $wpdb->get_blog_prefix(0).$this->table_name;
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery -- PCP error. Ignore.
|
||||
if ($table != $wpdb->get_var("SHOW TABLES LIKE '{$table}'")) return '';
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all the messages from the message store table if it contains data.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clear_message_store() {
|
||||
global $wpdb;
|
||||
|
||||
$table = $this->get_table();
|
||||
|
||||
// Check if the table exists and is accessible
|
||||
if (empty($table)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Check if the table has any rows
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery -- PCP warning. Direct query necessary. No caching necessary.
|
||||
$row_exists = $wpdb->get_var(
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- PCP error. Ignore.
|
||||
$wpdb->prepare("SELECT EXISTS (SELECT 1 FROM `{$table}` LIMIT 1)")
|
||||
);
|
||||
|
||||
// If there are no rows, $row_exists will be 0
|
||||
if (!$row_exists) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the table (delete all records)
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery -- PCP error. Ignore.
|
||||
$wpdb->query($wpdb->prepare("DELETE FROM `{$table}`"));
|
||||
}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* A trait with a basic singleton implementation
|
||||
*/
|
||||
trait Singleton_Trait {
|
||||
|
||||
/**
|
||||
* Internally stores the class's instance
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Returns an instance of the class
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public static function instance() {
|
||||
|
||||
if (is_null(self::$instance)) self::$instance = new self();
|
||||
|
||||
return self::$instance;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* We don't want our singleton object to be cloned
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function __clone() {
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Trait which exits the current request
|
||||
*/
|
||||
trait Action_Exit_Trait {
|
||||
|
||||
/**
|
||||
* Exit when the rule condition is satisfied.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function do_action() {
|
||||
Event::raise('action_before_exit');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Combines the forbid and exit trait
|
||||
*/
|
||||
trait Action_Forbid_and_Exit_Trait {
|
||||
|
||||
use Action_Forbid_Trait, Action_Exit_Trait {
|
||||
Action_Forbid_Trait::do_action as protected do_action_forbid;
|
||||
Action_Exit_Trait::do_action as protected do_action_exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forbid 403 and Exit when the rule condition is satisfied.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function do_action() {
|
||||
$this->do_action_forbid();
|
||||
$this->do_action_exit();
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Trait to set the header to forbidden
|
||||
*/
|
||||
trait Action_Forbid_Trait {
|
||||
|
||||
/**
|
||||
* Forbid 403 when the rule condition is satisfied.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function do_action() {
|
||||
header('HTTP/1.1 403 Forbidden');
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
trait Action_Permblock_and_Exit_Trait {
|
||||
|
||||
/**
|
||||
* Use the forbid and exit trait
|
||||
*/
|
||||
use Action_Forbid_and_Exit_Trait {
|
||||
Action_Forbid_and_Exit_Trait::do_action as protected do_action_forbid_and_exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds the reason for the perm. block
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $permblock_reason = '';
|
||||
|
||||
/**
|
||||
* Holds the IP for the perm. block
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $permblock_ip = '';
|
||||
|
||||
/**
|
||||
* Sets the reason for the perm. block
|
||||
*
|
||||
* @param string $reason
|
||||
* @return void
|
||||
*/
|
||||
public function set_perm_block_reason($reason) {
|
||||
$this->permblock_reason = $reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the IP for the perm. block
|
||||
*
|
||||
* @param string $ip
|
||||
* @return void
|
||||
*/
|
||||
public function set_perm_block_ip($ip) {
|
||||
$this->permblock_ip = $ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Permanently ban the IP and exit when the rule condition is satisfied.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function do_action() {
|
||||
|
||||
if (!Utility::attempt_to_access_wpdb()) {
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- PCP warning. Part of AIOS error reporting system.
|
||||
error_log('AIOS: Unable to access wpdb to ban IP address.');
|
||||
$this->do_action_forbid_and_exit();
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$table = $wpdb->prefix.'aiowps_permanent_block';
|
||||
|
||||
$ip = empty($this->permblock_ip) ? \AIOS_Helper::get_user_ip_address() : $this->permblock_ip;
|
||||
|
||||
$data = array(
|
||||
'blocked_ip' => $ip,
|
||||
'block_reason' => empty($this->permblock_reason) ? 'firewall_generic' : $this->permblock_reason,
|
||||
'blocked_date' => current_time('mysql')
|
||||
);
|
||||
|
||||
// Check if the IP already exists
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL, WordPress.DB.DirectDatabaseQuery -- PCP error. Table name cannot be done via prepare.
|
||||
$already_exists = $wpdb->get_var($wpdb->prepare("SELECT blocked_ip FROM `{$table}` WHERE blocked_ip = %s", $ip));
|
||||
|
||||
// If it does exist, no point adding it again so just forbid and exit
|
||||
if (!is_null($already_exists)) $this->do_action_forbid_and_exit();
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery -- PCP error. Table name cannot be done via prepare.
|
||||
if (false === $wpdb->query($wpdb->prepare("INSERT INTO " .$table." (blocked_ip, block_reason, blocked_date, created) VALUES (%s, %s, %s, UNIX_TIMESTAMP())", $data['blocked_ip'], $data['block_reason'], $data['blocked_date']))) {
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- PCP warning. Needed for error reporting.
|
||||
error_log('AIOS: Unable to insert IP address into table.');
|
||||
}
|
||||
|
||||
$this->do_action_forbid_and_exit();
|
||||
}
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Combines the redirect and exit trait
|
||||
*/
|
||||
trait Action_Redirect_and_Exit_Trait {
|
||||
|
||||
use Action_Redirect_Trait, Action_Exit_Trait {
|
||||
Action_Redirect_Trait::do_action as protected do_action_redirect;
|
||||
Action_Exit_Trait::do_action as protected do_action_exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect and Exit when the rule condition is satisfied.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function do_action() {
|
||||
$this->do_action_redirect();
|
||||
$this->do_action_exit();
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Trait to set the header to redirect
|
||||
*/
|
||||
trait Action_Redirect_Trait {
|
||||
|
||||
/**
|
||||
* Redirect to the location.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $location = '127.0.0.1';
|
||||
|
||||
/**
|
||||
* Redirect the rule condition is satisfied.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function do_action() {
|
||||
header("Location: $this->location");
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Rule that blocks certain kinds of data from the query string
|
||||
*/
|
||||
class Rule_Block_Query_Strings_6g extends Rule {
|
||||
|
||||
/**
|
||||
* Implements the action to be taken
|
||||
*/
|
||||
use Action_Forbid_and_Exit_Trait;
|
||||
|
||||
/**
|
||||
* Construct our rule
|
||||
*/
|
||||
public function __construct() {
|
||||
// Set the rule's metadata
|
||||
$this->name = 'Block query strings';
|
||||
$this->family = '6G';
|
||||
$this->priority = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the rule is active
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_active() {
|
||||
global $aiowps_firewall_config;
|
||||
return (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_query');
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition to be satisfied for the rule to apply
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_satisfied() {
|
||||
|
||||
if (empty($_SERVER['QUERY_STRING'])) return Rule::NOT_SATISFIED;
|
||||
|
||||
//Patterns to match against
|
||||
$patterns = array(
|
||||
'/[a-z0-9]{2000,}/i',
|
||||
'/(eval\()/i',
|
||||
'/(127\.0\.0\.1)/i',
|
||||
'/(javascript:)(.*)(;)/i',
|
||||
'/(base64_encode)(.*)(\()/i',
|
||||
'/(GLOBALS|REQUEST)(=|\[|%)/i',
|
||||
'/(<|%3C)(.*)script(.*)(>|%3)/i',
|
||||
'#(\|\.\.\.|\.\./|~|`|<|>|\|)#i',
|
||||
'#(boot\.ini|etc/passwd|self/environ)#i',
|
||||
'/(thumbs?(_editor|open)?|tim(thumb)?)\.php/i',
|
||||
'/(\'|\")(.*)(drop|insert|md5|select|union)/i',
|
||||
);
|
||||
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- PCP warning. Sanitizing will interfere with 6g rules.
|
||||
return Rule_Utils::contains_pattern(rawurldecode($_SERVER['QUERY_STRING']), $patterns);
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Rule that blocks certain referrers recommended by 6G
|
||||
*/
|
||||
class Rule_Block_Refs_6g extends Rule {
|
||||
|
||||
/**
|
||||
* Implements the action to be taken
|
||||
*/
|
||||
use Action_Forbid_and_Exit_Trait;
|
||||
|
||||
/**
|
||||
* Construct our rule
|
||||
*/
|
||||
public function __construct() {
|
||||
// Set the rule's metadata
|
||||
$this->name = 'Block referrer strings';
|
||||
$this->family = '6G';
|
||||
$this->priority = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the rule is active
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_active() {
|
||||
global $aiowps_firewall_config;
|
||||
return (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_referrers');
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition to be satisfied for the rule to apply
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_satisfied() {
|
||||
|
||||
if (empty($_SERVER['HTTP_REFERER'])) return Rule::NOT_SATISFIED;
|
||||
|
||||
//Patterns to match against
|
||||
$patterns = array(
|
||||
'/[a-z0-9]{2000,}/i',
|
||||
'/(semalt.com|todaperfeita)/i',
|
||||
);
|
||||
|
||||
return Rule_Utils::contains_pattern($_SERVER['HTTP_REFERER'], $patterns); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- This is not a WordPress context. Also this only evaluates to a boolean.
|
||||
}
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Rule that blocks certain kinds of data from the request string
|
||||
*/
|
||||
class Rule_Block_Request_Strings_6g extends Rule {
|
||||
|
||||
/**
|
||||
* Implements the action to be taken
|
||||
*/
|
||||
use Action_Forbid_and_Exit_Trait;
|
||||
|
||||
/**
|
||||
* Construct our rule
|
||||
*/
|
||||
public function __construct() {
|
||||
// Set the rule's metadata
|
||||
$this->name = 'Block request strings';
|
||||
$this->family = '6G';
|
||||
$this->priority = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the rule is active
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_active() {
|
||||
global $aiowps_firewall_config;
|
||||
return (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_request');
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition to be satisfied for the rule to apply
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_satisfied() {
|
||||
|
||||
if (empty($_SERVER['REQUEST_URI'])) return Rule::NOT_SATISFIED;
|
||||
|
||||
// ensure we get the request uri without the query string
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- PCP warning. Sanitizing will interfere with 6g rules.
|
||||
$uri = (string) parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
|
||||
if ('' == $uri) return Rule::NOT_SATISFIED;
|
||||
|
||||
//Patterns to match against
|
||||
$patterns = array(
|
||||
'/[a-z0-9]{2000,}/i',
|
||||
'#(https?|ftp|php):/#i',
|
||||
'#(base64_encode)(.*)(\()#i',
|
||||
'#(=\'|=\%27|/\'/?)\.#i',
|
||||
'#/(\$(\&)?|\*|\"|\.|,|&|&?)/?$#i',
|
||||
'#(\{0\}|\(/\(|\.\.\.|\+\+\+|\\"\\")#i',
|
||||
'#(~|`|<|>|:|;|,|%|\|\s|\{|\}|\[|\]|\|)#i',
|
||||
'#/(=|\$&|_mm|cgi-|etc/passwd|muieblack)#i',
|
||||
'#(&pws=0|_vti_|\(null\)|\{\$itemURL\}|echo(.*)kae|etc/passwd|eval\(|self/environ)#i',
|
||||
'#\.(aspx?|bash|bak?|cfg|cgi|dll|exe|git|hg|ini|jsp|log|mdb|out|sql|svn|swp|tar|rar|rdf)$#i',
|
||||
'#/(^$|(wp-)?config|mobiquo|phpinfo|shell|sqlpatch|thumb|thumb_editor|thumbopen|timthumb|webshell)\.php#i',
|
||||
);
|
||||
|
||||
return Rule_Utils::contains_pattern(rawurldecode($uri), $patterns);
|
||||
}
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Rule that blocks certain user-agents recommended by 6G
|
||||
*/
|
||||
class Rule_Block_User_Agents_6g extends Rule {
|
||||
|
||||
/**
|
||||
* Implements the action to be taken
|
||||
*/
|
||||
use Action_Forbid_and_Exit_Trait;
|
||||
|
||||
/**
|
||||
* Construct our rule
|
||||
*/
|
||||
public function __construct() {
|
||||
// Set the rule's metadata
|
||||
$this->name = 'Block user-agents';
|
||||
$this->family = '6G';
|
||||
$this->priority = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the rule is active
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_active() {
|
||||
global $aiowps_firewall_config;
|
||||
return (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_agents');
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition to be satisfied for the rule to apply
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_satisfied() {
|
||||
|
||||
if (empty($_SERVER['HTTP_USER_AGENT'])) return Rule::NOT_SATISFIED;
|
||||
|
||||
//Patterns to match against
|
||||
$patterns = array(
|
||||
'/[a-z0-9]{2000,}/i',
|
||||
'/(archive.org|binlar|casper|checkpriv|choppy|clshttp|cmsworld|diavol|dotbot|extract|feedfinder|flicky|g00g1e|harvest|heritrix|httrack|kmccrew|loader|miner|nikto|nutch|planetwork|postrank|purebot|pycurl|python|seekerspider|siclab|skygrid|sqlmap|sucker|turnit|vikspider|winhttp|xxxyy|youda|zmeu|zune)/i',
|
||||
);
|
||||
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- PCP warning. Sanitizing will interfere with 6g rules.
|
||||
return Rule_Utils::contains_pattern($_SERVER['HTTP_USER_AGENT'], $patterns);
|
||||
}
|
||||
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Rule that blocks certain kinds of HTTP request methods (e.g DEBUG or PUT)
|
||||
*/
|
||||
class Rule_Request_Method_6g extends Rule {
|
||||
|
||||
/**
|
||||
* Implements the action to be taken
|
||||
*/
|
||||
use Action_Forbid_and_Exit_Trait;
|
||||
|
||||
/**
|
||||
* List of request methods to block
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $blocked_methods;
|
||||
|
||||
/**
|
||||
* Construct our rule
|
||||
*/
|
||||
public function __construct() {
|
||||
global $aiowps_firewall_config;
|
||||
|
||||
// Set the rule's metadata
|
||||
$this->name = 'Block request methods';
|
||||
$this->family = '6G';
|
||||
$this->priority = 0;
|
||||
|
||||
$this->blocked_methods = $aiowps_firewall_config->get_value('aiowps_6g_block_request_methods');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the rule is active
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_active() {
|
||||
return !empty($this->blocked_methods);
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition to be satisfied for the rule to apply
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_satisfied() {
|
||||
return isset($_SERVER['REQUEST_METHOD']) && in_array(strtoupper($_SERVER['REQUEST_METHOD']), $this->blocked_methods);
|
||||
}
|
||||
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Rule that blocks IPs to access.
|
||||
*/
|
||||
class Rule_Ips_Blacklist extends Rule {
|
||||
|
||||
/**
|
||||
* Implements the action to be taken
|
||||
*/
|
||||
use Action_Forbid_and_Exit_Trait;
|
||||
|
||||
/**
|
||||
* List of IPs / IP range to block
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $blocked_ips;
|
||||
|
||||
/**
|
||||
* Construct our rule
|
||||
*
|
||||
* @global Config $aiowps_firewall_config
|
||||
*/
|
||||
public function __construct() {
|
||||
global $aiowps_firewall_config;
|
||||
|
||||
// Set the rule's metadata
|
||||
$this->name = 'Blocked IPs';
|
||||
$this->family = 'Blacklist';
|
||||
$this->priority = 0;
|
||||
$this->blocked_ips = $aiowps_firewall_config->get_value('aiowps_blacklist_ips');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the rule is active
|
||||
*
|
||||
* @global Constants $aiowps_firewall_constants
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_active() {
|
||||
global $aiowps_firewall_constants;
|
||||
if ($aiowps_firewall_constants->AIOS_DISABLE_BLACKLIST_IP_MANAGER) {
|
||||
return false;
|
||||
} else {
|
||||
return !empty($this->blocked_ips);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition to be satisfied for the rule to apply
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_satisfied() {
|
||||
|
||||
$user_ip_blocked = \AIOS_Helper::is_user_ip_address_within_list($this->blocked_ips);
|
||||
|
||||
if (true == $user_ip_blocked) return Rule::SATISFIED;
|
||||
|
||||
return Rule::NOT_SATISFIED;
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Rule that blocks user agents to access.
|
||||
*/
|
||||
class Rule_User_Agent_Blacklist extends Rule {
|
||||
|
||||
/**
|
||||
* Implements the action to be taken
|
||||
*/
|
||||
use Action_Forbid_and_Exit_Trait;
|
||||
|
||||
/**
|
||||
* List of user agents to block
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $blocked_user_agents;
|
||||
|
||||
/**
|
||||
* Construct our rule
|
||||
*/
|
||||
public function __construct() {
|
||||
global $aiowps_firewall_config;
|
||||
|
||||
// Set the rule's metadata
|
||||
$this->name = 'Blocked user agents';
|
||||
$this->family = 'Blacklist';
|
||||
$this->priority = 0;
|
||||
$this->blocked_user_agents = $aiowps_firewall_config->get_value('aiowps_blacklist_user_agents');
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the rule is active
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_active() {
|
||||
return !empty($this->blocked_user_agents) && isset($_SERVER['HTTP_USER_AGENT']);
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition to be satisfied for the rule to apply
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_satisfied() {
|
||||
foreach ($this->blocked_user_agents as $block_user_agent) {
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- PCP warning. Sanitizing will interfere with 6g rules.
|
||||
if (isset($_SERVER['HTTP_USER_AGENT']) && !empty($block_user_agent) && false !== stripos($_SERVER['HTTP_USER_AGENT'], $block_user_agent)) {
|
||||
return Rule::SATISFIED;
|
||||
}
|
||||
}
|
||||
return Rule::NOT_SATISFIED;
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Rule that bans the IP address if the POST request has a blank user-agent and referer
|
||||
*/
|
||||
class Rule_Ban_Post_Blank_Headers extends Rule {
|
||||
|
||||
/**
|
||||
* Implements the action to be taken
|
||||
*/
|
||||
use Action_Permblock_and_Exit_Trait;
|
||||
|
||||
/**
|
||||
* Construct our rule
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
// Set the rule's metadata
|
||||
$this->name = 'Ban POST requests with blank user-agent and referer';
|
||||
$this->family = 'Bots';
|
||||
$this->priority = 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the rule is active
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_active() {
|
||||
global $aiowps_firewall_config;
|
||||
return (bool) $aiowps_firewall_config->get_value('aiowps_ban_post_blank_headers');
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition to be satisfied for the rule to apply
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_satisfied() {
|
||||
$this->set_perm_block_reason('firewall_post_blank_user_agent_and_referer');
|
||||
return isset($_SERVER['REQUEST_METHOD']) && (0 === strcasecmp($_SERVER['REQUEST_METHOD'], "POST")) && empty($_SERVER['HTTP_USER_AGENT']) && empty($_SERVER['HTTP_REFERER']); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- This is not a WordPress context. Also this only evaluates to a boolean.
|
||||
}
|
||||
}
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Rule that blocks fake Googlebots.
|
||||
*/
|
||||
class Rule_Block_Fake_Googlebots extends Rule {
|
||||
|
||||
/**
|
||||
* Implements the action to be taken.
|
||||
*/
|
||||
use Action_Exit_Trait;
|
||||
|
||||
/**
|
||||
* Construct our rule.
|
||||
*/
|
||||
public function __construct() {
|
||||
// Set the rule's metadata.
|
||||
$this->name = 'Block fake Googlebots';
|
||||
$this->family = 'Bots';
|
||||
$this->priority = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the rule is active.
|
||||
*
|
||||
* @global Config $aiowps_firewall_config
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_active() {
|
||||
global $aiowps_firewall_config;
|
||||
return (bool) $aiowps_firewall_config->get_value('aiowps_block_fake_googlebots');
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition to be satisfied for the rule to apply.
|
||||
*
|
||||
* @global Config $aiowps_firewall_config
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_satisfied() {
|
||||
global $aiowps_firewall_config;
|
||||
|
||||
$user_agent = (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '');
|
||||
|
||||
if (preg_match('/Googlebot/i', $user_agent, $matches)) {
|
||||
// If the user agent says it's a Googlebot, start doing checks.
|
||||
|
||||
$ip = \AIOS_Helper::get_user_ip_address();
|
||||
|
||||
if (empty($ip)) {
|
||||
return Rule::NOT_SATISFIED;
|
||||
}
|
||||
|
||||
try {
|
||||
$name = gethostbyaddr($ip); // Let's get the hostname using the IP address.
|
||||
|
||||
if ($name == $ip || false === $name) {
|
||||
// gethostbyaddr failed.
|
||||
$googlebot_ips = $aiowps_firewall_config->get_value('aiowps_googlebot_ip_ranges');
|
||||
if (\AIOS_Helper::is_user_ip_address_within_list($googlebot_ips)) {
|
||||
return Rule::NOT_SATISFIED;
|
||||
} else {
|
||||
return Rule::SATISFIED;
|
||||
}
|
||||
}
|
||||
|
||||
$host_ip = gethostbyname($name); // Reverse lookup - let's get the IP address using the hostname.
|
||||
} catch (\Exception $e) {
|
||||
// gethostbyaddr or gethostbyname not available on site.
|
||||
$googlebot_ips = $aiowps_firewall_config->get_value('aiowps_googlebot_ip_ranges');
|
||||
if (\AIOS_Helper::is_user_ip_address_within_list($googlebot_ips)) {
|
||||
return Rule::NOT_SATISFIED;
|
||||
} else {
|
||||
return Rule::SATISFIED;
|
||||
}
|
||||
} catch (\Error $e) { // phpcs:ignore PHPCompatibility.Classes.NewClasses.errorFound -- this won't run on PHP 5.6 so we still want to catch it on other versions
|
||||
// gethostbyaddr or gethostbyname not available on site.
|
||||
$googlebot_ips = $aiowps_firewall_config->get_value('aiowps_googlebot_ip_ranges');
|
||||
if (\AIOS_Helper::is_user_ip_address_within_list($googlebot_ips)) {
|
||||
return Rule::NOT_SATISFIED;
|
||||
} else {
|
||||
return Rule::SATISFIED;
|
||||
}
|
||||
}
|
||||
|
||||
if (preg_match('/^(?:.+\.)?googlebot\.com$/i', $name) || preg_match('/^(?:.+\.)?google\.com$/i', $name) || preg_match('/^(?:.+\.)?googleusercontent\.com$/i', $name)) {
|
||||
if ($host_ip == $ip) {
|
||||
return Rule::NOT_SATISFIED;
|
||||
} else {
|
||||
return Rule::SATISFIED;
|
||||
}
|
||||
} else {
|
||||
return Rule::SATISFIED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Rule that uses a cookie to prevent bruteforce attacks.
|
||||
*/
|
||||
class Rule_Cookie_Prevent_Bruteforce extends Rule {
|
||||
|
||||
/**
|
||||
* Implements the action to be taken
|
||||
*/
|
||||
use Action_Redirect_and_Exit_Trait;
|
||||
|
||||
/**
|
||||
* Construct our rule
|
||||
*/
|
||||
public function __construct() {
|
||||
// Set the rule's metadata
|
||||
$this->name = 'Cookie based prevent bruteforce';
|
||||
$this->family = 'Bruteforce';
|
||||
$this->priority = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the rule is active
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_active() {
|
||||
global $aiowps_firewall_config, $aiowps_firewall_constants;
|
||||
if ($aiowps_firewall_constants->AIOS_DISABLE_COOKIE_BRUTE_FORCE_PREVENTION) {
|
||||
return false;
|
||||
} else {
|
||||
return (bool) $aiowps_firewall_config->get_value('aios_enable_brute_force_attack_prevention');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition to be satisfied for the rule to apply
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_satisfied() {
|
||||
global $aiowps_firewall_config;
|
||||
/**
|
||||
* This rule is not applied at AIOS plugin activation time.
|
||||
*/
|
||||
$is_plugins_page = isset($_SERVER['SCRIPT_FILENAME']) && 1 === preg_match('#/wp-admin/(network/)?plugins\.php$#i', $_SERVER['SCRIPT_FILENAME']);
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- PCP warning. A nonce is not available at this point.
|
||||
$is_activation_action = isset($_GET['action']) && 'activate' === $_GET['action'];
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- PCP warning. A nonce is not available at this point.
|
||||
$is_target_plugin = isset($_GET['plugin']) && 'all-in-one-wp-security-and-firewall/wp-security.php' === $_GET['plugin'];
|
||||
|
||||
|
||||
if ($is_plugins_page && $is_activation_action && $is_target_plugin) {
|
||||
return Rule::NOT_SATISFIED;
|
||||
}
|
||||
|
||||
$brute_force_secret_word = $aiowps_firewall_config->get_value('aios_brute_force_secret_word');
|
||||
$brute_force_secret_cookie_name = $aiowps_firewall_config->get_value('aios_brute_force_secret_cookie_name');
|
||||
$login_page_slug = $aiowps_firewall_config->get_value('aios_login_page_slug');
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- PCP warning. A nonce is not available at this point.
|
||||
if (!isset($_GET[$brute_force_secret_word])) {
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- PCP warning. Sanitizing is not required, as we validate the raw input.
|
||||
$brute_force_secret_cookie_val = isset($_COOKIE[$brute_force_secret_cookie_name]) ? $_COOKIE[$brute_force_secret_cookie_name] : '';
|
||||
$pw_protected_exception = $aiowps_firewall_config->get_value('aios_brute_force_attack_prevention_pw_protected_exception');
|
||||
$prevent_ajax_exception = $aiowps_firewall_config->get_value('aios_brute_force_attack_prevention_ajax_exception');
|
||||
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- PCP warning. Sanitizing is not required, as we validate the raw input.
|
||||
if (!empty($_SERVER['REQUEST_URI']) && !hash_equals($brute_force_secret_cookie_val, \AIOS_Helper::get_hash($brute_force_secret_word))) {
|
||||
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- PCP warning. Sanitizing is not required, as we validate the raw input.
|
||||
$request_uri = (string) parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
|
||||
// admin section or login page or login custom slug called
|
||||
$is_admin_or_login = (false != strpos($request_uri, 'wp-admin') || false != strpos($request_uri, 'wp-login') || ('' != $login_page_slug && false != strpos($request_uri, $login_page_slug))) ? 1 : 0;
|
||||
|
||||
// admin side ajax called
|
||||
$is_admin_ajax_request = ('1' == $prevent_ajax_exception && isset($_SERVER['SCRIPT_NAME']) && ('admin-ajax.php' === basename($_SERVER['SCRIPT_NAME']))) ? 1 : 0;
|
||||
|
||||
// password protected page called
|
||||
$is_password_protected_access = ('1' == $pw_protected_exception && isset($_GET['action']) && 'postpass' == $_GET['action']) ? 1 : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- PCP warning. A nonce is not available at this point.
|
||||
|
||||
// logout, set password, reset password action called
|
||||
$is_logout_resetpassword_action = (isset($_GET['action']) && ('logout' == $_GET['action'] || 'rp' == $_GET['action'] || 'resetpass' == $_GET['action'])) ? 1 : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- PCP warning. A nonce is not available at this point.
|
||||
|
||||
// cookie based brute force on and accessing admin without ajax and password protected then redirect
|
||||
if ($is_admin_or_login && !$is_admin_ajax_request && !$is_password_protected_access && !$is_logout_resetpassword_action) {
|
||||
$redirect_url = $aiowps_firewall_config->get_value('aios_cookie_based_brute_force_redirect_url');
|
||||
$this->location = $redirect_url;
|
||||
return Rule::SATISFIED;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Rule::NOT_SATISFIED;
|
||||
}
|
||||
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Rule that blocks certain kinds of data from the request string
|
||||
*/
|
||||
class Rule_Advanced_Character_Filter extends Rule {
|
||||
|
||||
/**
|
||||
* Implements the action to be taken
|
||||
*/
|
||||
use Action_Forbid_and_Exit_Trait;
|
||||
|
||||
/**
|
||||
* Construct our rule
|
||||
*/
|
||||
public function __construct() {
|
||||
// Set the rule's metadata
|
||||
$this->name = 'Advanced character filter';
|
||||
$this->family = 'General';
|
||||
$this->priority = 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the rule is active
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_active() {
|
||||
global $aiowps_firewall_config;
|
||||
return (bool) $aiowps_firewall_config->get_value('aiowps_advanced_char_string_filter');
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition to be satisfied for the rule to apply
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_satisfied() {
|
||||
|
||||
if (empty($_SERVER['REQUEST_URI'])) return Rule::NOT_SATISFIED;
|
||||
|
||||
// ensure we get the request uri without the query string
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- PCP warning. Sanitizing will interfere with 6g rules.
|
||||
$uri = (string) parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
||||
|
||||
return Rule_Utils::contains_pattern($uri, array_merge($this->get_general_characters(), $this->get_common_patterns(), $this->get_specific_exploits()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of 'specific exploits' patterns
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_specific_exploits() {
|
||||
return array(
|
||||
'/errors\./i',
|
||||
'/config\./i',
|
||||
'/include\./i',
|
||||
'/display\./i',
|
||||
'/register\./i',
|
||||
'/password\./i',
|
||||
'/maincore\./i',
|
||||
'/authorize\./i',
|
||||
'/macromates\./i',
|
||||
'/head\_auth\./i',
|
||||
'/submit\_links\./i',
|
||||
'/change\_action\./i',
|
||||
'/com\_facileforms\//i',
|
||||
'/admin\_db\_utilities\./i',
|
||||
'/admin\.webring\.docs\./i',
|
||||
'/Table\/Latest\/index\./i',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of common patterns
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_common_patterns() {
|
||||
return array(
|
||||
'/\_vpi/i',
|
||||
'/\.inc/i',
|
||||
'/xAou6/i',
|
||||
'/db\_name/i',
|
||||
'/select\(/i',
|
||||
'/convert\(/i',
|
||||
'/\/query\//i',
|
||||
'/ImpEvData/i',
|
||||
'/\.XMLHTTP/i',
|
||||
'/proxydeny/i',
|
||||
'/function\./i',
|
||||
'/remoteFile/i',
|
||||
'/servername/i',
|
||||
'/\&rptmode\=/i',
|
||||
'/sys\_cpanel/i',
|
||||
'/db\_connect/i',
|
||||
'/doeditconfig/i',
|
||||
'/check\_proxy/i',
|
||||
'/system\_user/i',
|
||||
'/\/\(null\)\//i',
|
||||
'/clientrequest/i',
|
||||
'/option\_value/i',
|
||||
'/ref\.outcontrol/i',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of general characters
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_general_characters() {
|
||||
return array(
|
||||
'/\,/i',
|
||||
'/\:/i',
|
||||
'/\;/i',
|
||||
'/\=/i',
|
||||
'/\[/i',
|
||||
'/\]/i',
|
||||
'/\^/i',
|
||||
'/\`/i',
|
||||
'/\{/i',
|
||||
'/\}/i',
|
||||
'/\~/i',
|
||||
'/\"/i',
|
||||
'/\$/i',
|
||||
'/\</i',
|
||||
'/\>/i',
|
||||
'/\|/i',
|
||||
'/\.\./i',
|
||||
'/\%0/i',
|
||||
'/\%A/i',
|
||||
'/\%B/i',
|
||||
'/\%C/i',
|
||||
'/\%D/i',
|
||||
'/\%E/i',
|
||||
'/\%F/i',
|
||||
'/\%22/i',
|
||||
'/\%27/i',
|
||||
'/\%28/i',
|
||||
'/\%29/i',
|
||||
'/\%3C/i',
|
||||
'/\%3E/i',
|
||||
'/\%3F/i',
|
||||
'/\%5B/i',
|
||||
'/\%5C/i',
|
||||
'/\%5D/i',
|
||||
'/\%7B/i',
|
||||
'/\%7C/i',
|
||||
'/\%7D/i',
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Rule that blocks certain data from the URL's query string
|
||||
*/
|
||||
class Rule_Bad_Query_Strings extends Rule {
|
||||
|
||||
/**
|
||||
* Implements the action to be taken
|
||||
*/
|
||||
use Action_Forbid_and_Exit_Trait;
|
||||
|
||||
/**
|
||||
* Construct our rule
|
||||
*/
|
||||
public function __construct() {
|
||||
// Set the rule's metadata
|
||||
$this->name = 'Bad query strings';
|
||||
$this->family = 'General';
|
||||
$this->priority = 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the rule is active
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_active() {
|
||||
global $aiowps_firewall_config;
|
||||
return (bool) $aiowps_firewall_config->get_value('aiowps_deny_bad_query_strings');
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition to be satisfied for the rule to apply
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_satisfied() {
|
||||
|
||||
if (empty($_SERVER['QUERY_STRING'])) return Rule::NOT_SATISFIED;
|
||||
|
||||
$patterns = array(
|
||||
'/ftp:/i',
|
||||
'/http:/i',
|
||||
'/https:/i',
|
||||
'/mosConfig/i',
|
||||
'/^.*(globals|encode|loopback).*/i',
|
||||
"/(\;|'|\"|%22).*(request|insert|union|declare|drop)/i",
|
||||
);
|
||||
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- PCP warning. Sanitizing will interfere with 6g rules.
|
||||
return Rule_Utils::contains_pattern($_SERVER['QUERY_STRING'], $patterns);
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Rule that blocks access to the xmlrpc.php file
|
||||
*/
|
||||
class Rule_Block_Xmlrpc extends Rule {
|
||||
|
||||
/**
|
||||
* Implements the action to be taken
|
||||
*/
|
||||
use Action_Forbid_and_Exit_Trait;
|
||||
|
||||
/**
|
||||
* Construct our rule
|
||||
*/
|
||||
public function __construct() {
|
||||
// Set the rule's metadata
|
||||
$this->name = 'Completely block XMLRPC';
|
||||
$this->family = 'General';
|
||||
$this->priority = 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the rule is active
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_active() {
|
||||
global $aiowps_firewall_config;
|
||||
return (bool) $aiowps_firewall_config->get_value('aiowps_enable_pingback_firewall');
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition to be satisfied for the rule to apply
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_satisfied() {
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- PCP warning. Sanitizing will interfere with 6g rules.
|
||||
return (isset($_SERVER['SCRIPT_FILENAME']) && 1 === preg_match('/\/xmlrpc\.php$/i', $_SERVER['SCRIPT_FILENAME']));
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Rule that blocks comments being posted if a proxy is detected.
|
||||
*/
|
||||
class Rule_Proxy_Comment_Posting extends Rule {
|
||||
|
||||
/**
|
||||
* Implements the action to be taken
|
||||
*/
|
||||
use Action_Forbid_and_Exit_Trait;
|
||||
|
||||
/**
|
||||
* Construct our rule
|
||||
*/
|
||||
public function __construct() {
|
||||
// Set the rule's metadata
|
||||
$this->name = 'Proxy comment posting';
|
||||
$this->family = 'General';
|
||||
$this->priority = 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the rule is active
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_active() {
|
||||
global $aiowps_firewall_config;
|
||||
return (bool) $aiowps_firewall_config->get_value('aiowps_forbid_proxy_comments');
|
||||
}
|
||||
|
||||
/**
|
||||
* The condition to be satisfied for the rule to apply
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_satisfied() {
|
||||
|
||||
//Preconditions for the rule
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- PCP warning. Sanitizing will interfere with 6g rules.
|
||||
$is_comment_form = (isset($_SERVER['SCRIPT_FILENAME']) && 1 === preg_match('/\/wp-comments-post\.php$/i', $_SERVER['SCRIPT_FILENAME']));
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- PCP warning. Sanitizing will interfere with 6g rules.
|
||||
$is_post = (isset($_SERVER['REQUEST_METHOD']) && 0 === strcasecmp($_SERVER['REQUEST_METHOD'], "POST"));
|
||||
|
||||
if (!$is_post || !$is_comment_form) return Rule::NOT_SATISFIED;
|
||||
|
||||
//Headers that are present if a proxy is being used
|
||||
$headers = array(
|
||||
'HTTP_VIA',
|
||||
'HTTP_FORWARDED',
|
||||
'HTTP_USERAGENT_VIA',
|
||||
'HTTP_X_FORWARDED_FOR',
|
||||
'HTTP_X_FORWARDED_HOST',
|
||||
'HTTP_PROXY_CONNECTION',
|
||||
'HTTP_XPROXY_CONNECTION',
|
||||
'HTTP_PC_REMOTE_ADDR',
|
||||
'HTTP_CLIENT_IP',
|
||||
);
|
||||
|
||||
foreach ($headers as $header) {
|
||||
if (!empty($_SERVER[$header])) return Rule::SATISFIED;
|
||||
}
|
||||
|
||||
return Rule::NOT_SATISFIED;
|
||||
}
|
||||
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Builds our rules
|
||||
*/
|
||||
class Rule_Builder {
|
||||
|
||||
/**
|
||||
* Gets our rule if it's active
|
||||
*
|
||||
* @return iterable
|
||||
*/
|
||||
public static function get_active_rule() {
|
||||
|
||||
foreach (self::get_rule_classname() as $classname) {
|
||||
|
||||
$rule = new $classname();
|
||||
|
||||
if (!$rule->is_active()) {
|
||||
Event::raise('rule_not_active', $rule, $classname);
|
||||
continue;
|
||||
}
|
||||
|
||||
Event::raise('rule_active', $rule, $classname);
|
||||
yield $rule;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the classname for each rule
|
||||
*
|
||||
* @return iterable
|
||||
*/
|
||||
private static function get_rule_classname() {
|
||||
$rec_iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(AIOWPS_FIREWALL_DIR.'/rule/rules/', \FilesystemIterator::SKIP_DOTS));
|
||||
|
||||
foreach ($rec_iterator as $dir_iterator) {
|
||||
$matches = array();
|
||||
if (preg_match('/^rule-(?<rule_name>.*)\.php$/', $dir_iterator->getFilename(), $matches)) {
|
||||
yield "AIOWPS\Firewall\Rule_".ucwords(str_replace('-', '_', $matches['rule_name']), '_');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Utility methods to help with the rules
|
||||
*/
|
||||
class Rule_Utils {
|
||||
|
||||
/**
|
||||
* Check if the subject contains the given pattern or patterns
|
||||
*
|
||||
* @param string $subject - The subject we wish to check the pattern or patterns against.
|
||||
* @param string|array $pattern - Regex pattern. An array for multiple patterns; a string otherwise.
|
||||
* @return boolean
|
||||
*/
|
||||
public static function contains_pattern($subject, $pattern) {
|
||||
|
||||
if (empty($subject)) return false;
|
||||
|
||||
if (is_string($pattern)) return (1 === preg_match($pattern, $subject));
|
||||
|
||||
if (!is_array($pattern)) return false;
|
||||
|
||||
foreach ($pattern as $patt) {
|
||||
if (preg_match($patt, $subject)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* Base class for our firewall rules
|
||||
*/
|
||||
abstract class Rule {
|
||||
|
||||
/**
|
||||
* Name of the rule
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* Name of the family the rule belongs to
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $family;
|
||||
|
||||
/**
|
||||
* Rule's priority (0 is the highest)
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $priority;
|
||||
|
||||
/**
|
||||
* An abstraction for when the rule is satisfied
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
const SATISFIED = true;
|
||||
|
||||
/**
|
||||
* An abstraction for when the rule is not satisfied
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
const NOT_SATISFIED = false;
|
||||
|
||||
|
||||
/**
|
||||
* Executes the rule's action
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function do_action();
|
||||
|
||||
/**
|
||||
* Check if the rule is active
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function is_active();
|
||||
|
||||
/**
|
||||
* Check if the rule has been satisfied
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function is_satisfied();
|
||||
|
||||
/**
|
||||
* Apply the rule and execute the action if satisfied
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function apply() {
|
||||
|
||||
if ($this->is_satisfied()) {
|
||||
|
||||
Event::raise('rule_triggered', $this, time());
|
||||
$this->do_action();
|
||||
|
||||
}
|
||||
|
||||
Event::raise('rule_not_triggered', $this, time());
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the rule's name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
/**
|
||||
* The firewall can be loaded from several different contexts. This class detects from which context the firewall is loaded.
|
||||
*/
|
||||
class Context {
|
||||
|
||||
/**
|
||||
* Possible contexts where the firewall can be loaded
|
||||
*/
|
||||
const DIRECTIVE = 'directive';
|
||||
const PLUGINS_LOADED = 'plugins_loaded';
|
||||
const WP_CONFIG = 'wp-config';
|
||||
const MU_PLUGIN = 'mu-plugin';
|
||||
|
||||
/**
|
||||
* Get the current context where the firewall is running
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function current() {
|
||||
|
||||
$incs = get_included_files();
|
||||
$index = self::get_bootstrap_index($incs);
|
||||
|
||||
$is_setup = (-1 !== $index);
|
||||
|
||||
if (!$is_setup) return self::PLUGINS_LOADED;
|
||||
|
||||
if (0 === $index) return self::DIRECTIVE;
|
||||
|
||||
if (preg_match('/wp-config\.php$/i', $incs[$index-1])) {
|
||||
return self::WP_CONFIG;
|
||||
}
|
||||
|
||||
if (preg_match('/aios-firewall-loader\.php$/', $incs[$index-1])) {
|
||||
return self::MU_PLUGIN;
|
||||
}
|
||||
|
||||
return self::DIRECTIVE;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we're in a context safe to run WordPress functions
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function wordpress_safe() {
|
||||
return (self::plugins_loaded() || self::mu_plugin());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current context is `plugins_loaded`
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function plugins_loaded() {
|
||||
return (self::PLUGINS_LOADED === self::current());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current context is `directive` (i.e: auto_prepend_file)
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function directive() {
|
||||
return (self::DIRECTIVE === self::current());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current context is `wp_config`
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function wp_config() {
|
||||
return (self::WP_CONFIG === self::current());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current context is `mu_plugin`
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function mu_plugin() {
|
||||
return (self::MU_PLUGIN === self::current());
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate the bootstrap file's index
|
||||
*
|
||||
* @param array $incs
|
||||
* @return int
|
||||
*/
|
||||
private static function get_bootstrap_index(array $incs) {
|
||||
foreach ($incs as $index => $file) {
|
||||
if (preg_match('/aios-bootstrap\.php$/', $file)) {
|
||||
return $index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
if (!defined('AIOWPS_FIREWALL_DIR')) {
|
||||
header('HTTP/1.1 403 Forbidden');
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Loads and executes our firewall
|
||||
*/
|
||||
class Loader {
|
||||
|
||||
/**
|
||||
* Reference to itself
|
||||
*
|
||||
* @var Loader
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* Loads and builds all the necessary files
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function load_firewall() {
|
||||
|
||||
try {
|
||||
|
||||
/**
|
||||
* The preloader file should not be directly accessed.
|
||||
* It should only be loaded via the bootstrap file or in a WordPress context
|
||||
*/
|
||||
if ($this->is_preloader_directly_accessed()) return;
|
||||
|
||||
$this->init();
|
||||
|
||||
global $aiowps_firewall_constants;
|
||||
if ($aiowps_firewall_constants->AIOS_NO_FIREWALL) return;
|
||||
|
||||
//Allow list for bypassing PHP rules
|
||||
if (Allow_List::is_ip_allowed()) return;
|
||||
|
||||
$families = new Family_Collection(Family_Builder::get_families());
|
||||
|
||||
foreach (Rule_Builder::get_active_rule() as $rule) {
|
||||
$families->add_rule_to_member($rule);
|
||||
}
|
||||
|
||||
foreach ($families->get_family() as $member) {
|
||||
$member->apply_all();
|
||||
}
|
||||
|
||||
} catch (Exit_Exception $e) {
|
||||
$this->log_message($e->getMessage());
|
||||
exit();
|
||||
} catch (\Exception $e) {
|
||||
$this->log_message($e->getMessage());
|
||||
} catch (\Error $e) { // phpcs:ignore PHPCompatibility.Classes.NewClasses.errorFound -- this won't run on PHP 5.6 so we still want to catch it on other versions
|
||||
$this->log_message($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs general initialisation
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function init() {
|
||||
|
||||
$this->init_includes();
|
||||
$this->init_services();
|
||||
|
||||
new Debug();
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects whether the preloader file (wp-security-firewall.php) was directly accessed
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_preloader_directly_accessed() {
|
||||
return (1 === preg_match('/wp-security-firewall\.php$/', get_included_files()[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Log our error messages
|
||||
*
|
||||
* @param string $message
|
||||
* @return void
|
||||
*/
|
||||
private function log_message($message) {
|
||||
if (function_exists('do_action')) {
|
||||
do_action('aios_firewall_loader_log_error', $message, $this);
|
||||
}
|
||||
|
||||
error_log('AIOS firewall error: ' . $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises our services
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function init_services() {
|
||||
|
||||
$workspace = $this->get_firewall_workspace();
|
||||
|
||||
if (empty($workspace)) {
|
||||
throw new \Exception('unable to locate workspace.');
|
||||
}
|
||||
|
||||
$GLOBALS['aiowps_firewall_config'] = new Config($workspace . 'settings.php');
|
||||
$GLOBALS['aiowps_firewall_message_store'] = Message_Store::instance();
|
||||
$GLOBALS['aiowps_firewall_constants'] = new Constants();
|
||||
Allow_List::set_path($workspace.'allowlist.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get our workspace directory, i.e., where we save and load data to and from.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function get_firewall_workspace() {
|
||||
global $aiowps_firewall_rules_path;
|
||||
|
||||
$workspace = '';
|
||||
|
||||
if (!empty($aiowps_firewall_rules_path)) {
|
||||
$workspace = $aiowps_firewall_rules_path;
|
||||
} elseif (Context::wordpress_safe()) {
|
||||
$workspace = \AIOWPSecurity_Utility_Firewall::get_firewall_rules_path();
|
||||
}
|
||||
|
||||
return $workspace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the autoloader
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function init_includes() {
|
||||
|
||||
spl_autoload_register(function($class) {
|
||||
if (0 === strpos($class, "AIOWPS\\Firewall\\")) { //only autoload the firewall's files
|
||||
|
||||
$relative_classname = substr($class, strlen("AIOWPS\\Firewall\\"), strlen($class)-1);
|
||||
|
||||
$classname = str_replace('_', '-', strtolower($relative_classname));
|
||||
|
||||
$file = "wp-security-firewall-{$classname}.php";
|
||||
$rule = "{$classname}.php";
|
||||
|
||||
$paths = array(
|
||||
AIOWPS_FIREWALL_DIR."/{$file}",
|
||||
AIOWPS_FIREWALL_DIR."/family/{$file}",
|
||||
AIOWPS_FIREWALL_DIR."/rule/{$file}",
|
||||
AIOWPS_FIREWALL_DIR."/rule/actions/{$classname}.php",
|
||||
AIOWPS_FIREWALL_DIR."/rule/rules/{$rule}",
|
||||
AIOWPS_FIREWALL_DIR."/rule/rules/6g/{$rule}",
|
||||
AIOWPS_FIREWALL_DIR."/rule/rules/bruteforce/{$rule}",
|
||||
AIOWPS_FIREWALL_DIR."/rule/rules/blacklist/{$rule}",
|
||||
AIOWPS_FIREWALL_DIR."/rule/rules/general/{$rule}",
|
||||
AIOWPS_FIREWALL_DIR."/rule/rules/bots/{$rule}",
|
||||
AIOWPS_FIREWALL_DIR."/libs/{$file}",
|
||||
AIOWPS_FIREWALL_DIR."/libs/traits/{$classname}.php",
|
||||
);
|
||||
|
||||
clearstatcache();
|
||||
foreach ($paths as $path) {
|
||||
if (file_exists($path)) {
|
||||
include_once($path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Manually include needed files
|
||||
$classes_dir = dirname(AIOWPS_FIREWALL_DIR);
|
||||
|
||||
$manual_files = array(
|
||||
$classes_dir.'/wp-security-firewall-resource-unavailable.php',
|
||||
$classes_dir.'/wp-security-firewall-resource.php',
|
||||
$classes_dir.'/wp-security-helper.php',
|
||||
);
|
||||
|
||||
foreach ($manual_files as $file) {
|
||||
clearstatcache();
|
||||
if (file_exists($file)) include_once $file;
|
||||
}
|
||||
|
||||
if (Context::wordpress_safe()) {
|
||||
include_once("{$classes_dir}/wp-security-utility-file.php");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets or creates an instance of this object
|
||||
*
|
||||
* @return Loader
|
||||
*/
|
||||
public static function get_instance() {
|
||||
|
||||
if (null === self::$instance) {
|
||||
return new self();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
}
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
namespace AIOWPS\Firewall;
|
||||
|
||||
class Utility {
|
||||
|
||||
/**
|
||||
* Returns the directory of where the WordPress files are installed
|
||||
* This differs from get_root_dir() when WordPress is setup in a subdirectory
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_wordpress_dir() {
|
||||
|
||||
if (Context::wordpress_safe()) {
|
||||
return wp_normalize_path(ABSPATH);
|
||||
}
|
||||
|
||||
global $aiowps_firewall_data;
|
||||
|
||||
return isset($aiowps_firewall_data['ABSPATH']) ? $aiowps_firewall_data['ABSPATH'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root directory of the site
|
||||
* This may be different from where the WordPress files are installed if WordPress is setup in a subdirectory
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function get_root_dir() {
|
||||
|
||||
if (Context::wordpress_safe()) {
|
||||
return \AIOWPSecurity_Utility_File::get_home_path();
|
||||
}
|
||||
|
||||
// We're in the firewall context here, so get the root directory from the bootstrap file path
|
||||
$includes = get_included_files();
|
||||
|
||||
foreach ($includes as $file) {
|
||||
if (preg_match('/aios-bootstrap\.php$/', $file)) {
|
||||
return self::normalize_path(dirname($file).'/');
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the file path
|
||||
*
|
||||
* @see https://developer.wordpress.org/reference/functions/wp_normalize_path/
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public static function normalize_path($path) {
|
||||
// Standardize all paths to use '/'.
|
||||
$path = str_replace('\\', '/', $path);
|
||||
|
||||
// Replace multiple slashes down to a singular, allowing for network shares having two slashes.
|
||||
$path = preg_replace('|(?<=.)/+|', '/', $path);
|
||||
|
||||
// Windows paths should uppercase the drive letter.
|
||||
if (':' === substr($path, 1, 1)) {
|
||||
$path = ucfirst($path);
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to wp-config.php
|
||||
*
|
||||
* @param string $root - Where to look for wp-config.php file
|
||||
* @return string
|
||||
*/
|
||||
public static function get_wpconfig_path($root = '') {
|
||||
|
||||
if (empty($root)) $root = self::get_wordpress_dir();
|
||||
|
||||
$wp_config_file = $root . 'wp-config.php';
|
||||
if (file_exists($wp_config_file)) {
|
||||
return $wp_config_file;
|
||||
} elseif (file_exists(dirname($root) . '/wp-config.php')) {
|
||||
return dirname($root) . '/wp-config.php';
|
||||
}
|
||||
return $wp_config_file;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Recursive directory creation based on full path
|
||||
*
|
||||
* @see https://developer.wordpress.org/reference/functions/wp_mkdir_p/
|
||||
* @param string $target
|
||||
* @return bool
|
||||
*/
|
||||
public static function wp_mkdir_p($target) {
|
||||
$wrapper = null;
|
||||
|
||||
// Strip the protocol.
|
||||
if (self::wp_is_stream($target)) {
|
||||
list($wrapper, $target) = explode('://', $target, 2);
|
||||
}
|
||||
|
||||
// From php.net/mkdir user contributed notes.
|
||||
$target = str_replace('//', '/', $target);
|
||||
|
||||
// Put the wrapper back on the target.
|
||||
if (null !== $wrapper) {
|
||||
$target = $wrapper . '://' . $target;
|
||||
}
|
||||
|
||||
/*
|
||||
* Safe mode fails with a trailing slash under certain PHP versions.
|
||||
* Use rtrim() instead of untrailingslashit to avoid formatting.php dependency.
|
||||
*/
|
||||
$target = rtrim($target, '/');
|
||||
if (empty($target)) {
|
||||
$target = '/';
|
||||
}
|
||||
|
||||
if (file_exists($target)) {
|
||||
return @is_dir($target);
|
||||
}
|
||||
|
||||
// Do not allow path traversals.
|
||||
if (false !== strpos($target, '../') || false !== strpos($target, '..' . DIRECTORY_SEPARATOR)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We need to find the permissions of the parent folder that exists and inherit that.
|
||||
$target_parent = dirname($target);
|
||||
while ('.' !== $target_parent && ! is_dir($target_parent) && dirname($target_parent) !== $target_parent) {
|
||||
$target_parent = dirname($target_parent);
|
||||
}
|
||||
|
||||
// Get the permission bits
|
||||
$stat = @stat($target_parent);
|
||||
if ($stat) {
|
||||
$dir_perms = $stat['mode'] & 0007777;
|
||||
} else {
|
||||
$dir_perms = 0777;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_mkdir -- PCP error. WP not loaded. WP API not available.
|
||||
if (@mkdir($target, $dir_perms, true)) {
|
||||
|
||||
/*
|
||||
* If a umask is set that modifies $dir_perms, we'll have to re-set
|
||||
* the $dir_perms correctly with chmod()
|
||||
*/
|
||||
if (($dir_perms & ~umask()) != $dir_perms) {
|
||||
$folder_parts = explode('/', substr($target, strlen($target_parent) + 1));
|
||||
for ($i = 1, $c = count($folder_parts); $i <= $c; $i++) {
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions -- PCP error. WP not loaded. WP API not available.
|
||||
chmod($target_parent . '/' . implode('/', array_slice($folder_parts, 0, $i)), $dir_perms);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests if a given path is a stream URL
|
||||
*
|
||||
* @see https://developer.wordpress.org/reference/functions/wp_is_stream/
|
||||
* @param string $path
|
||||
* @return bool
|
||||
*/
|
||||
public static function wp_is_stream($path) {
|
||||
$scheme_separator = strpos($path, '://');
|
||||
|
||||
if (false === $scheme_separator) {
|
||||
// $path isn't a stream.
|
||||
return false;
|
||||
}
|
||||
|
||||
$stream = substr($path, 0, $scheme_separator);
|
||||
|
||||
return in_array($stream, stream_get_wrappers(), true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Attempts to give us access to the $wpdb object from the firewall.
|
||||
* This should only be used when you're sure WordPress will not be loading after the firewall.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function attempt_to_access_wpdb() {
|
||||
|
||||
// wpdb is already accessible
|
||||
if (isset($GLOBALS['wpdb'])) return true;
|
||||
|
||||
$wp_path = self::get_wordpress_dir() . 'wp-load.php';
|
||||
|
||||
clearstatcache();
|
||||
if (!file_exists($wp_path)) return false;
|
||||
|
||||
define('SHORTINIT', true);
|
||||
|
||||
$included = (bool) include $wp_path;
|
||||
|
||||
global $wpdb;
|
||||
|
||||
// If $wpdb is inaccessible by this point, it means loading wp-settings didn't complete.
|
||||
// So we have to manually include the wp-config (which includes wp-settings) for it to complete.
|
||||
if (empty($wpdb) && $included) include self::get_wpconfig_path();
|
||||
|
||||
global $wpdb;
|
||||
|
||||
return !empty($wpdb);
|
||||
|
||||
}
|
||||
}
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* This is the file that will be loaded using auto_prepend_file directive
|
||||
*/
|
||||
|
||||
if (!defined('AIOWPS_FIREWALL_DIR')) {
|
||||
define('AIOWPS_FIREWALL_DIR', dirname(__FILE__));
|
||||
define('AIOWPS_FIREWALL_DIR_PARENT', dirname(AIOWPS_FIREWALL_DIR));
|
||||
}
|
||||
|
||||
if (!defined('AIOWPSEC_FIREWALL_DONE')) {
|
||||
|
||||
//Gracefully handle if the file is unable to be included. (i.e: ensure the user's site does not crash)
|
||||
if (!(@include_once AIOWPS_FIREWALL_DIR . '/wp-security-firewall-loader.php')) {
|
||||
error_log('AIOS firewall error: failed to load the firewall. Unable to include wp-security-firewall-loader.php.');
|
||||
return;
|
||||
}
|
||||
|
||||
\AIOWPS\Firewall\Loader::get_instance()->load_firewall();
|
||||
define('AIOWPSEC_FIREWALL_DONE', true);
|
||||
}
|
||||
+800
@@ -0,0 +1,800 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Feature_Item_Manager {
|
||||
|
||||
private $feature_list = array();
|
||||
|
||||
public $feature_items = array();
|
||||
|
||||
private $total_points = 0;
|
||||
|
||||
private $total_achievable_points = 0;
|
||||
|
||||
private $feature_point_1 = "5";
|
||||
|
||||
private $feature_point_2 = "10";
|
||||
|
||||
private $feature_point_3 = "15";
|
||||
|
||||
private $feature_point_4 = "20";
|
||||
|
||||
private $sec_level_basic = "1";
|
||||
|
||||
private $sec_level_inter = "2";
|
||||
|
||||
private $sec_level_advanced = "3";
|
||||
|
||||
private $feature_active = "active";
|
||||
|
||||
private $feature_inactive = "inactive";
|
||||
|
||||
private $feature_partial = "partial";
|
||||
|
||||
/**
|
||||
* Constructor sets up the feature item manager
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->setup_feature_list();
|
||||
$this->initialize_features();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function sets up the feature list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function setup_feature_list() {
|
||||
$feature_list = array(
|
||||
// Settings menu features
|
||||
'wp-generator-meta-tag' => array(
|
||||
'name' => __('Remove WP generator meta tag', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_1,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_remove_wp_generator_meta_info'
|
||||
)
|
||||
),
|
||||
// User Accounts menu features
|
||||
'user-accounts-change-admin-user' => array(
|
||||
'name' => __('Change admin username', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_3,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
''
|
||||
),
|
||||
'callback' => array($this, 'check_user_accounts_change_admin_user_feature')
|
||||
),
|
||||
'user-accounts-display-name' => array(
|
||||
'name' => __('Change display name', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_1,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
''
|
||||
),
|
||||
'callback' => array($this, 'check_user_accounts_display_name_feature')
|
||||
),
|
||||
// User Login menu features
|
||||
'user-login-login-lockdown' => array(
|
||||
'name' => __('Login lockout', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_4,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_login_lockdown'
|
||||
)
|
||||
),
|
||||
'user-login-force-logout' => array(
|
||||
'name' => __('Force logout', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_1,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_forced_logout'
|
||||
)
|
||||
),
|
||||
'hibp' => array(
|
||||
'name' => __('HIBP', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_inter,
|
||||
'options' => array(
|
||||
'aiowps_hibp_user_profile_update',
|
||||
'aiowps_http_password_reset',
|
||||
)
|
||||
),
|
||||
'disable-application-password' => array(
|
||||
'name' => __('Disable application password', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_inter,
|
||||
'options' => array(
|
||||
'aiowps_disable_application_password'
|
||||
)
|
||||
),
|
||||
'user-login-lockout-ip-whitelisting' => array(
|
||||
'name' => __('Login Lockout IP whitelisting', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_3,
|
||||
'level' => $this->sec_level_inter,
|
||||
'options' => array(
|
||||
'aiowps_lockdown_enable_whitelisting'
|
||||
)
|
||||
),
|
||||
// User Registration menu features
|
||||
'manually-approve-registrations' => array(
|
||||
'name' => __('Registration approval', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_4,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_manual_registration_approval'
|
||||
)
|
||||
),
|
||||
'user-registration-captcha' => array(
|
||||
'name' => __('Registration CAPTCHA', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_4,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_registration_page_captcha'
|
||||
)
|
||||
),
|
||||
'registration-honeypot' => array(
|
||||
'name' => __('Enable registration honeypot', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_inter,
|
||||
'options' => array(
|
||||
'aiowps_enable_registration_honeypot'
|
||||
)
|
||||
),
|
||||
'http-authentication-admin-frontend' => array(
|
||||
'name' => __('HTTP authentication for admin and frontend', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_http_authentication_admin',
|
||||
'aiowps_http_authentication_frontend',
|
||||
)
|
||||
),
|
||||
// Database Security menu features
|
||||
'db-security-db-prefix' => array(
|
||||
'name' => __('Database prefix', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_inter,
|
||||
'options' => array(
|
||||
''
|
||||
),
|
||||
'callback' => array($this, 'check_db_security_db_prefix_feature')
|
||||
),
|
||||
// Filesystem Security menu features
|
||||
'filesystem-file-permissions' => array(
|
||||
'name' => __('File permissions', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_4,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
''
|
||||
),
|
||||
'callback' => array($this, 'check_filesystem_permissions_feature')
|
||||
),
|
||||
'filesystem-file-editing' => array(
|
||||
'name' => __('File editing', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_disable_file_editing'
|
||||
)
|
||||
),
|
||||
'auto-delete-wp-files' => array(
|
||||
'name' => __('WordPress files access', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_auto_delete_default_wp_files'
|
||||
)
|
||||
),
|
||||
// Blacklist Manager menu features
|
||||
'blacklist-manager-ip-user-agent-blacklisting' => array(
|
||||
'name' => __('IP and user agent blacklisting', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_3,
|
||||
'level' => $this->sec_level_advanced,
|
||||
'options' => array(
|
||||
'aiowps_enable_blacklisting'
|
||||
)
|
||||
),
|
||||
// Firewall menu features
|
||||
'firewall-basic-rules' => array(
|
||||
'name' => __('Enable basic firewall', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_3,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_basic_firewall'
|
||||
),
|
||||
'feature_condition_callback' => array('AIOWPSecurity_Utility', 'allow_to_write_to_htaccess')
|
||||
),
|
||||
'firewall-pingback-rules' => array(
|
||||
'name' => __('Enable pingback vulnerability protection', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_3,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_pingback_firewall'
|
||||
)
|
||||
),
|
||||
'firewall-block-debug-file-access' => array(
|
||||
'name' => __('Block access to debug log file', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_inter,
|
||||
'options' => array(
|
||||
'aiowps_block_debug_log_file_access'
|
||||
),
|
||||
'feature_condition_callback' => array('AIOWPSecurity_Utility', 'allow_to_write_to_htaccess')
|
||||
),
|
||||
'firewall-disable-index-views' => array(
|
||||
'name' => __('Disable index views', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_1,
|
||||
'level' => $this->sec_level_inter,
|
||||
'options' => array(
|
||||
'aiowps_disable_index_views'
|
||||
),
|
||||
'feature_condition_callback' => array('AIOWPSecurity_Utility', 'allow_to_write_to_htaccess')
|
||||
),
|
||||
'firewall-disable-trace-track' => array(
|
||||
'name' => __('Disable trace and track', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_advanced,
|
||||
'options' => array(
|
||||
'aiowps_disable_trace_and_track'
|
||||
),
|
||||
'feature_condition_callback' => array('AIOWPSecurity_Utility', 'allow_to_write_to_htaccess')
|
||||
),
|
||||
'firewall-forbid-proxy-comments' => array(
|
||||
'name' => __('Forbid proxy comments', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_advanced,
|
||||
'options' => array(
|
||||
'aiowps_forbid_proxy_comments'
|
||||
)
|
||||
),
|
||||
'firewall-deny-bad-queries' => array(
|
||||
'name' => __('Deny bad queries', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_3,
|
||||
'level' => $this->sec_level_advanced,
|
||||
'options' => array(
|
||||
'aiowps_deny_bad_query_strings'
|
||||
)
|
||||
),
|
||||
'firewall-advanced-character-string-filter' => array(
|
||||
'name' => __('Advanced character string filter', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_3,
|
||||
'level' => $this->sec_level_advanced,
|
||||
'options' => array(
|
||||
'aiowps_advanced_char_string_filter'
|
||||
)
|
||||
),
|
||||
'firewall-enable-6g' => array(
|
||||
'name' => __('6G firewall', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_4,
|
||||
'level' => $this->sec_level_advanced,
|
||||
'options' => array(
|
||||
'aiowps_enable_6g_firewall',
|
||||
)
|
||||
),
|
||||
'firewall-block-fake-googlebots' => array(
|
||||
'name' => __('Block fake Googlebots', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_1,
|
||||
'level' => $this->sec_level_advanced,
|
||||
'options' => array(
|
||||
'aiowps_block_fake_googlebots'
|
||||
)
|
||||
),
|
||||
'prevent-hotlinking' => array(
|
||||
'name' => __('Prevent image hotlinking', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_prevent_hotlinking'
|
||||
)
|
||||
),
|
||||
'firewall-enable-404-blocking' => array(
|
||||
'name' => __('Enable IP blocking for 404 detection', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_1,
|
||||
'level' => $this->sec_level_inter,
|
||||
'options' => array(
|
||||
'aiowps_enable_404_IP_lockout'
|
||||
)
|
||||
),
|
||||
'firewall-disable-rss-and-atom' => array(
|
||||
'name' => __('Disable RSS and ATOM feeds', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_disable_rss_and_atom_feeds'
|
||||
)
|
||||
),
|
||||
'upgrade-unsafe-http-calls' => array(
|
||||
'name' => __('Upgrade unsafe HTTP calls', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_inter,
|
||||
'options' => array(
|
||||
'aiowps_upgrade_unsafe_http_calls'
|
||||
)
|
||||
),
|
||||
// Brute Force menu features
|
||||
'bf-rename-login-page' => array(
|
||||
'name' => __('Enable rename login page', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_inter,
|
||||
'options' => array(
|
||||
'aiowps_enable_rename_login_page'
|
||||
)
|
||||
),
|
||||
'firewall-enable-brute-force-attack-prevention' => array(
|
||||
'name' => __('Enable brute force attack prevention', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_4,
|
||||
'level' => $this->sec_level_advanced,
|
||||
'options' => array(
|
||||
'aiowps_enable_brute_force_attack_prevention'
|
||||
)
|
||||
),
|
||||
'user-login-captcha' => array(
|
||||
'name' => __('Login CAPTCHA', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_4,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_login_captcha'
|
||||
)
|
||||
),
|
||||
'lost-password-captcha' => array(
|
||||
'name' => __('Lost password CAPTCHA', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_lost_password_captcha'
|
||||
)
|
||||
),
|
||||
'custom-login-captcha' => array(
|
||||
'name' => __('Custom login CAPTCHA', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_4,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_custom_login_captcha'
|
||||
)
|
||||
),
|
||||
'password_protected-captcha' => array(
|
||||
'name' => __('Password-protected CAPTCHA', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_1,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_password_protected_captcha'
|
||||
)
|
||||
),
|
||||
'whitelist-manager-ip-login-whitelisting' => array(
|
||||
'name' => __('Login IP whitelisting', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_3,
|
||||
'level' => $this->sec_level_inter,
|
||||
'options' => array(
|
||||
'aiowps_enable_whitelisting'
|
||||
)
|
||||
),
|
||||
'login-honeypot' => array(
|
||||
'name' => __('Enable login honeypot', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_inter,
|
||||
'options' => array(
|
||||
'aiowps_enable_login_honeypot'
|
||||
)
|
||||
),
|
||||
// Spam Prevention menu features
|
||||
'comment-form-captcha' => array(
|
||||
'name' => __('Comment CAPTCHA', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_4,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_comment_captcha'
|
||||
)
|
||||
),
|
||||
'detect-spambots' => array(
|
||||
'name' => __('Detect spambots', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_spambot_detecting'
|
||||
)
|
||||
),
|
||||
'auto-block-spam-ips' => array(
|
||||
'name' => __('Auto block spam ips', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_autoblock_spam_ip'
|
||||
)
|
||||
),
|
||||
// Scanner menu features
|
||||
'scan-file-change-detection' => array(
|
||||
'name' => __('File change detection', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_4,
|
||||
'level' => $this->sec_level_inter,
|
||||
'options' => array(
|
||||
'aiowps_enable_automated_fcd_scan'
|
||||
)
|
||||
),
|
||||
// Miscellaneous menu features
|
||||
'enable-copy-protection' => array(
|
||||
'name' => __('Enable Copy Protection', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_1,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_copy_protection'
|
||||
)
|
||||
),
|
||||
'enable-frame-protection' => array(
|
||||
'name' => __('Enable iFrame protection', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_1,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_prevent_site_display_inside_frame'
|
||||
)
|
||||
),
|
||||
'disable-users-enumeration' => array(
|
||||
'name' => __('Disable users enumeration', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_1,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_prevent_users_enumeration'
|
||||
)
|
||||
),
|
||||
'disallow-unauthorised-requests' => array(
|
||||
'name' => __('Disallow unauthorized REST requests', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_1,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_disallow_unauthorized_rest_requests'
|
||||
)
|
||||
),
|
||||
'enable-salt-postfix' => array(
|
||||
'name' => __('Enable salt postfix', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_3,
|
||||
'level' => $this->sec_level_advanced,
|
||||
'options' => array(
|
||||
'aiowps_enable_salt_postfix'
|
||||
)
|
||||
),
|
||||
// conditional features
|
||||
'bp-register-captcha' => array(
|
||||
'name' => __('BuddyPress registration CAPTCHA', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_1,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_bp_register_captcha'
|
||||
),
|
||||
'feature_condition_callback' => array('AIOWPSecurity_Utility', 'is_buddypress_plugin_active'),
|
||||
),
|
||||
'bbp-new-topic-captcha' => array(
|
||||
'name' => __('bbPress new topic CAPTCHA', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_1,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_bbp_new_topic_captcha'
|
||||
),
|
||||
'feature_condition_callback' => array('AIOWPSecurity_Utility', 'is_bbpress_plugin_active'),
|
||||
),
|
||||
'woo-login-captcha' => array(
|
||||
'name' => __('Woo login CAPTCHA', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_woo_login_captcha'
|
||||
),
|
||||
'feature_condition_callback' => array('AIOWPSecurity_Utility', 'is_woocommerce_plugin_active'),
|
||||
),
|
||||
'woo-lostpassword-captcha' => array(
|
||||
'name' => __('Woo lost password CAPTCHA', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_woo_lostpassword_captcha'
|
||||
),
|
||||
'feature_condition_callback' => array('AIOWPSecurity_Utility', 'is_woocommerce_plugin_active'),
|
||||
),
|
||||
'woo-register-captcha' => array(
|
||||
'name' => __('Woo register CAPTCHA', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_woo_register_captcha'
|
||||
),
|
||||
'feature_condition_callback' => array('AIOWPSecurity_Utility', 'is_woocommerce_plugin_active'),
|
||||
),
|
||||
'woo-checkout-captcha' => array(
|
||||
'name' => __('Woo Checkout CAPTCHA', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_woo_checkout_captcha'
|
||||
),
|
||||
'feature_condition_callback' => array('AIOWPSecurity_Utility', 'is_woocommerce_plugin_active'),
|
||||
),
|
||||
// Ban POST requests with blank user-agent and referer
|
||||
'firewall-ban-post-blank-headers' => array(
|
||||
'name' => __('Ban POST requests that have blank user-agent and referer headers', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_2,
|
||||
'level' => $this->sec_level_inter,
|
||||
'options' => array(
|
||||
'aiowps_ban_post_blank_headers'
|
||||
)
|
||||
),
|
||||
'contact-form-7-captcha' => array(
|
||||
/* translators: %s: Plugin name */
|
||||
'name' => sprintf(__('%s CAPTCHA', 'all-in-one-wp-security-and-firewall'), 'Contact Form 7'),
|
||||
'points' => $this->feature_point_1,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enable_contact_form_7_captcha'
|
||||
),
|
||||
'feature_condition_callback' => array('AIOWPSecurity_Utility', 'is_contact_form_7_plugin_active'),
|
||||
),
|
||||
'enforce-strong-password' => array(
|
||||
'name' => __('Enforce use of strong passwords by users', 'all-in-one-wp-security-and-firewall'),
|
||||
'points' => $this->feature_point_1,
|
||||
'level' => $this->sec_level_basic,
|
||||
'options' => array(
|
||||
'aiowps_enforce_strong_password'
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
$feature_list = apply_filters('aiowpsecurity_feature_list', $feature_list);
|
||||
$this->feature_list = array_filter($feature_list, array($this, 'should_add_item'));
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will create a feature item object for each feature in the feature list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function initialize_features() {
|
||||
foreach ($this->feature_list as $id => $data) {
|
||||
$callback = isset($data['callback']) ? $data['callback'] : array($this, 'is_feature_enabled');
|
||||
$this->feature_items[$id] = new AIOWPSecurity_Feature_Item($id, $data['name'], $data['points'], $data['level'], $data['options'], $callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will return the feature item for the passed in ID
|
||||
*
|
||||
* @param string $feature_id - the id of the feature item we want to return
|
||||
*
|
||||
* @return boolean|AIOWPSecurity_Feature_Item - returns a feature item or false on coding error
|
||||
*/
|
||||
public function get_feature_item_by_id($feature_id) {
|
||||
if (isset($this->feature_items[$feature_id])) return $this->feature_items[$feature_id];
|
||||
error_log("AIOS Feature Manager - Feature ID not found (coding error): $feature_id");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the callback function associated with the feature item.
|
||||
*
|
||||
* @param mixed $feature_item The feature item object.
|
||||
*/
|
||||
private function call_feature_callback($feature_item) {
|
||||
call_user_func($feature_item->callback, $feature_item);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will output the feature details badge HTML
|
||||
*
|
||||
* @param string $feature_id - the id of the feature we want to get the badge for
|
||||
* @param bool $return_instead_of_echo - whether to return the HTML or echo it
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public function output_feature_details_badge($feature_id, $return_instead_of_echo = false) {
|
||||
// Retrieve the feature item by ID
|
||||
$feature_item = $this->get_feature_item_by_id($feature_id);
|
||||
|
||||
if (!$feature_item) return;
|
||||
|
||||
$this->call_feature_callback($feature_item);
|
||||
|
||||
// Prepare HTML for the feature badge
|
||||
$max_security_points = $feature_item->item_points;
|
||||
$current_security_points = $feature_item->is_active() ? $max_security_points : 0;
|
||||
$security_level = $feature_item->get_security_level_string();
|
||||
$protection_level = (0 == $current_security_points) ? 'none' : 'full';
|
||||
$status_icon = (0 == $current_security_points) ? 'dashicons-unlock' : 'dashicons-lock';
|
||||
|
||||
$badge_html = '<div class="aiowps_feature_details_badge">';
|
||||
$badge_html .= '<span class="aiowps_feature_details_badge_difficulty aiowps_feature_protection_'.$protection_level.'" title="'.__('Feature difficulty', 'all-in-one-wp-security-and-firewall').'">';
|
||||
$badge_html .= '<span class="dashicons '.$status_icon.'"></span>'.$security_level.'</span>';
|
||||
$badge_html .= '<span class="aiowps_feature_details_badge_points" title="'.__('Security points', 'all-in-one-wp-security-and-firewall').'">';
|
||||
$badge_html .= $current_security_points.'/'.$max_security_points.'</span>';
|
||||
$badge_html .= '</div>';
|
||||
|
||||
if ($return_instead_of_echo) {
|
||||
return $badge_html;
|
||||
} else {
|
||||
echo $badge_html;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will calculate the total points for the AJAX save function
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function calculate_total_feature_points() {
|
||||
$this->calculate_total_points();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will setup the feature status and calculate the total points
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function check_feature_status_and_recalculate_points() {
|
||||
$this->check_and_set_feature_status();
|
||||
$this->calculate_total_points();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will calculate the total points for the site
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function calculate_total_points() {
|
||||
foreach ($this->feature_items as $item) {
|
||||
if ($item->is_active()) $this->total_points = $this->total_points + intval($item->item_points);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will calculate the total achievable points
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function calculate_total_achievable_points() {
|
||||
foreach ($this->feature_items as $item) {
|
||||
$this->total_achievable_points = $this->total_achievable_points + intval($item->item_points);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will return the total points for the site
|
||||
*
|
||||
* @return int - the total points for the site
|
||||
*/
|
||||
public function get_total_site_points() {
|
||||
if (empty($this->total_points)) $this->calculate_total_points();
|
||||
return $this->total_points;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will return the total achievable points
|
||||
*
|
||||
* @return int - the total achievable points
|
||||
*/
|
||||
public function get_total_achievable_points() {
|
||||
if (empty($this->total_achievable_points)) $this->calculate_total_achievable_points();
|
||||
return $this->total_achievable_points;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will loop over each feature item, checking if it's enabled and setting it's feature status
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function check_and_set_feature_status() {
|
||||
foreach ($this->feature_items as $item) {
|
||||
call_user_func($item->callback, $item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will check if the feature database value is active and set the feature status
|
||||
*
|
||||
* @param AIOWPSecurity_Feature_Item $item - the item we want to check is active
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function is_feature_enabled($item) {
|
||||
global $aio_wp_security;
|
||||
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
|
||||
|
||||
$enabled = false;
|
||||
foreach ($item->feature_options as $option) {
|
||||
if ('1' == $aiowps_firewall_config->get_value($option) || '1' == $aio_wp_security->configs->get_value($option)) $enabled = true;
|
||||
}
|
||||
|
||||
if ($enabled) {
|
||||
$item->set_feature_status($this->feature_active);
|
||||
} else {
|
||||
$item->set_feature_status($this->feature_inactive);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will check if the default admin user exists and set the feature status
|
||||
*
|
||||
* @param AIOWPSecurity_Feature_Item $item - the item we want to check is active
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function check_user_accounts_change_admin_user_feature($item) {
|
||||
if (AIOWPSecurity_Utility::check_user_exists('admin')) {
|
||||
$item->set_feature_status($this->feature_inactive);
|
||||
} else {
|
||||
$item->set_feature_status($this->feature_active);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will check if the username and nicknames are identical and set the feature status
|
||||
*
|
||||
* @param AIOWPSecurity_Feature_Item $item - the item we want to check is active
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function check_user_accounts_display_name_feature($item) {
|
||||
if (AIOWPSecurity_Utility::check_identical_login_and_nick_names()) {
|
||||
$item->set_feature_status($this->feature_inactive);
|
||||
} else {
|
||||
$item->set_feature_status($this->feature_active);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will check if the default database prefix is in use and set the feature status
|
||||
*
|
||||
* @param AIOWPSecurity_Feature_Item $item - the item we want to check is active
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function check_db_security_db_prefix_feature($item) {
|
||||
global $wpdb;
|
||||
$site_id = get_current_blog_id();
|
||||
$default_prefix = (1 === $site_id) ? 'wp_' : "wp_{$site_id}_";
|
||||
if ($default_prefix === $wpdb->prefix) {
|
||||
$item->set_feature_status($this->feature_inactive);
|
||||
} else {
|
||||
$item->set_feature_status($this->feature_active);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will check the filesystem permissions and set the feature status
|
||||
*
|
||||
* @param AIOWPSecurity_Feature_Item $item - the item we want to check is active
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function check_filesystem_permissions_feature($item) {
|
||||
//TODO
|
||||
$is_secure = 1;
|
||||
$files_dirs_to_check = AIOWPSecurity_Utility_File::get_files_and_dirs_to_check();
|
||||
|
||||
foreach ($files_dirs_to_check as $file_or_dir) {
|
||||
$actual_perm = AIOWPSecurity_Utility_File::get_file_permission($file_or_dir['path']);
|
||||
$is_secure = $is_secure*AIOWPSecurity_Utility_File::is_file_permission_secure($file_or_dir['permissions'], $actual_perm);
|
||||
}
|
||||
|
||||
//Only if all of the files' permissions are deemed secure give this a thumbs up
|
||||
if (1 == $is_secure) {
|
||||
$item->set_feature_status($this->feature_active);
|
||||
} else {
|
||||
$item->set_feature_status($this->feature_inactive);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will check if an item should be added to the feature list
|
||||
*
|
||||
* @param array $item - the item we want to check if it should be added
|
||||
* @return bool
|
||||
*/
|
||||
public static function should_add_item($item) {
|
||||
if (empty($item['feature_condition_callback'])) {
|
||||
return true;
|
||||
} elseif (is_callable($item['feature_condition_callback'])) {
|
||||
return call_user_func($item['feature_condition_callback']);
|
||||
} else {
|
||||
error_log("Callback function set but not callable (coding error). Feature: " . $item['name']);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Feature_Item {
|
||||
|
||||
public $feature_id; // Example "user-accounts-tab1-change-admin-user"
|
||||
|
||||
public $feature_name;
|
||||
|
||||
public $item_points;
|
||||
|
||||
public $security_level; // 1, 2, 3
|
||||
|
||||
public $feature_status; // active, inactive, partial
|
||||
|
||||
public $feature_options; // The database options to check if the feature is enabled or not
|
||||
|
||||
public $callback;
|
||||
|
||||
/**
|
||||
* Constructor sets up the feature item
|
||||
*
|
||||
* @param string $feature_id - the id of the feature
|
||||
* @param string $feature_name - the name of the feature
|
||||
* @param string $item_points - the points this feature is worth
|
||||
* @param string $security_level - the level of the feature
|
||||
* @param array $feature_options - the options the feature uses
|
||||
* @param boolean $callback - callback to set feature active status
|
||||
*/
|
||||
public function __construct($feature_id, $feature_name, $item_points, $security_level, $feature_options, $callback) {
|
||||
$this->feature_id = $feature_id;
|
||||
$this->feature_name = $feature_name;
|
||||
$this->item_points = $item_points;
|
||||
$this->security_level = $security_level;
|
||||
$this->feature_options = $feature_options;
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will set the status of the feature
|
||||
*
|
||||
* @param string $status - the status of the feature
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_feature_status($status) {
|
||||
$this->feature_status = $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will return the string version of the level
|
||||
*
|
||||
* @return string - the string value of the level
|
||||
*/
|
||||
public function get_security_level_string() {
|
||||
$level_string = "";
|
||||
if ("1" == $this->security_level) {
|
||||
$level_string = __('Basic', 'all-in-one-wp-security-and-firewall');
|
||||
} elseif ("2" == $this->security_level) {
|
||||
$level_string = __('Intermediate', 'all-in-one-wp-security-and-firewall');
|
||||
} elseif ("3" == $this->security_level) {
|
||||
$level_string = __('Advanced', 'all-in-one-wp-security-and-firewall');
|
||||
}
|
||||
return $level_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will return a boolean to indicate if the feature is on or not
|
||||
*
|
||||
* @return boolean - true if the feature is on otherwise false
|
||||
*/
|
||||
public function is_active() {
|
||||
return ('active' == $this->feature_status);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
/**
|
||||
* Do not modify the files in this folder.
|
||||
*/
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) die('Access denied.');
|
||||
/**
|
||||
* This is a small glue class, which makes available all the commands in AIOWPSecurity_Commands, and translates the response from AIOWPSecurity_Commands (which is either data to return, or a WP_Error) into the format used by UpdraftCentral.
|
||||
*/
|
||||
class UpdraftCentral_WP_Security_Commands extends UpdraftCentral_Commands {
|
||||
|
||||
private $commands;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->commands = new AIOWPSecurity_Commands();
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic method to pass on the command to AIOWPSecurity_Commands
|
||||
*
|
||||
* @param string $name - command name
|
||||
* @param array $arguments - command parameters
|
||||
*
|
||||
* @return array - response
|
||||
*/
|
||||
public function __call($name, $arguments) {
|
||||
if (!is_callable(array($this->commands, $name))) {
|
||||
return $this->_generic_error_response('aios_no_such_command', $name);
|
||||
}
|
||||
|
||||
$result = call_user_func_array(array($this->commands, $name), $arguments);
|
||||
|
||||
if (is_wp_error($result)) {
|
||||
return $this->_generic_error_response($result->get_error_code(), $result->get_error_data());
|
||||
} else {
|
||||
return $this->_response($result);
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) die('Access denied.');
|
||||
|
||||
/**
|
||||
* This file is the bootstrapper for UpdraftCentral integration: i.e. it registers what is necessary to deal with commands in the AIOS namespace.
|
||||
*/
|
||||
class WP_Security_UpdraftCentral {
|
||||
|
||||
public function __construct() {
|
||||
add_filter('updraftplus_remotecontrol_command_classes', array($this, 'updraftcentral_remotecontrol_command_classes'));
|
||||
add_filter('updraftcentral_remotecontrol_command_classes', array($this, 'updraftcentral_remotecontrol_command_classes'));
|
||||
add_action('updraftcentral_command_class_wanted', array($this, 'updraftcentral_command_class_wanted'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register our class
|
||||
*
|
||||
* @param string $command_classes Passing over an array of command classes.
|
||||
*/
|
||||
public function updraftcentral_remotecontrol_command_classes($command_classes) {
|
||||
if (is_array($command_classes)) $command_classes['aios'] = 'UpdraftCentral_WP_Security_Commands';
|
||||
return $command_classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the class when required
|
||||
*
|
||||
* @param string $command_php_class Passing over if there are any command classes.
|
||||
*/
|
||||
public function updraftcentral_command_class_wanted($command_php_class) {
|
||||
if ('UpdraftCentral_WP_Security_Commands' == $command_php_class) {
|
||||
// This fragment is only needed for compatibility with UD < 1.12.30 - thenceforth, the class can be assumed to exist.
|
||||
if (!class_exists('UpdraftCentral_Commands')) {
|
||||
include_once(apply_filters('updraftcentral_command_base_class_at', UPDRAFTPLUS_DIR.'/central/commands.php'));
|
||||
}
|
||||
include_once(AIO_WP_SECURITY_PATH.'/classes/updraftcentral-wp-security-commands.php');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
new WP_Security_UpdraftCentral();
|
||||
Executable
+121
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH') && !defined('AIOWPS_FIREWALL_DIR')) {
|
||||
exit; //Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* All ids and static names, array.
|
||||
*/
|
||||
if (class_exists('AIOS_Abstracted_Ids')) return;
|
||||
|
||||
class AIOS_Abstracted_Ids {
|
||||
|
||||
/**
|
||||
* Get firewall block request methods.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_firewall_block_request_methods() {
|
||||
return array('DEBUG','MOVE', 'PUT', 'TRACK');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get IP retrieve methods.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_ip_retrieve_methods() {
|
||||
// The keys are merely for maintaining backward compatibility.
|
||||
return array(
|
||||
'0' => 'REMOTE_ADDR',
|
||||
'1' => 'HTTP_CF_CONNECTING_IP',
|
||||
'2' => 'HTTP_X_FORWARDED_FOR',
|
||||
'3' => 'HTTP_X_FORWARDED',
|
||||
'4' => 'HTTP_CLIENT_IP',
|
||||
'5' => 'HTTP_X_REAL_IP',
|
||||
'6' => 'HTTP_X_CLUSTER_CLIENT_IP',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get AIOS custom admin notice ids.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function custom_admin_notice_ids() {
|
||||
return array(
|
||||
'automated-database-backup',
|
||||
'ip-retrieval-settings',
|
||||
'load-firewall-resources-failed',
|
||||
'end-of-support-php-56',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get notice ids for notices that have transformed HTACESS rules to PHP.
|
||||
*
|
||||
* @return array notice ids.
|
||||
*/
|
||||
public static function htaccess_to_php_feature_notice_ids() {
|
||||
return array(
|
||||
'login-whitelist-disabled-on-upgrade',
|
||||
'ip-blacklist-settings-on-upgrade',
|
||||
'upgrade-firewall-tab-rules',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get locale codes that are more than 2 char long supported by Google ReCaptcha.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_google_recaptcha_locale_codes() {
|
||||
/**
|
||||
* Google reCaptcha accepts 2 char language codes and also more than 2 char language codes.
|
||||
* Most are 2 chars in length e.g. 'ar' for Arabic.
|
||||
* Few are more than 2 char in length e.g 'de-AT' for German (Austria)
|
||||
*
|
||||
* Below is the list of more than 2 char language codes supported by Google reCaptcha.
|
||||
* if determine_locale() detects any of the below we return it, otherwise,
|
||||
* we would return the 2 letter code.
|
||||
*/
|
||||
return array(
|
||||
'zh-HK', // Chinese (Hong Kong).
|
||||
'zh-CN', // Chinese (Simplified).
|
||||
'zh-TW', // Chinese (Traditional).
|
||||
'en-GB', // UK.
|
||||
'fr-CA', // French (Canadian).
|
||||
'de-AT', // German (Austria).
|
||||
'de-CH', // German (Switzerland).
|
||||
'pt-BR', // Portuguese (Brazil).
|
||||
'pt-PT', // Portuguese (Portugal).
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get IP Lookup services.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_ip_lookup_services() {
|
||||
return array(
|
||||
'ipify' => 'http://api.ipify.org/',
|
||||
'ipecho' => 'http://ipecho.net/plain',
|
||||
'ident' => 'http://ident.me',
|
||||
'tnedi' => 'http://tnedi.me',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Reverse IP Lookup services.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function get_reverse_ip_lookup_services() {
|
||||
return array(
|
||||
'ip-api' => 'http://ip-api.com/json/%s',
|
||||
'ipinfo' => 'https://ipinfo.io/%s/json'
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
+253
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
|
||||
if (!defined('ABSPATH')) die('Access denied.');
|
||||
|
||||
if (!class_exists('AIOWPSecurity_Ajax')) :
|
||||
|
||||
class AIOWPSecurity_Ajax {
|
||||
|
||||
private $commands_object;
|
||||
|
||||
private $nonce;
|
||||
|
||||
private $subaction;
|
||||
|
||||
private $data;
|
||||
|
||||
private $results;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
if (!class_exists('AIOWPSecurity_Commands')) include_once(AIO_WP_SECURITY_PATH.'/classes/wp-security-commands.php');
|
||||
$this->commands_object = new AIOWPSecurity_Commands();
|
||||
|
||||
add_action('wp_ajax_aios_ajax', array($this, 'handle_ajax_requests'));
|
||||
add_action('wp_ajax_nopriv_get_antibot_keys', array($this->commands_object, 'get_antibot_keys'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return singleton instance
|
||||
*
|
||||
* @return AIOWPSecurity_Ajax Returns AIOWPSecurity_Ajax object
|
||||
*/
|
||||
public static function get_instance() {
|
||||
static $instance = null;
|
||||
if (null === $instance) {
|
||||
$instance = new self();
|
||||
}
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles ajax requests
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle_ajax_requests() {
|
||||
$this->set_nonce();
|
||||
$this->set_subaction();
|
||||
$this->set_data();
|
||||
|
||||
$result = AIOWPSecurity_Utility_Permissions::check_nonce_and_user_cap($this->nonce, 'wp-security-ajax-nonce');
|
||||
if (is_wp_error($result)) {
|
||||
wp_send_json(array(
|
||||
'result' => false,
|
||||
'error_code' => $result->get_error_code(),
|
||||
'error_message' => $result->get_error_message(),
|
||||
));
|
||||
}
|
||||
|
||||
if (is_multisite() && !current_user_can('manage_network_options')) {
|
||||
if (!$this->is_valid_multisite_command()) {
|
||||
$this->send_invalid_multisite_command_error_response();
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->is_invalid_command()) {
|
||||
$this->add_invalid_command_error_log_entry();
|
||||
$this->set_invalid_command_error_response();
|
||||
} else {
|
||||
$this->execute_command();
|
||||
$this->set_error_response_on_wp_error();
|
||||
$this->maybe_set_results_as_null();
|
||||
}
|
||||
|
||||
$this->json_encode_results();
|
||||
|
||||
$json_last_error = json_last_error();
|
||||
if ($json_last_error) {
|
||||
$this->set_error_response_on_json_encode_error($json_last_error);
|
||||
}
|
||||
|
||||
echo $this->results; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Variable is an array containing escaped data.
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets nonce property value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function set_nonce() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput -- It's the actual nonce. It does not need sanitizing.
|
||||
$this->nonce = empty($_POST['nonce']) ? '' : $_POST['nonce'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets subaction property value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function set_subaction() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce already checked.
|
||||
$this->subaction = empty($_POST['subaction']) ? '' : sanitize_text_field(wp_unslash($_POST['subaction']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets data property value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function set_data() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce already checked.
|
||||
$this->data = isset($_POST['data']) ? wp_unslash($_POST['data']) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether it is multisite setup and command is valid multisite command
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_valid_multisite_command() {
|
||||
/**
|
||||
* Filters the commands allowed to the sub site admins. Other commands are only available to network admin. Only used in a multisite context.
|
||||
*/
|
||||
$allowed_commands = apply_filters('aios_multisite_allowed_commands', array(
|
||||
'delete_audit_log',
|
||||
'delete_locked_ip_record',
|
||||
'clear_debug_logs',
|
||||
'unlock_ip',
|
||||
'blocked_ip_list_unblock_ip',
|
||||
'lock_ip',
|
||||
'dismiss_notice',
|
||||
));
|
||||
|
||||
return in_array($this->subaction, $allowed_commands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the invalid multisite command error response
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function send_invalid_multisite_command_error_response() {
|
||||
wp_send_json(array(
|
||||
'result' => false,
|
||||
'error_code' => 'update_failed',
|
||||
'error_message' => __('Options can only be saved by network admin', 'all-in-one-wp-security-and-firewall')
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if applied ajax command is an invalid command or not
|
||||
*
|
||||
* @return bool Returns true if ajax command is an invalid command, false otherwise
|
||||
*/
|
||||
private function is_invalid_command() {
|
||||
return !is_callable(array($this->commands_object, $this->subaction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Log an error message for invalid ajax command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function add_invalid_command_error_log_entry() {
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Part of error reporting.
|
||||
error_log("AIOS: ajax_handler: no such command (" . $this->subaction . ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set `results` property with error response array for invalid ajax command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function set_invalid_command_error_response() {
|
||||
$this->results = array(
|
||||
'result' => false,
|
||||
'error_code' => 'command_not_found',
|
||||
/* translators: %s: Subaction */
|
||||
'error_message' => sprintf(__('The command "%s" was not found', 'all-in-one-wp-security-and-firewall'), $this->subaction)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the ajax command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function execute_command() {
|
||||
$this->results = call_user_func(array($this->commands_object, $this->subaction), $this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set `results` property with error message
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function set_error_response_on_wp_error() {
|
||||
if (is_wp_error($this->results)) {
|
||||
$this->results = array(
|
||||
'result' => false,
|
||||
'error_code' => $this->results->get_error_code(),
|
||||
'error_message' => $this->results->get_error_message(),
|
||||
'error_data' => $this->results->get_error_data(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set `results` property to null, if it is not yet set
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function maybe_set_results_as_null() {
|
||||
// if nothing was returned for some reason, set as result null.
|
||||
if (empty($this->results)) {
|
||||
$this->results = array(
|
||||
'result' => null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets `results` property with json encode error
|
||||
*
|
||||
* @param int $json_last_error
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function set_error_response_on_json_encode_error($json_last_error) {
|
||||
$this->results = array(
|
||||
'result' => false,
|
||||
'error_code' => $json_last_error,
|
||||
'error_message' => 'json_encode error : ' . $json_last_error,
|
||||
'error_data' => '',
|
||||
);
|
||||
|
||||
$this->results = wp_json_encode($this->results);
|
||||
}
|
||||
|
||||
/**
|
||||
* Json encode the `results` property value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function json_encode_results() {
|
||||
$this->results = wp_json_encode($this->results);
|
||||
}
|
||||
}
|
||||
|
||||
endif;
|
||||
Executable
+159
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
if (!defined('ABSPATH')) die('No direct access allowed');
|
||||
|
||||
require_once(AIO_WP_SECURITY_PATH.'/classes/wp-security-audit-events.php');
|
||||
require_once(AIO_WP_SECURITY_PATH.'/classes/wp-security-audit-text-handler.php');
|
||||
|
||||
class AIOWPSecurity_Audit_Event_Handler {
|
||||
|
||||
protected static $_instance = null;
|
||||
|
||||
/**
|
||||
* This method will create and return the only instance of this class.
|
||||
*
|
||||
* @return AIOWPSecurity_Audit_Event_Handler Returns an instance of the class
|
||||
*/
|
||||
public static function instance() {
|
||||
if (empty(self::$_instance)) {
|
||||
self::$_instance = new self();
|
||||
}
|
||||
return self::$_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for the class.
|
||||
*/
|
||||
private function __construct() {
|
||||
add_action('aiowps_record_event', array($this, 'record_event'), 10, 4);
|
||||
add_action('aiowps_bulk_record_events', function($events) {
|
||||
$this->add_bulk_events($events);
|
||||
}, 10, 4);
|
||||
add_action('aiowps_clean_old_events', array($this, 'delete_old_events'), 10);
|
||||
|
||||
if (!wp_next_scheduled('aiowps_clean_old_events')) {
|
||||
wp_schedule_event(time(), 'daily', 'aiowps_clean_old_events');
|
||||
}
|
||||
|
||||
AIOWPSecurity_Audit_Events::add_event_actions();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function records an event in the audit log
|
||||
*
|
||||
* @global AIO_WP_Security $aio_wp_security
|
||||
*
|
||||
* @param string $event_type - the event type
|
||||
* @param array $details - details about the event
|
||||
* @param string $event_level - the event level
|
||||
* @param string $username - the username, this is only used if there is no user logged in
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function record_event($event_type, $details, $event_level = 'info', $username = '') {
|
||||
|
||||
if (!function_exists('wp_get_current_user')) {
|
||||
global $aio_wp_security;
|
||||
|
||||
$aio_wp_security->debug_logger->log_debug("AIOWPSecurity_Audit_Event_Handler::record_event() called before plugins_loaded hook has run.", 4);
|
||||
return;
|
||||
}
|
||||
|
||||
$record_event = apply_filters('aios_audit_log_record_event', true, $event_type, $details, $event_level, $username);
|
||||
|
||||
if (!$record_event) return;
|
||||
|
||||
$user = wp_get_current_user();
|
||||
$username = (is_a($user, 'WP_User') && 0 !== $user->ID) ? $user->user_login : $username;
|
||||
$ip = apply_filters('aios_audit_log_event_user_ip', AIOWPSecurity_Utility_IP::get_user_ip_address());
|
||||
$data = apply_filters('aios_audit_log_event_user_country_code', array('ip' => $ip));
|
||||
$country_code = isset($data['country_code']) ? $data['country_code'] : '';
|
||||
$stacktrace = maybe_serialize(AIOWPSecurity_Utility::normalise_call_stack_args(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS))); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace -- Required for the stacktrace to work.
|
||||
$network_id = get_current_network_id();
|
||||
$site_id = get_current_blog_id();
|
||||
$details = wp_json_encode($details, true);
|
||||
|
||||
$this->add_new_event($network_id, $site_id, $username, $ip, $event_level, $event_type, $details, $stacktrace, $country_code);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function adds the event to the audit log database table
|
||||
*
|
||||
* @param integer $network_id - the id of the current network
|
||||
* @param integer $site_id - the id of the current site
|
||||
* @param string $username - the username of the user who triggered the event
|
||||
* @param string $ip - the IP address of the user
|
||||
* @param string $event_level - the event level
|
||||
* @param string $event_type - the event type
|
||||
* @param string $details - details about the event
|
||||
* @param string $stacktrace - the event stacktrace
|
||||
* @param string $country_code - the country code
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function add_new_event($network_id, $site_id, $username, $ip, $event_level, $event_type, $details, $stacktrace, $country_code) {
|
||||
global $wpdb;
|
||||
|
||||
$wpdb->query($wpdb->prepare("INSERT INTO ".AIOWPSEC_TBL_AUDIT_LOG." (network_id, site_id, username, ip, level, event_type, details, stacktrace, created, country_code) VALUES (%d, %d, %s, %s, %s, %s, %s, %s, UNIX_TIMESTAMP(), %s)", $network_id, $site_id, $username, $ip, $event_level, $event_type, $details, $stacktrace, $country_code)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared -- We can't use %i because our plugin supports wordpress < 6.2.
|
||||
}
|
||||
|
||||
/**
|
||||
* This function adds multiple events to the audit log database table
|
||||
*
|
||||
* @param array $events - each event in the array must contain the keys (network_id, site_id, username, ip, level, event_type, details, stacktrace and created)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function add_bulk_events($events) {
|
||||
global $wpdb;
|
||||
|
||||
$sql = "INSERT INTO ".AIOWPSEC_TBL_AUDIT_LOG." (network_id, site_id, username, ip, level, event_type, details, stacktrace, created, country_code) VALUES ";
|
||||
$values = array();
|
||||
|
||||
foreach ($events as $event) {
|
||||
$sql .= "(%d, %d, %s, %s, %s, %s, %s, %s, %d, %s),";
|
||||
|
||||
$record_event = apply_filters('aios_audit_log_record_event', true, $event['event_type'], $event['details'], $event['level'], $event['username']);
|
||||
if (!$record_event) continue;
|
||||
|
||||
$event['ip'] = apply_filters('aios_audit_log_event_user_ip', $event['ip']);
|
||||
$data = apply_filters('aios_audit_log_event_user_country_code', array('ip' => $event['ip']));
|
||||
$event['country_code'] = isset($data['country_code']) ? $data['country_code'] : '';
|
||||
$values[] = $event['network_id'];
|
||||
$values[] = $event['site_id'];
|
||||
$values[] = $event['username'];
|
||||
$values[] = $event['ip'];
|
||||
$values[] = $event['level'];
|
||||
$values[] = $event['event_type'];
|
||||
$values[] = $event['details'];
|
||||
$values[] = $event['stacktrace'];
|
||||
$values[] = $event['created'];
|
||||
$values[] = $event['country_code'];
|
||||
}
|
||||
|
||||
// remove last ',' character from query
|
||||
$sql = rtrim($sql, ',');
|
||||
|
||||
$wpdb->query($wpdb->prepare($sql, $values)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared -- The sql query is being dynamically built.
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will try to delete all audit logs older than 3 months from the database.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete_old_events() {
|
||||
global $wpdb;
|
||||
|
||||
$after_days = 90;
|
||||
|
||||
if (defined('AIOWPSEC_PURGE_AUDIT_LOGS_AFTER_DAYS')) {
|
||||
$after_days = abs(AIOWPSEC_PURGE_AUDIT_LOGS_AFTER_DAYS);
|
||||
}
|
||||
|
||||
$after_days = empty($after_days) ? 90 : $after_days;
|
||||
$older_than_date = strtotime("-{$after_days} days", time());
|
||||
|
||||
$wpdb->query($wpdb->prepare("DELETE FROM ".AIOWPSEC_TBL_AUDIT_LOG." WHERE created < %s", $older_than_date)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared -- We can't use %i because our plugin supports wordpress < 6.2.
|
||||
}
|
||||
}
|
||||
Executable
+641
@@ -0,0 +1,641 @@
|
||||
<?php
|
||||
|
||||
if (!defined('ABSPATH')) die('No direct access allowed');
|
||||
|
||||
class AIOWPSecurity_Audit_Events {
|
||||
|
||||
public static $log_levels = array(
|
||||
'info',
|
||||
'warning',
|
||||
'fatal',
|
||||
'error',
|
||||
'debug',
|
||||
'trace',
|
||||
);
|
||||
|
||||
public static $event_types = array();
|
||||
|
||||
private static $installed_plugin_info = array();
|
||||
|
||||
private static $removed_plugin_info = array();
|
||||
|
||||
private static $installed_theme_info = array();
|
||||
|
||||
private static $removed_theme_info = array();
|
||||
|
||||
/**
|
||||
* This function adds all the event actions we want to capture and record in the audit log
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function add_event_actions() {
|
||||
// Setup event types to display filter dropdown for audit logs list
|
||||
add_action('init', 'AIOWPSecurity_Audit_Events::setup_event_types');
|
||||
|
||||
// Core events
|
||||
add_action('_core_updated_successfully', 'AIOWPSecurity_Audit_Events::core_updated', 10, 2);
|
||||
|
||||
// Plugin events
|
||||
add_action('upgrader_process_complete', 'AIOWPSecurity_Audit_Events::plugin_installed', 10, 2);
|
||||
add_action('activated_plugin', 'AIOWPSecurity_Audit_Events::plugin_activated', 10, 2);
|
||||
add_action('upgrader_process_complete', 'AIOWPSecurity_Audit_Events::plugin_updated', 10, 2);
|
||||
add_action('deactivated_plugin', 'AIOWPSecurity_Audit_Events::plugin_deactivated', 10, 2);
|
||||
add_action('delete_plugin', 'AIOWPSecurity_Audit_Events::plugin_delete', 10, 2);
|
||||
add_action('deleted_plugin', 'AIOWPSecurity_Audit_Events::plugin_deleted', 10, 2);
|
||||
|
||||
// Theme events
|
||||
add_action('upgrader_process_complete', 'AIOWPSecurity_Audit_Events::theme_installed', 10, 2);
|
||||
add_action('switch_theme', 'AIOWPSecurity_Audit_Events::theme_activated', 10, 1);
|
||||
add_action('upgrader_process_complete', 'AIOWPSecurity_Audit_Events::theme_updated', 10, 2);
|
||||
add_action('delete_theme', 'AIOWPSecurity_Audit_Events::theme_delete', 10, 2);
|
||||
add_action('deleted_theme', 'AIOWPSecurity_Audit_Events::theme_deleted', 10, 2);
|
||||
|
||||
// Translation events
|
||||
add_action('upgrader_process_complete', 'AIOWPSecurity_Audit_Events::translation_updated', 10, 2);
|
||||
|
||||
// User events
|
||||
add_action('password_reset', 'AIOWPSecurity_Audit_Events::password_reset', 10, 1);
|
||||
add_action('deleted_user', 'AIOWPSecurity_Audit_Events::user_deleted', 10, 3);
|
||||
add_action('remove_user_from_blog', 'AIOWPSecurity_Audit_Events::user_removed_from_blog', 10, 3);
|
||||
|
||||
// Rule events
|
||||
add_action('plugins_loaded', 'AIOWPSecurity_Audit_Events::rule_event', 10, 2);
|
||||
|
||||
// Attach an URL to the details to show as a link for configuring rules
|
||||
add_filter('aios_audit_filter_details', function($details, $event_type) {
|
||||
|
||||
// Ensure we only process rules from the firewall
|
||||
if (!preg_match('/^rule_/', $event_type)) return $details;
|
||||
|
||||
$key = "{$details['firewall_event']['rule_name']}::{$details['firewall_event']['rule_family']}";
|
||||
|
||||
// Get the URL for the corresponding rule
|
||||
$location = AIOS_Helper::get_firewall_rule_location($key);
|
||||
$can_show_configure = !empty($location);
|
||||
|
||||
// Only the super admin on the main site can configure the firewall, so only show the configure link to them
|
||||
if (is_multisite()) $can_show_configure = $can_show_configure && is_main_site() && is_super_admin();
|
||||
|
||||
if ($can_show_configure) $details['firewall_event']['location'] = admin_url("admin.php?{$location}");
|
||||
|
||||
return $details;
|
||||
}, 10, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function removes event actions that need to be removed when we are removing the plugin
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function remove_event_actions() {
|
||||
remove_action('delete_plugin', 'AIOWPSecurity_Audit_Events::plugin_delete');
|
||||
remove_action('deleted_plugin', 'AIOWPSecurity_Audit_Events::plugin_deleted');
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the event_types array
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function setup_event_types() {
|
||||
self::$event_types = array(
|
||||
'core_updated' => __('Core updated', 'all-in-one-wp-security-and-firewall'),
|
||||
'plugin_installed' => __('Plugin installed', 'all-in-one-wp-security-and-firewall'),
|
||||
'plugin_activated' => __('Plugin activated', 'all-in-one-wp-security-and-firewall'),
|
||||
'plugin_updated' => __('Plugin updated', 'all-in-one-wp-security-and-firewall'),
|
||||
'plugin_deactivated' => __('Plugin deactivated', 'all-in-one-wp-security-and-firewall'),
|
||||
'plugin_deleted' => __('Plugin deleted', 'all-in-one-wp-security-and-firewall'),
|
||||
'theme_installed' => __('Theme installed', 'all-in-one-wp-security-and-firewall'),
|
||||
'theme_activated' => __('Theme activated', 'all-in-one-wp-security-and-firewall'),
|
||||
'theme_updated' => __('Theme updated', 'all-in-one-wp-security-and-firewall'),
|
||||
'theme_deleted' => __('Theme deleted', 'all-in-one-wp-security-and-firewall'),
|
||||
'translation_updated' => __('Translation updated', 'all-in-one-wp-security-and-firewall'),
|
||||
'entity_changed' => __('Entity changed', 'all-in-one-wp-security-and-firewall'),
|
||||
'successful_login' => __('Successful login', 'all-in-one-wp-security-and-firewall'),
|
||||
'successful_logout' => __('Successful logout', 'all-in-one-wp-security-and-firewall'),
|
||||
'failed_login' => __('Failed login', 'all-in-one-wp-security-and-firewall'),
|
||||
'user_registration' => __('User registration', 'all-in-one-wp-security-and-firewall'),
|
||||
'user_deleted' => __('User deleted', 'all-in-one-wp-security-and-firewall'),
|
||||
'user_removed' => __('User removed from blog', 'all-in-one-wp-security-and-firewall'),
|
||||
'table_migration' => __('Table migration', 'all-in-one-wp-security-and-firewall'),
|
||||
'rule_triggered' => __('Rule triggered', 'all-in-one-wp-security-and-firewall'),
|
||||
'rule_not_triggered' => __('Rule not triggered', 'all-in-one-wp-security-and-firewall'),
|
||||
'rule_active' => __('Rule active', 'all-in-one-wp-security-and-firewall'),
|
||||
'rule_not_active' => __('Rule not active', 'all-in-one-wp-security-and-firewall'),
|
||||
'password_reset' => __('Password reset', 'all-in-one-wp-security-and-firewall'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a core updated event to the audit log
|
||||
*
|
||||
* @param string $new_version - the wp version we updated to
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function core_updated($new_version) {
|
||||
global $wp_version;
|
||||
|
||||
$details = array(
|
||||
'core_updated' => array(
|
||||
'old_version' => $wp_version,
|
||||
'new_version' => $new_version
|
||||
)
|
||||
);
|
||||
do_action('aiowps_record_event', 'core_updated', $details, 'info');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a plugin installed event to the audit log
|
||||
*
|
||||
* @param WP_Upgrader $upgrader - WP_Upgrader instance
|
||||
* @param array $hook_extra - Array of bulk item update data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function plugin_installed($upgrader, $hook_extra) {
|
||||
// If this is empty then we have no way to know if this is a plugin/theme install/update so create an entity changed event
|
||||
if (empty($hook_extra)) {
|
||||
self::event_entity_changed($upgrader);
|
||||
return;
|
||||
}
|
||||
if ('plugin' !== $hook_extra['type'] || 'install' !== $hook_extra['action']) return;
|
||||
self::$installed_plugin_info = $upgrader->new_plugin_data;
|
||||
self::event_plugin_changed('installed', '', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a plugin activated event to the audit log
|
||||
*
|
||||
* @param string $plugin - Path to the plugin file relative to the plugins directory
|
||||
* @param boolean $network_activation - Whether to enable the plugin for all sites in the network or just the current site
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function plugin_activated($plugin, $network_activation) {
|
||||
$network = $network_activation ? 'network' : '';
|
||||
self::event_plugin_changed('activated', $plugin, $network);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a plugin updated event to the audit log
|
||||
*
|
||||
* @param WP_Upgrader $upgrader - WP_Upgrader instance
|
||||
* @param array $hook_extra - Array of bulk item update data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function plugin_updated($upgrader, $hook_extra) {
|
||||
// If this is empty then we have no way to know if this is a plugin/theme install/update so return as we catch this in plugin_installed()
|
||||
if (empty($hook_extra)) return;
|
||||
if ('plugin' !== $hook_extra['type'] || 'update' !== $hook_extra['action']) return;
|
||||
if (isset($hook_extra['plugin'])) {
|
||||
$plugin = $hook_extra['plugin'];
|
||||
self::event_plugin_changed('updated', $plugin, '');
|
||||
} elseif (isset($hook_extra['plugins'])) {
|
||||
foreach ($hook_extra['plugins'] as $plugin) {
|
||||
self::event_plugin_changed('updated', $plugin, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a plugin deactivated event to the audit log
|
||||
*
|
||||
* @param string $plugin - Path to the plugin file relative to the plugins directory
|
||||
* @param boolean $network_deactivation - Whether to disable the plugin for all sites in the network or just the current site
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function plugin_deactivated($plugin, $network_deactivation) {
|
||||
$network = $network_deactivation ? 'network' : '';
|
||||
self::event_plugin_changed('deactivated', $plugin, $network, 'warning');
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the plugin info of the plugin that is about to be deleted
|
||||
*
|
||||
* @param string $plugin - Path to the plugin file relative to the plugins directory
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function plugin_delete($plugin) {
|
||||
$filename = WP_PLUGIN_DIR . '/' . $plugin;
|
||||
if (!file_exists($filename)) return;
|
||||
|
||||
self::$removed_plugin_info = get_plugin_data($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a plugin deleted event to the audit log
|
||||
*
|
||||
* @param string $plugin - Path to the plugin file relative to the plugins directory
|
||||
* @param boolean $deleted - Whether the plugin deletion was successful
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function plugin_deleted($plugin, $deleted) {
|
||||
if ($deleted) self::event_plugin_changed('deleted', $plugin, '', 'warning');
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will construct the event details and send it to be recorded
|
||||
*
|
||||
* @param string $action - the action taken (activated, deactivated)
|
||||
* @param string $plugin - Path to the plugin file relative to the plugins directory
|
||||
* @param string $network - status of if the plugin was network activated/deactivated
|
||||
* @param string $level - the log level
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function event_plugin_changed($action, $plugin, $network, $level = 'info') {
|
||||
if ('deleted' == $action) {
|
||||
$info = self::$removed_plugin_info;
|
||||
} elseif ('installed' == $action) {
|
||||
$info = self::$installed_plugin_info;
|
||||
} else {
|
||||
$filename = WP_PLUGIN_DIR . '/' . $plugin;
|
||||
if (!file_exists($filename)) return;
|
||||
$info = get_plugin_data($filename);
|
||||
}
|
||||
|
||||
$name = empty($info['Name']) ? 'Unknown' : $info['Name'];
|
||||
$version = empty($info['Version']) ? '0.0.0' : $info['Version'];
|
||||
|
||||
$details = array(
|
||||
'plugin' => array(
|
||||
'name' => $name,
|
||||
'version' => $version,
|
||||
'action' => $action,
|
||||
'network' => $network
|
||||
)
|
||||
);
|
||||
do_action('aiowps_record_event', 'plugin_' . $action, $details, $level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a theme installed event to the audit log
|
||||
*
|
||||
* @param WP_Upgrader $upgrader - WP_Upgrader instance
|
||||
* @param array $hook_extra - Array of bulk item update data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function theme_installed($upgrader, $hook_extra) {
|
||||
// If this is empty then we have no way to know if this is a plugin/theme install/update so return as we catch this in plugin_installed()
|
||||
if (empty($hook_extra)) return;
|
||||
if ('theme' !== $hook_extra['type'] || 'install' !== $hook_extra['action']) return;
|
||||
self::$installed_theme_info = $upgrader->new_theme_data;
|
||||
self::event_theme_changed('installed', '', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a theme activated event to the audit log
|
||||
*
|
||||
* @param string $new_name - Name of the new active theme
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function theme_activated($new_name) {
|
||||
$details = array(
|
||||
'theme' => array(
|
||||
'name' => $new_name,
|
||||
'action' => 'activated',
|
||||
)
|
||||
);
|
||||
do_action('aiowps_record_event', 'theme_activated', $details);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a theme updated event to the audit log
|
||||
*
|
||||
* @param WP_Upgrader $upgrader - WP_Upgrader instance
|
||||
* @param array $hook_extra - Array of bulk item update data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function theme_updated($upgrader, $hook_extra) {
|
||||
// If this is empty then we have no way to know if this is a plugin/theme install/update so return as we catch this in plugin_installed()
|
||||
if (empty($hook_extra)) return;
|
||||
if ('theme' !== $hook_extra['type'] || 'update' !== $hook_extra['action']) return;
|
||||
if (isset($hook_extra['theme'])) {
|
||||
$theme = $hook_extra['theme'];
|
||||
self::event_theme_changed('updated', $theme, '');
|
||||
} elseif (isset($hook_extra['themes'])) {
|
||||
foreach ($hook_extra['themes'] as $theme) {
|
||||
self::event_theme_changed('updated', $theme, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the theme info of the plugin that is about to be deleted
|
||||
*
|
||||
* @param string $theme - Path to the theme file relative to the themes directory
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function theme_delete($theme) {
|
||||
$info_object = wp_get_theme($theme);
|
||||
$info = array(
|
||||
'Name' => $info_object->get('Name'),
|
||||
'Version' => $info_object->get('Version'),
|
||||
);
|
||||
self::$removed_theme_info = $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a theme deleted event to the audit log
|
||||
*
|
||||
* @param string $theme - Path to the theme file relative to the themes directory
|
||||
* @param boolean $deleted - Whether the theme deletion was successful
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function theme_deleted($theme, $deleted) {
|
||||
if ($deleted) self::event_theme_changed('deleted', $theme, '', 'warning');
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will construct the event details and send it to be recorded
|
||||
*
|
||||
* @param string $action - the action taken (activated, deactivated)
|
||||
* @param string $theme - Path to the theme file relative to the themes directory
|
||||
* @param string $network - status of if the theme was network activated/deactivated
|
||||
* @param string $level - the log level
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function event_theme_changed($action, $theme, $network, $level = 'info') {
|
||||
if ('deleted' == $action) {
|
||||
$info = self::$removed_theme_info;
|
||||
} elseif ('installed' == $action) {
|
||||
$info = self::$installed_theme_info;
|
||||
} else {
|
||||
$info_object = wp_get_theme($theme);
|
||||
$info = array(
|
||||
'Name' => $info_object->get('Name'),
|
||||
'Version' => $info_object->get('Version'),
|
||||
);
|
||||
}
|
||||
|
||||
$name = empty($info['Name']) ? 'Unknown' : $info['Name'];
|
||||
$version = empty($info['Version']) ? '0.0.0' : $info['Version'];
|
||||
|
||||
$details = array(
|
||||
'theme' => array(
|
||||
'name' => $name,
|
||||
'version' => $version,
|
||||
'action' => $action,
|
||||
'network' => $network
|
||||
)
|
||||
);
|
||||
do_action('aiowps_record_event', 'theme_' . $action, $details, $level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a translation updated event to the audit log
|
||||
*
|
||||
* @param WP_Upgrader $upgrader - WP_Upgrader instance
|
||||
* @param array $hook_extra - Array of bulk item update data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function translation_updated($upgrader, $hook_extra) {
|
||||
|
||||
// If this is empty then we have no way to know if this is a plugin/theme/translation install/update so return as we catch this in plugin_installed()
|
||||
if (empty($hook_extra)) return;
|
||||
|
||||
if ('translation' !== $hook_extra['type'] || 'update' !== $hook_extra['action']) return;
|
||||
|
||||
if (!isset($hook_extra['translations']) || empty($hook_extra['translations'])) return;
|
||||
|
||||
foreach ($hook_extra['translations'] as $info) {
|
||||
$details = array(
|
||||
'translation_updated' => $info
|
||||
);
|
||||
do_action('aiowps_record_event', 'translation_updated', $details, 'info');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a entity changed event to the audit log
|
||||
*
|
||||
* @param WP_Upgrader $upgrader - WP_Upgrader instance
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function event_entity_changed($upgrader) {
|
||||
|
||||
$entity = (isset($upgrader->result) && isset($upgrader->result['destination_name'])) ? $upgrader->result['destination_name'] : false;
|
||||
|
||||
$details = array(
|
||||
'entity_changed' => array(
|
||||
'entity' => $entity,
|
||||
)
|
||||
);
|
||||
do_action('aiowps_record_event', 'entity_changed', $details, 'warning');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all the firewall rule events to the audit log
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function rule_event() {
|
||||
$aiowps_firewall_message_store = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::MESSAGE_STORE);
|
||||
$events = array();
|
||||
foreach (array('active', 'not_active', 'triggered', 'not_triggered') as $event) {
|
||||
$data = $aiowps_firewall_message_store->get('rule_'.$event);
|
||||
|
||||
if (empty($data)) continue;
|
||||
|
||||
foreach ($data as $rule) {
|
||||
|
||||
$details = array(
|
||||
'firewall_event' => array(
|
||||
'event' => $event,
|
||||
'rule_name' => $rule['name'],
|
||||
'rule_family' => $rule['family'],
|
||||
)
|
||||
);
|
||||
|
||||
$blog_id = AIOWPSecurity_Utility::get_blog_id_from_request($rule['request']);
|
||||
|
||||
$rule['request'] = apply_filters('aios_audit_filter_request', $rule['request'], $event);
|
||||
|
||||
$events[] = array(
|
||||
'network_id' => get_current_network_id(),
|
||||
'site_id' => $blog_id,
|
||||
'username' => (isset($rule['potential_user']) ? AIOWPSecurity_Utility::verify_username($rule['potential_user']) : false),
|
||||
'ip' => $rule['ip'],
|
||||
'level' => 'triggered' === $event ? 'warning' : 'info',
|
||||
'event_type' => 'rule_'.$event,
|
||||
'details' => wp_json_encode($details, true),
|
||||
'stacktrace' => (isset($rule['request']) ? print_r($rule['request'], true) : ''), // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- PCP warning. Part of AOIS error reporting system.
|
||||
'created' => $rule['time']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($events)) return;
|
||||
|
||||
do_action('aiowps_bulk_record_events', $events);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a password reset event to the audit log
|
||||
*
|
||||
* @param object $user_data - Object containing user's data
|
||||
*/
|
||||
public static function password_reset($user_data) {
|
||||
|
||||
$user_login = (false === $user_data) ? 'unknown' : $user_data->user_login;
|
||||
|
||||
$details = array(
|
||||
'password_reset' => array(
|
||||
'user_login' => $user_login
|
||||
)
|
||||
);
|
||||
do_action('aiowps_record_event', 'password_reset', $details, 'warning');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a user deleted event to the audit log
|
||||
*
|
||||
* @param int $user_id - the id of the deleted user
|
||||
* @param int|null $reassign - the id of the user to reassign data to or null
|
||||
* @param object $user_data - Object containing user's data
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function user_deleted($user_id, $reassign, $user_data) {
|
||||
|
||||
$user_login = (false === $user_data) ? 'unknown' : $user_data->user_login;
|
||||
|
||||
$details = array(
|
||||
'user_deleted' => array(
|
||||
'user_id' => $user_id,
|
||||
'reassign' => $reassign,
|
||||
'user_login' => $user_login
|
||||
|
||||
)
|
||||
);
|
||||
do_action('aiowps_record_event', 'user_deleted', $details, 'warning');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a user removed event to the audit log
|
||||
*
|
||||
* @param int $user_id - the id of the removed user
|
||||
* @param int $blog_id - the id of the blog the user was removed from
|
||||
* @param int $reassign - the id of the user to reassign data to or null
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function user_removed_from_blog($user_id, $blog_id, $reassign) {
|
||||
$user_data = get_user_by('ID', $user_id);
|
||||
$user_login = is_a($user_data, 'WP_User') && 0 !== $user_data->ID ? $user_data->user_login : 'unknown';
|
||||
|
||||
$details = array(
|
||||
'user_removed' => array(
|
||||
'user_id' => $user_id,
|
||||
'blog_id' => $blog_id,
|
||||
'reassign' => $reassign,
|
||||
'user_login' => $user_login
|
||||
|
||||
)
|
||||
);
|
||||
do_action('aiowps_record_event', 'user_removed', $details, 'warning');
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a failed login event to the audit log
|
||||
*
|
||||
* @param string $username - the username for the failed login attempt
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function event_failed_login($username) {
|
||||
$user = is_email($username) ? get_user_by('email', $username) : get_user_by('login', $username);
|
||||
$details = array(
|
||||
'failed_login' => array(
|
||||
'imported' => false,
|
||||
'username' => $username,
|
||||
'known' => true,
|
||||
)
|
||||
);
|
||||
if (is_a($user, 'WP_User')) {
|
||||
do_action('aiowps_record_event', 'failed_login', $details, 'warning', $username);
|
||||
} else {
|
||||
$details['failed_login']['known'] = false;
|
||||
do_action('aiowps_record_event', 'failed_login', $details, 'warning', $username);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a user registration event to the audit log
|
||||
*
|
||||
* @param integer $user_id - the user ID of the newly registered user
|
||||
* @param string $type - the type of registration valid values (admin, pending, registered)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function event_user_registration($user_id, $type) {
|
||||
$registered_user = get_user_by('ID', $user_id);
|
||||
$registered_username = is_a($registered_user, 'WP_User') && 0 !== $registered_user->ID ? $registered_user->user_login : '';
|
||||
|
||||
$details = array(
|
||||
'user_registration' => array(
|
||||
'registered_username' => $registered_username,
|
||||
'type' => $type,
|
||||
)
|
||||
);
|
||||
|
||||
if ('admin' == $type) {
|
||||
$admin_user = wp_get_current_user();
|
||||
$admin_username = is_a($admin_user, 'WP_User') ? $admin_user->user_login : '';
|
||||
$details['user_registration']['admin_username'] = $admin_username;
|
||||
do_action('aiowps_record_event', 'user_registration', $details, 'info');
|
||||
} elseif ('pending' == $type) {
|
||||
do_action('aiowps_record_event', 'user_registration', $details, 'info', $registered_username);
|
||||
} elseif ('registered' == $type) {
|
||||
do_action('aiowps_record_event', 'user_registration', $details, 'info', $registered_username);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a successful login event to the audit log
|
||||
*
|
||||
* @param string $username - the username for the successful login
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function event_successful_login($username) {
|
||||
$details = array(
|
||||
'successful_login' => array(
|
||||
'username' => $username,
|
||||
)
|
||||
);
|
||||
do_action('aiowps_record_event', 'successful_login', $details, 'info', $username);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a successful logout event to the audit log
|
||||
*
|
||||
* @param string $username - the username for the successful logout
|
||||
* @param boolean $force_logout - if the logout was a force logout
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function event_successful_logout($username, $force_logout = false) {
|
||||
$details = array(
|
||||
'successful_logout' => array(
|
||||
'username' => $username,
|
||||
'force_logout' => $force_logout ? __('(force logout)', 'all-in-one-wp-security-and-firewall') : ''
|
||||
)
|
||||
);
|
||||
do_action('aiowps_record_event', 'successful_logout', $details, 'info', $username);
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+238
@@ -0,0 +1,238 @@
|
||||
<?php
|
||||
|
||||
if (!defined('ABSPATH')) die('No direct access allowed');
|
||||
|
||||
/**
|
||||
* Handles converting the audit log's details column to a text representation
|
||||
*/
|
||||
class AIOWPSecurity_Audit_Text_Handler {
|
||||
|
||||
/**
|
||||
* Return the text version of 'successful_login' event
|
||||
*
|
||||
* @param array $info
|
||||
* @return string
|
||||
*/
|
||||
public static function successful_login_to_text($info) {
|
||||
/* translators: %s: User name */
|
||||
return sprintf(__('Successful login with username: %s', 'all-in-one-wp-security-and-firewall'), $info['username']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text version of 'successful_logout' event
|
||||
*
|
||||
* @param array $info - contains info used to generate the returned string
|
||||
*
|
||||
* @return string - the text to be shown for details on audit log table
|
||||
*/
|
||||
public static function successful_logout_to_text($info) {
|
||||
return __('Successful logout with username:', 'all-in-one-wp-security-and-firewall') . ' ' . $info['username'] . ' ' . $info['force_logout'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text version of 'core_updated' event
|
||||
*
|
||||
* @param array $info
|
||||
* @return string
|
||||
*/
|
||||
public static function core_updated_to_text($info) {
|
||||
/* translators: 1: Old version, 2: New version */
|
||||
return sprintf(__('WordPress updated from version %1$s to %2$s', 'all-in-one-wp-security-and-firewall'), $info['old_version'], $info['new_version']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text version of 'plugin' event
|
||||
*
|
||||
* @param array $info
|
||||
* @return string
|
||||
*/
|
||||
public static function plugin_to_text($info) {
|
||||
return sprintf(__('Plugin', 'all-in-one-wp-security-and-firewall').': %s %s %s (v%s)', $info['name'], $info['network'], $info['action'], $info['version']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text version of 'theme' event
|
||||
*
|
||||
* @param array $info
|
||||
* @return string
|
||||
*/
|
||||
public static function theme_to_text($info) {
|
||||
if ('activated' == $info['action']) {
|
||||
return sprintf(__('Theme', 'all-in-one-wp-security-and-firewall').': %s %s', $info['name'], $info['action']);
|
||||
} else {
|
||||
return sprintf(__('Theme', 'all-in-one-wp-security-and-firewall').': %s %s %s (v%s)', $info['name'], $info['network'], $info['action'], $info['version']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text version of 'entity_changed' event
|
||||
*
|
||||
* @param array $info
|
||||
* @return string
|
||||
*/
|
||||
public static function entity_changed_to_text($info) {
|
||||
if ($info['entity']) {
|
||||
/* translators: %s: Entity name */
|
||||
return sprintf(__('Entity: "%s" has changed, please check the stacktrace for more details', 'all-in-one-wp-security-and-firewall'), $info['entity']);
|
||||
} else {
|
||||
return __('An unknown entity has changed, please check the stacktrace for more details', 'all-in-one-wp-security-and-firewall');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text version of 'translation_updated' event
|
||||
*
|
||||
* @param array $info
|
||||
* @return string
|
||||
*/
|
||||
public static function translation_updated_to_text($info) {
|
||||
if ('core' == $info['type']) {
|
||||
/* translators: 1: Language name, 2: Version */
|
||||
return sprintf(__('Core %1$s translations updated to version %2$s', 'all-in-one-wp-security-and-firewall'), $info['language'], $info['version']);
|
||||
} elseif ('plugin' == $info['type']) {
|
||||
/* translators: 1: Slug, 2: Language, 3: Version */
|
||||
return sprintf(__('Plugin "%1$s" %2$s translations updated to version %3$s', 'all-in-one-wp-security-and-firewall'), $info['slug'], $info['language'], $info['version']);
|
||||
} elseif ('theme' == $info['type']) {
|
||||
/* translators: 1: Slug, 2: Language name, 3: Version */
|
||||
return sprintf(__('Theme "%1$s" %2$s translations updated to version %3$s', 'all-in-one-wp-security-and-firewall'), $info['slug'], $info['language'], $info['version']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text version of 'failed_login' event
|
||||
*
|
||||
* @param array $info
|
||||
* @return string
|
||||
*/
|
||||
public static function failed_login_to_text($info) {
|
||||
if ($info['imported']) {
|
||||
return __('Event imported from the failed logins table', 'all-in-one-wp-security-and-firewall');
|
||||
} elseif ($info['known']) {
|
||||
/* translators: %s: User name */
|
||||
return sprintf(__('Failed login attempt with a known username: %s', 'all-in-one-wp-security-and-firewall'), $info['username']);
|
||||
} else {
|
||||
/* translators: %s: User name */
|
||||
return sprintf(__('Failed login attempt with a unknown username: %s', 'all-in-one-wp-security-and-firewall'), $info['username']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text version of 'user_registration' event
|
||||
*
|
||||
* @param array $info
|
||||
* @return string
|
||||
*/
|
||||
public static function user_registration_to_text($info) {
|
||||
if ('admin' == $info['type']) {
|
||||
/* translators: 1: Admin User name, 2: Registered User name */
|
||||
return sprintf(__('Admin %1$s registered new user: %2$s', 'all-in-one-wp-security-and-firewall'), $info['admin_username'], $info['registered_username']);
|
||||
} elseif ('pending' == $info['type']) {
|
||||
/* translators: %s: Registered User name */
|
||||
return sprintf(__('User %s registered and set to pending', 'all-in-one-wp-security-and-firewall'), $info['registered_username']);
|
||||
} elseif ('registered' == $info['type']) {
|
||||
/* translators: %s: Registered User name */
|
||||
return sprintf(__('User %s registered', 'all-in-one-wp-security-and-firewall'), $info['registered_username']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text version of 'table_migration' event
|
||||
*
|
||||
* @param array $info
|
||||
* @return string
|
||||
*/
|
||||
public static function table_migration_to_text($info) {
|
||||
if ($info['success']) {
|
||||
/* translators: 1: From table, 2: To table */
|
||||
return sprintf(__('Successfully migrated the `%1$s` table data to the `%2$s` table', 'all-in-one-wp-security-and-firewall'), $info['from_table'], $info['to_table']);
|
||||
} else {
|
||||
/* translators: 1: From table, 2: To table */
|
||||
return sprintf(__('Failed to migrate the `%1$s` table data to the `%2$s` table', 'all-in-one-wp-security-and-firewall'), $info['from_table'], $info['to_table']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text version of 'firewall_event' event
|
||||
*
|
||||
* @param array $info
|
||||
* @return string
|
||||
*/
|
||||
public static function firewall_event_to_text($info) {
|
||||
$output = '';
|
||||
switch ($info['event']) {
|
||||
case 'triggered':
|
||||
/* translators: 1: Rule name, 2: Rule family */
|
||||
$output = sprintf(__('"%1$s [%2$s]" rule has been triggered.', 'all-in-one-wp-security-and-firewall'), $info['rule_name'], $info['rule_family']);
|
||||
break;
|
||||
case 'not_triggered':
|
||||
/* translators: 1: Rule name, 2: Rule family */
|
||||
$output = sprintf(__('"%1$s [%2$s]" rule was not triggered.', 'all-in-one-wp-security-and-firewall'), $info['rule_name'], $info['rule_family']);
|
||||
break;
|
||||
case 'active':
|
||||
/* translators: 1: Rule name, 2: Rule family */
|
||||
$output = sprintf(__('"%1$s [%2$s]" rule is active.', 'all-in-one-wp-security-and-firewall'), $info['rule_name'], $info['rule_family']);
|
||||
break;
|
||||
case 'not_active':
|
||||
/* translators: 1: Rule name, 2: Rule family */
|
||||
$output = sprintf(__('"%1$s [%2$s]" rule is not active.', 'all-in-one-wp-security-and-firewall'), $info['rule_name'], $info['rule_family']);
|
||||
break;
|
||||
default:
|
||||
$output = array(
|
||||
'status' => $info['event'],
|
||||
'rule_name' => $info['rule_name'],
|
||||
'rule_family' => $info['rule_family'],
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
// Add a link to the rule, if present
|
||||
if (isset($info['location'])) {
|
||||
$output .= "<br><br><a href='{$info['location']}'target='_blank'>".__('Configure this rule', 'all-in-one-wp-security-and-firewall').'</a>';
|
||||
}
|
||||
|
||||
return is_array($output) ? wp_json_encode($output) : $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text version of 'password_reset' event
|
||||
*
|
||||
* @param array $info
|
||||
* @return string
|
||||
*/
|
||||
public static function password_reset_to_text($info) {
|
||||
/* translators: %s: User login */
|
||||
return sprintf(__('Password for user account: `%s` successfully changed', 'all-in-one-wp-security-and-firewall'), $info['user_login']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text version of 'user_deleted' event
|
||||
*
|
||||
* @param array $info
|
||||
* @return string
|
||||
*/
|
||||
public static function user_deleted_to_text($info) {
|
||||
if (empty($info['reassign'])) {
|
||||
/* translators: 1: User login, 2: User ID */
|
||||
return sprintf(__('User account: %1$s with ID: `%2$s` has been deleted', 'all-in-one-wp-security-and-firewall'), $info['user_login'], $info['user_id']);
|
||||
} else {
|
||||
/* translators: 1: User login, 2: User ID, 3: Reassign */
|
||||
return sprintf(__('User account: `%1$s` with ID: `%2$s` has been deleted and all content has been reassigned to user with ID: `%3$s`', 'all-in-one-wp-security-and-firewall'), $info['user_login'], $info['user_id'], $info['reassign']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the text version of 'user_removed' event
|
||||
*
|
||||
* @param array $info
|
||||
* @return string
|
||||
*/
|
||||
public static function user_removed_to_text($info) {
|
||||
if (empty($info['reassign'])) {
|
||||
/* translators: 1: User login, 2: User ID, 3: Blog ID */
|
||||
return sprintf(__('User account: %1$s with ID: `%2$s` has been removed from the blog with ID: `%3$s`', 'all-in-one-wp-security-and-firewall'), $info['user_login'], $info['user_id'], $info['blog_id']);
|
||||
} else {
|
||||
/* translators: 1: User login, 2: User ID, 3: Blog ID, 4: Reassign */
|
||||
return sprintf(__('User account: `%1$s` with ID: `%2$s` has been removed from the blog with ID: `%3$s` and all content has been reassigned to user with ID: `%4$s`', 'all-in-one-wp-security-and-firewall'), $info['user_login'], $info['user_id'], $info['blog_id'], $info['reassign']);
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
abstract class AIOWPSecurity_Base_Tasks {
|
||||
/**
|
||||
* Runs intended various tasks
|
||||
* Handles single and multi-site (NW activation) cases
|
||||
*
|
||||
* @global type $wpdb
|
||||
*/
|
||||
public static function run() {
|
||||
if (is_multisite()) {
|
||||
global $wpdb;
|
||||
// check if it is a network activation
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery -- PCP warning. Ignore.
|
||||
$blog_ids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
|
||||
foreach ($blog_ids as $blog_id) {
|
||||
switch_to_blog($blog_id);
|
||||
static::run_for_a_site();
|
||||
restore_current_blog();
|
||||
}
|
||||
} else {
|
||||
static::run_for_a_site();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run uninstallation task for a single site.
|
||||
*
|
||||
* This method must be implemented in child classes.
|
||||
* Since static abstract methods are not allowed in PHP, we enforce it at runtime.
|
||||
*
|
||||
* @throws Exception If not overridden in a child class.
|
||||
* @return void
|
||||
*/
|
||||
protected static function run_for_a_site() {
|
||||
throw new Exception(
|
||||
sprintf('%s : Child classes must implement run_for_a_site() method.', get_called_class())
|
||||
);
|
||||
}
|
||||
}
|
||||
Executable
+207
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; //Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Firewall bootstrap file content block.
|
||||
*/
|
||||
class AIOWPSecurity_Block_Bootstrap extends AIOWPSecurity_Block_File {
|
||||
|
||||
/**
|
||||
* Keeps track of our bootstrap file version
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $version = '1.0.2';
|
||||
|
||||
/**
|
||||
* Inserts our code into our bootstrap file.
|
||||
*
|
||||
* @return boolean|WP_Error
|
||||
*/
|
||||
public function insert_contents() {
|
||||
|
||||
$info = pathinfo($this->file_path);
|
||||
|
||||
if (!isset($info['dirname'])) {
|
||||
return new WP_Error(
|
||||
'file_no_directory',
|
||||
'No directory has been set',
|
||||
$this->file_path
|
||||
);
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.WP.AlternativeFunctions -- wp_filesystem not recommended for firewall.
|
||||
if (!is_writable($info['dirname'])) {
|
||||
return new WP_Error(
|
||||
'file_directory_not_writable',
|
||||
'The directory has incorrect write permissions. Please double check its permissions and try again.',
|
||||
$info['dirname']
|
||||
);
|
||||
}
|
||||
|
||||
return (false !== @file_put_contents($this->file_path, $this->get_contents())); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
||||
// phpcs:enable WordPress.WP.AlternativeFunctions -- wp_filesystem not recommended for firewall.
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the bootstrap contents are valid
|
||||
*
|
||||
* @param string $contents
|
||||
* @return boolean
|
||||
*/
|
||||
protected function is_content_valid($contents) {
|
||||
|
||||
//Ensure we're using the correct version of the file
|
||||
$version = $this->get_bootstrap_version();
|
||||
|
||||
if (false === $version) return false;
|
||||
|
||||
if ($version['full_version'] !== $this->version) return false;
|
||||
|
||||
//Ensure the required paths are valid
|
||||
$regexes = array('/file_exists\((?<file_path>\'.*\')\)/isU', '/include_once\((?<file_path>\'.*\')\)/isU');
|
||||
$firewall_path_str = $this->get_firewall_path_str();
|
||||
|
||||
foreach ($regexes as $regex) {
|
||||
|
||||
if (preg_match($regex, $contents, $matches)) {
|
||||
|
||||
if ($firewall_path_str !== $matches['file_path']) return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bootstrap version from file
|
||||
*
|
||||
* @return array|boolean Array with the version information; false otherwise.
|
||||
*/
|
||||
protected function get_bootstrap_version() {
|
||||
|
||||
$contents = @file_get_contents($this->file_path); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
||||
|
||||
if (false === $contents) return false;
|
||||
|
||||
if (preg_match('/@version (?<full_version>(?<major>\d{1,})\.(?<minor>\d{1,})\.(?<patch>\d{1,}))$/m', $contents, $matches)) {
|
||||
return $matches;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the firewall path string that contains "__DIR__" for home dir, if plugin dir isn't a symbolic link..
|
||||
*
|
||||
* @return string The firewall path string.
|
||||
*/
|
||||
private function get_firewall_path_str() {
|
||||
$firewall_path = AIOWPSecurity_Utility_Firewall::get_firewall_path();
|
||||
$firewall_path_str = $this->get_path_str_for_given_absolute_path($firewall_path);
|
||||
return $firewall_path_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get path string to write bootstrap file from given path.
|
||||
*
|
||||
* @param string $path a path that we want to write to the bootstrap file.
|
||||
* @return string The path that can be written in the bootstrap file.
|
||||
*/
|
||||
private function get_path_str_for_given_absolute_path($path) {
|
||||
$home_path = AIOWPSecurity_Utility_File::get_home_path();
|
||||
// If the plugin is symbolic linked, then the plugin's firewall path is not started with home_path.
|
||||
$path_str = (0 === strpos($path, $home_path)) ? "__DIR__.'/".substr($path, strlen($home_path))."'" : "'".$path."'";
|
||||
return $path_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the firewall rules path string that contains "__DIR__" for home dir, if plugin dir isn't a symbolic link.
|
||||
*
|
||||
* @return string The firewall rule path string.
|
||||
*/
|
||||
private function get_firewall_rules_path_str() {
|
||||
$firewall_rules_path = AIOWPSecurity_Utility_Firewall::get_firewall_rules_path();
|
||||
$firewall_rules_path_str = $this->get_path_str_for_given_absolute_path($firewall_rules_path);
|
||||
return $firewall_rules_path_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The regex pattern that demarcates our contents
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_regex_pattern() {
|
||||
return '#// Begin AIOWPSEC Firewall(.*)// End AIOWPSEC Firewall#isU';
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap file contents to insert
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_contents() {
|
||||
$firewall_path_str = $this->get_firewall_path_str();
|
||||
$firewall_rules_path_str = $this->get_firewall_rules_path_str();
|
||||
|
||||
// Any extra data we want to have accessible to the firewall
|
||||
$data = array(
|
||||
'ABSPATH' => wp_normalize_path(ABSPATH)
|
||||
);
|
||||
|
||||
$code = "<?php\n";
|
||||
$code .= $this->get_warning_message();
|
||||
|
||||
$directive = AIOWPSecurity_Utility_Firewall::get_already_set_directive();
|
||||
|
||||
if (!empty($directive) && $directive !== $this->file_path) {
|
||||
$directive = wp_normalize_path($directive);
|
||||
$code .= "// Previously set auto_prepend_file\n";
|
||||
$code .= "if (file_exists('{$directive}')) {\n";
|
||||
$code .= "\tinclude_once('{$directive}');\n";
|
||||
$code .= "}\n";
|
||||
}
|
||||
|
||||
|
||||
$code .= '$GLOBALS[\'aiowps_firewall_rules_path\'] = '.$firewall_rules_path_str.";\n\n";
|
||||
|
||||
// write any other data we need
|
||||
$code .= "\$GLOBALS['aiowps_firewall_data'] = array(\n";
|
||||
foreach ($data as $name => $value) {
|
||||
$code .= "\t'{$name}' => '{$value}',\n";
|
||||
}
|
||||
$code .= ");\n\n"; //close data array
|
||||
|
||||
$code .= "// Begin AIOWPSEC Firewall\n";
|
||||
$code .= "if (file_exists({$firewall_path_str})) {\n";
|
||||
$code .= "\tinclude_once({$firewall_path_str});\n";
|
||||
$code .= "}\n";
|
||||
$code .= "// End AIOWPSEC Firewall\n";
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets our warning message for users
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_warning_message() {
|
||||
|
||||
$warning = "/** \n";
|
||||
$warning .= " * @version {$this->version}\n";
|
||||
$warning .= " * WARNING: Please do not delete this file.\n";
|
||||
$warning .= " * \n";
|
||||
$warning .= " * This will cause PHP to throw a fatal error and render your site unusable.\n";
|
||||
$warning .= " * \n";
|
||||
$warning .= " * To safely delete this file, please check both your .user.ini file and your php.ini file and ensure this file is not set in the auto_prepend_file directive.\n";
|
||||
$warning .= " * \n";
|
||||
$warning .= " * Please ask your web hosting provider if you need guidance with executing the aforementioned steps.\n";
|
||||
$warning .= " */\n";
|
||||
|
||||
return $warning;
|
||||
}
|
||||
}
|
||||
Executable
+202
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; //Exit if accessed directly
|
||||
}
|
||||
|
||||
abstract class AIOWPSecurity_Block_File {
|
||||
|
||||
/**
|
||||
* Full path to the file we're managing
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $file_path;
|
||||
|
||||
/**
|
||||
* Receives the full file path
|
||||
*
|
||||
* @param string $file_path
|
||||
*/
|
||||
public function __construct($file_path) {
|
||||
$this->file_path = $file_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the contents to the managed file
|
||||
*
|
||||
* @return boolean|WP_Error true if success; false if failed
|
||||
*/
|
||||
abstract public function insert_contents();
|
||||
|
||||
/**
|
||||
* Returns the contents to be inserted into the managed file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function get_contents();
|
||||
|
||||
/**
|
||||
* Returns the regex pattern that separates our contents from others the file may contain
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function get_regex_pattern();
|
||||
|
||||
/**
|
||||
* Checks whether the file's contents are valid
|
||||
*
|
||||
* @param string $contents
|
||||
* @return boolean
|
||||
*/
|
||||
abstract protected function is_content_valid($contents);
|
||||
|
||||
/**
|
||||
* Updates the contents of the managed file
|
||||
*
|
||||
* @return boolean|WP_Error true if updated; false if not updated
|
||||
*/
|
||||
public function update_contents() {
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions -- wp_filesystem not recommended.
|
||||
if (!is_readable($this->file_path) || !is_writable($this->file_path)) {
|
||||
return new WP_Error(
|
||||
'file_wrong_permissions',
|
||||
'The file has incorrect read or write permissions. Please double check its permissions and try again.',
|
||||
$this->file_path
|
||||
);
|
||||
}
|
||||
|
||||
$contents = @file_get_contents($this->file_path); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
||||
|
||||
if (false === $contents) {
|
||||
return new WP_Error(
|
||||
'file_unable_to_read',
|
||||
'Unable to read the file. Please double check its permissions and try again.',
|
||||
$this->file_path
|
||||
);
|
||||
}
|
||||
|
||||
$matches = array();
|
||||
$match_count = preg_match_all($this->get_regex_pattern(), $contents, $matches);
|
||||
|
||||
if (empty($matches[1]) || false === $match_count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//checks whether an update is required
|
||||
$requires_update = false;
|
||||
$match = '';
|
||||
foreach ($matches[1] as $match) {
|
||||
|
||||
$requires_update = !$this->is_content_valid($match);
|
||||
|
||||
if (true === $requires_update) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//perform the update
|
||||
if ($requires_update) {
|
||||
|
||||
$block_removed = $this->remove_contents();
|
||||
$block_inserted = $this->insert_contents();
|
||||
|
||||
return (true === $block_removed && true === $block_inserted);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the file contains our contents
|
||||
*
|
||||
* @return boolean|WP_Error true if found; false if not found
|
||||
*/
|
||||
public function contains_contents() {
|
||||
|
||||
clearstatcache();
|
||||
if (!file_exists($this->file_path)) {
|
||||
return new WP_Error(
|
||||
'file_does_not_exist',
|
||||
'The file does not exist.',
|
||||
$this->file_path
|
||||
);
|
||||
}
|
||||
|
||||
if (!is_readable($this->file_path)) {
|
||||
return new WP_Error(
|
||||
'file_wrong_permissions',
|
||||
'The file has incorrect read permissions. Please double check its permissions and try again.',
|
||||
$this->file_path
|
||||
);
|
||||
}
|
||||
|
||||
$contents = @file_get_contents($this->file_path); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
||||
|
||||
if (false === $contents) {
|
||||
return new WP_Error(
|
||||
'file_unable_to_read',
|
||||
'Unable to read the file. Please double check its permissions and try again.',
|
||||
$this->file_path
|
||||
);
|
||||
}
|
||||
|
||||
return (1 === preg_match($this->get_regex_pattern(), $contents));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes our contents from the file
|
||||
*
|
||||
* @return boolean|WP_Error
|
||||
*/
|
||||
public function remove_contents() {
|
||||
|
||||
if (!is_readable($this->file_path) || !is_writable($this->file_path)) {
|
||||
return new WP_Error(
|
||||
'file_wrong_permissions',
|
||||
'The file has incorrect read or write permissions. Please double check its permissions and try again.',
|
||||
$this->file_path
|
||||
);
|
||||
}
|
||||
|
||||
$contents = @file_get_contents($this->file_path); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
||||
|
||||
if (false === $contents) {
|
||||
return new WP_Error(
|
||||
'file_unable_to_read',
|
||||
'Unable to read the file. Please double check its permissions and try again.',
|
||||
$this->file_path
|
||||
);
|
||||
}
|
||||
|
||||
$removed = 0;
|
||||
$contents = preg_replace($this->get_regex_pattern(), "", $contents, -1, $removed);
|
||||
|
||||
if (null === $contents) {
|
||||
return new WP_Error(
|
||||
'file_unable_to_alter',
|
||||
'Unable to alter the file.',
|
||||
$this->file_path
|
||||
);
|
||||
}
|
||||
|
||||
if (false === @file_put_contents($this->file_path, $contents, LOCK_EX)) { // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
||||
return new WP_Error(
|
||||
'file_unable_to_write',
|
||||
'Unable to write to the file. Please double check its permissions and try again.',
|
||||
$this->file_path
|
||||
);
|
||||
}
|
||||
|
||||
return $removed > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* By default returns the full path to the file being managed
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
return $this->file_path;
|
||||
}
|
||||
|
||||
} //end of class
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; //Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Block_Htaccess extends AIOWPSecurity_Block_File {
|
||||
|
||||
/**
|
||||
* Attempts to insert our apache directives into the htaccess file
|
||||
*
|
||||
* @return boolean|WP_Error true if success; false if failed
|
||||
*/
|
||||
public function insert_contents() {
|
||||
|
||||
$home_path = AIOWPSecurity_Utility_File::get_home_path();
|
||||
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions -- wp_filesystem not recommended.
|
||||
if (!is_writable($home_path)) {
|
||||
return new WP_Error(
|
||||
'file_wrong_permissions',
|
||||
'The file has incorrect write permissions. Please double check its permissions and try again.',
|
||||
$this->file_path
|
||||
);
|
||||
}
|
||||
|
||||
return (false !== @file_put_contents($this->file_path, $this->get_contents(), FILE_APPEND)); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the file's contents are valid
|
||||
*
|
||||
* @param string $contents
|
||||
* @return boolean
|
||||
*/
|
||||
protected function is_content_valid($contents) {
|
||||
|
||||
$regex = '/php_value auto_prepend_file \'(.*)\'/isU';
|
||||
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
||||
|
||||
$matches = array();
|
||||
|
||||
if (preg_match_all($regex, $contents, $matches)) {
|
||||
$match = '';
|
||||
foreach ($matches[1] as $match) {
|
||||
|
||||
if ($bootstrap_path !== $match) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The regex pattern that demarcates our contents
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_regex_pattern() {
|
||||
return '/\r?\n?# Begin AIOWPSEC Firewall(.*?)# End AIOWPSEC Firewall/is';
|
||||
}
|
||||
|
||||
/**
|
||||
* Our contents; the required apache directives for auto prepending a file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_contents() {
|
||||
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
||||
|
||||
$directives = "\n# Begin AIOWPSEC Firewall\n";
|
||||
$directives .= "\t<IfModule mod_php5.c>\n";
|
||||
$directives .= "\t\tphp_value auto_prepend_file '{$bootstrap_path}'\n";
|
||||
$directives .= "\t</IfModule>\n";
|
||||
$directives .= "\t<IfModule mod_php7.c>\n";
|
||||
$directives .= "\t\tphp_value auto_prepend_file '{$bootstrap_path}'\n";
|
||||
$directives .= "\t</IfModule>\n";
|
||||
$directives .= "\t<IfModule mod_php.c>\n";
|
||||
$directives .= "\t\tphp_value auto_prepend_file '{$bootstrap_path}'\n";
|
||||
$directives .= "\t</IfModule>\n";
|
||||
$directives .= "# End AIOWPSEC Firewall";
|
||||
|
||||
return $directives;
|
||||
}
|
||||
|
||||
|
||||
} //end of class
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; //Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Block_Litespeed extends AIOWPSecurity_Block_Htaccess {
|
||||
|
||||
/**
|
||||
* Get the directives needed for litespeed server
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_contents() {
|
||||
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
||||
|
||||
$directives = "\n# Begin AIOWPSEC Firewall\n";
|
||||
$directives .= "\t<IfModule LiteSpeed>\n";
|
||||
$directives .= "\t\tphp_value auto_prepend_file '{$bootstrap_path}'\n";
|
||||
$directives .= "\t</IfModule>\n";
|
||||
$directives .= "\t<IfModule lsapi_module>\n";
|
||||
$directives .= "\t\tphp_value auto_prepend_file '{$bootstrap_path}'\n";
|
||||
$directives .= "\t</IfModule>\n";
|
||||
$directives .= "# End AIOWPSEC Firewall";
|
||||
|
||||
return $directives;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Executable
+93
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; //Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Block_Muplugin extends AIOWPSecurity_Block_File {
|
||||
|
||||
/**
|
||||
* Inserts our code into our mu-plugin.
|
||||
*
|
||||
* The mu-plugin and the mu-plugin directory will be created if they don't already exists
|
||||
*
|
||||
* @return boolean|WP_Error
|
||||
*/
|
||||
public function insert_contents() {
|
||||
$info = pathinfo($this->file_path);
|
||||
|
||||
if (!isset($info['dirname'])) {
|
||||
return new WP_Error(
|
||||
'file_no_directory',
|
||||
'No directory has been set',
|
||||
$this->file_path
|
||||
);
|
||||
}
|
||||
|
||||
if (false === wp_mkdir_p($info['dirname'])) {
|
||||
return new WP_Error(
|
||||
'file_no_directory_created',
|
||||
'Unable to create the directory',
|
||||
$info['dirname']
|
||||
);
|
||||
}
|
||||
|
||||
return (false !== @file_put_contents($this->file_path, $this->get_contents())); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_operations_file_put_contents -- WP_Filesystem is not appropriate here.
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the mu-plugin contents are valid
|
||||
*
|
||||
* @param string $contents
|
||||
* @return boolean
|
||||
*/
|
||||
protected function is_content_valid($contents) {
|
||||
|
||||
//The regexes we extract the paths from
|
||||
$regexes = array('/file_exists\(\'(.*)\'\)/isU', '/include_once\(\'(.*)\'\)/isU');
|
||||
$regex = '';
|
||||
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
||||
|
||||
foreach ($regexes as $regex) {
|
||||
$matches = array();
|
||||
$result = preg_match($regex, $contents, $matches);
|
||||
|
||||
if (empty($matches[1]) || false === $result) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($bootstrap_path !== $matches[1]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The regex pattern that demarcates our contents
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_regex_pattern() {
|
||||
return '#// Begin AIOWPSEC Firewall(.*)// End AIOWPSEC Firewall#isU';
|
||||
}
|
||||
|
||||
/**
|
||||
* Our firewall code to insert
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_contents() {
|
||||
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
||||
|
||||
$code = "<?php\n";
|
||||
$code .= "// Begin AIOWPSEC Firewall\n";
|
||||
$code .= "if (file_exists('{$bootstrap_path}')) {\n";
|
||||
$code .= "\tinclude_once('{$bootstrap_path}');\n";
|
||||
$code .= "}\n";
|
||||
$code .= "// End AIOWPSEC Firewall\n";
|
||||
|
||||
return $code;
|
||||
}
|
||||
}
|
||||
Executable
+98
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; //Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Block_Userini extends AIOWPSecurity_Block_File {
|
||||
|
||||
/**
|
||||
* Inserts our directive into the user.ini file
|
||||
*
|
||||
* @return boolean|WP_Error true if inserted; false if failed
|
||||
*/
|
||||
public function insert_contents() {
|
||||
|
||||
$home_path = AIOWPSecurity_Utility_File::get_home_path();
|
||||
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions -- wp_filesystem not recommended.
|
||||
if (!is_writable($home_path)) {
|
||||
return new WP_Error(
|
||||
'file_directory_not_writable',
|
||||
'The directory has incorrect write permissions. Please double check its permissions and try again.',
|
||||
$home_path
|
||||
);
|
||||
}
|
||||
|
||||
return (false !== @file_put_contents($this->file_path, $this->get_contents(), FILE_APPEND)); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the user.ini file contents are valid
|
||||
*
|
||||
* @param string $contents
|
||||
* @return boolean
|
||||
*/
|
||||
protected function is_content_valid($contents) {
|
||||
|
||||
$regex = '/auto_prepend_file=\'(.*)\'/isU';
|
||||
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
||||
|
||||
$match = array();
|
||||
$result = preg_match($regex, $contents, $match);
|
||||
|
||||
if (empty($match[1]) || false === $result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($bootstrap_path !== $match[1]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Our regex pattern that demarcates our contents
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_regex_pattern() {
|
||||
return '/\r?\n?# Begin AIOWPSEC Firewall(.*?)# End AIOWPSEC Firewall/is';
|
||||
}
|
||||
|
||||
/**
|
||||
* Directives inserted into user.ini
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_contents() {
|
||||
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
||||
|
||||
$directive = "\n# Begin AIOWPSEC Firewall\n";
|
||||
$directive .= "auto_prepend_file='{$bootstrap_path}'\n";
|
||||
$directive .= "# End AIOWPSEC Firewall";
|
||||
|
||||
return $directive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the contains_contents function to check for already set directives
|
||||
*
|
||||
* @return boolean|WP_Error
|
||||
*/
|
||||
public function contains_contents() {
|
||||
$contains = parent::contains_contents();
|
||||
|
||||
if (false === $contains) {
|
||||
$directive_userini = AIOWPSecurity_Utility_Firewall::get_already_set_directive($this->file_path);
|
||||
$directive = AIOWPSecurity_Utility_Firewall::get_already_set_directive();
|
||||
|
||||
if ((AIOWPSecurity_Utility_Firewall::get_bootstrap_path() === $directive) || (AIOWPSecurity_Utility_Firewall::get_bootstrap_path() === $directive_userini)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return $contains;
|
||||
}
|
||||
}
|
||||
Executable
+103
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; //Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Block_WpConfig extends AIOWPSecurity_Block_File {
|
||||
|
||||
/**
|
||||
* Attempts to insert our code contents into wp-config file
|
||||
*
|
||||
* @return boolean|WP_Error true if success; false if unsuccessful
|
||||
*/
|
||||
public function insert_contents() {
|
||||
|
||||
if (!is_readable($this->file_path) || !is_writable($this->file_path)) {
|
||||
return new WP_Error(
|
||||
'file_wrong_permissions',
|
||||
'The file has incorrect read or write permissions. Please double check its permissions and try again.',
|
||||
$this->file_path
|
||||
);
|
||||
}
|
||||
|
||||
//Take a backup of the file first
|
||||
if (false === AIOWPSecurity_Utility_File::backup_and_rename_wp_config($this->file_path)) {
|
||||
return new WP_Error(
|
||||
'file_unable_to_backup',
|
||||
'We were unable to take a backup of your file.',
|
||||
$this->file_path
|
||||
);
|
||||
}
|
||||
|
||||
$wp_config = file($this->file_path, FILE_IGNORE_NEW_LINES);
|
||||
|
||||
if (false === $wp_config) {
|
||||
return new WP_Error(
|
||||
'file_no_contents',
|
||||
'Unable to access the file\'s contents',
|
||||
$this->file_path
|
||||
);
|
||||
}
|
||||
|
||||
array_shift($wp_config);
|
||||
array_unshift($wp_config, $this->get_contents());
|
||||
|
||||
return (false !== @file_put_contents($this->file_path, implode(PHP_EOL, $wp_config), LOCK_EX)); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the validity of the content
|
||||
*
|
||||
* @param string $contents - contents we're checking
|
||||
* @return boolean true if content is valid; false if invalid
|
||||
*/
|
||||
protected function is_content_valid($contents) {
|
||||
//The regexes we extract the paths from
|
||||
$regexes = array('/file_exists\(\'(.*)\'\)/isU', '/include_once\(\'(.*)\'\)/isU');
|
||||
$regex = '';
|
||||
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
||||
|
||||
foreach ($regexes as $regex) {
|
||||
$matches = array();
|
||||
$result = preg_match($regex, $contents, $matches);
|
||||
|
||||
if (empty($matches[1]) || false === $result) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($bootstrap_path !== $matches[1]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The particular regex that demarcates our contents
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_regex_pattern() {
|
||||
return '#\r?\n?// Begin AIOWPSEC Firewall(.*?)// End AIOWPSEC Firewall#is';
|
||||
}
|
||||
|
||||
/**
|
||||
* Our firewall code to insert
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_contents() {
|
||||
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
||||
|
||||
$code = "<?php\n";
|
||||
$code .= "// Begin AIOWPSEC Firewall\n";
|
||||
$code .= "if (file_exists('{$bootstrap_path}')) {\n";
|
||||
$code .= "\tinclude_once('{$bootstrap_path}');\n";
|
||||
$code .= "}\n";
|
||||
$code .= "// End AIOWPSEC Firewall";
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
}
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;//Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Blocking {
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
**/
|
||||
public function __construct() {
|
||||
//NOP
|
||||
}
|
||||
|
||||
/**
|
||||
* Will return an array of blocked IPs in the AIOWPSEC_TBL_PERM_BLOCK table
|
||||
*
|
||||
* @param string $block_reason - spam, etc
|
||||
* @param string $output_type
|
||||
*
|
||||
* @return single dimensional array
|
||||
*/
|
||||
public static function get_list_blocked_ips($block_reason = '', $output_type = 'ARRAY_A') {
|
||||
global $wpdb;
|
||||
$blocked_ip_array = array();
|
||||
if (empty($block_reason)) {
|
||||
$sql = 'SELECT blocked_ip FROM '.AIOWPSEC_TBL_PERM_BLOCK;
|
||||
} else {
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- PCP error. Ignore.
|
||||
$sql = $wpdb->prepare('SELECT blocked_ip FROM '.AIOWPSEC_TBL_PERM_BLOCK.' WHERE block_reason=%s', $block_reason);
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery -- PCP error. Ignore.
|
||||
$result = $wpdb->get_results($sql, $output_type);
|
||||
//The result returned by wp function is multi-dim array. Let's return a simple single dimensional array of ip addresses
|
||||
if (!empty($result)) {
|
||||
foreach ($result as $item) {
|
||||
$blocked_ip_array[] = $item['blocked_ip'];
|
||||
}
|
||||
}
|
||||
return $blocked_ip_array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an IP address is blocked permanently according to the database
|
||||
*
|
||||
* @param int $ip_address
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_ip_blocked($ip_address) {
|
||||
global $wpdb;
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery -- PCP error. Ignore.
|
||||
$blocked_record = $wpdb->get_row($wpdb->prepare('SELECT * FROM '.AIOWPSEC_TBL_PERM_BLOCK.' WHERE blocked_ip=%s', $ip_address));
|
||||
return !empty($blocked_record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will add an IP address to the permanent block list
|
||||
*
|
||||
* @param int $ip_address
|
||||
* @param string $reason
|
||||
* @return bool - TRUE or FALSE on error
|
||||
*/
|
||||
public static function add_ip_to_block_list($ip_address, $reason = '') {
|
||||
global $wpdb, $aio_wp_security;
|
||||
$user = wp_get_current_user();
|
||||
$roles_allowed_to_block_ips = apply_filters('aio_roles_allowed_to_block_ips', array('administrator', 'editor', 'author'));
|
||||
if ('spam_discard' != $reason && array_intersect($roles_allowed_to_block_ips, $user->roles) && AIOWPSecurity_Utility_IP::get_user_ip_address() == $ip_address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//Check if this IP address is already in the block list
|
||||
$blocked = AIOWPSecurity_Blocking::is_ip_blocked($ip_address);
|
||||
$time_now = current_time('mysql');
|
||||
if (empty($blocked)) {
|
||||
//Add this IP to the blocked table
|
||||
$data = array(
|
||||
'blocked_ip'=>$ip_address,
|
||||
'block_reason'=>$reason,
|
||||
'blocked_date'=>$time_now
|
||||
);
|
||||
$data = apply_filters('aiowps_pre_add_to_permanent_block', $data);
|
||||
$perm_block_tbl_name = AIOWPSEC_TBL_PERM_BLOCK;
|
||||
$country_origin = isset($data['country_origin']) ? $data['country_origin'] : '';
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQLPlaceholders.QuotedSimplePlaceholder, WordPress.DB.DirectDatabaseQuery -- PCP warning. Ignore.
|
||||
$sql = $wpdb->prepare("INSERT INTO ".$perm_block_tbl_name." (blocked_ip, block_reason, blocked_date, country_origin, created) VALUES ('%s', '%s', '%s', '%s', UNIX_TIMESTAMP())", $data['blocked_ip'], $data['block_reason'], $data['blocked_date'], $country_origin);
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery -- PCP error. Ignore.
|
||||
$res = $wpdb->query($sql);
|
||||
if (false === $res) {
|
||||
$aio_wp_security->debug_logger->log_debug("AIOWPSecurity_Blocking::add_ip_to_block_list - Error inserting record into AIOWPSEC_TBL_PERM_BLOCK table for IP ".$ip_address);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function unblock_ip($ip_address) {
|
||||
global $wpdb;
|
||||
$where = array('blocked_ip' => $ip_address);
|
||||
// phpcs:ignore WordPress.DB -- PCP error. Direct call necessary. No caching needed.
|
||||
$result = $wpdb->delete(AIOWPSEC_TBL_PERM_BLOCK, $where);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will check the current visitor IP against the blocked table
|
||||
* If IP present will block the visitor from viewing the site
|
||||
*/
|
||||
public static function check_visitor_ip_and_perform_blocking() {
|
||||
global $aio_wp_security;
|
||||
$visitor_ip = AIOWPSecurity_Utility_IP::get_user_ip_address();
|
||||
$ip_type = WP_Http::is_ip_address($visitor_ip);
|
||||
if (empty($ip_type)) {
|
||||
$aio_wp_security->debug_logger->log_debug("do_general_ip_blocking_tasks: ".$visitor_ip." is not a valid IP!", 4);
|
||||
return;
|
||||
}
|
||||
|
||||
//Check if this IP address is in the block list
|
||||
$blocked = AIOWPSecurity_Blocking::is_ip_blocked($visitor_ip);
|
||||
//TODO - future feature: add blocking whitelist and check
|
||||
|
||||
if (empty($blocked)) {
|
||||
return; //Visitor IP is not blocked - allow page to load
|
||||
} else {
|
||||
//block this visitor!!
|
||||
$redirect_url = sanitize_url(apply_filters('aios_blocked_request_redirect_url', 'http://127.0.0.1'));
|
||||
AIOWPSecurity_Utility::redirect_to_url($redirect_url);
|
||||
}
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+941
@@ -0,0 +1,941 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) die('No direct access.');
|
||||
|
||||
class AIOWPSecurity_Captcha {
|
||||
|
||||
private $cloudflare_verify_turnstile_url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
|
||||
|
||||
private $google_verify_recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify';
|
||||
|
||||
/**
|
||||
* Constructor for the AIOWPSecurity_Captcha class.
|
||||
*
|
||||
* Initializes and sets up actions and filters related to AIOWPS Captcha features.
|
||||
*/
|
||||
public function __construct() {
|
||||
global $aio_wp_security;
|
||||
|
||||
$this->upgrade_captcha_options();
|
||||
add_action('login_enqueue_scripts', array($this, 'aiowps_login_enqueue'));
|
||||
add_filter('script_loader_tag', array($this, 'add_cfasync_data_attribute_to_captcha_tag'), 10, 2);
|
||||
|
||||
|
||||
if (AIOWPSecurity_Utility::is_contact_form_7_plugin_active() && '1' == $aio_wp_security->configs->get_value('aiowps_enable_contact_form_7_captcha')) {
|
||||
add_action('wpcf7_admin_init', array($this, 'add_contact_form_7_captcha_tag_generator_button'), 100, 0);
|
||||
add_filter('wpcf7_contact_form_properties', array($this, 'add_contact_form_7_captcha'));
|
||||
add_filter('wpcf7_validate', array($this, 'verify_contact_form_7_captcha'), 10, 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method modifies the script tag output by adding 'data-cfasync="false"' attribute,
|
||||
* which is used to disable Cloudflare Rocket Loader for the specified captcha script handle.
|
||||
*
|
||||
* @param string $tag - The generated HTML tag for the script.
|
||||
* @param string $handle - The script handle being processed.
|
||||
*
|
||||
* @return string The modified HTML tag with 'data-cfasync="false"' if applicable.
|
||||
*/
|
||||
public function add_cfasync_data_attribute_to_captcha_tag($tag, $handle) {
|
||||
global $aio_wp_security;
|
||||
|
||||
// Get the default captcha from AIO WP Security configurations
|
||||
$default_captcha = $aio_wp_security->configs->get_value('aiowps_default_captcha');
|
||||
|
||||
// Check if the current script handle matches the default captcha handle
|
||||
if ($default_captcha === $handle) {
|
||||
if (!preg_match('/\sdata-cfasync\s*=\s*["\']false["\']/', $tag)) {
|
||||
// Add 'data-cfasync="false"' attribute to the script tag
|
||||
$tag = str_replace('src=', 'data-cfasync="false" src=', $tag);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the modified or original script tag
|
||||
return $tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function handles upgrading captcha options
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function upgrade_captcha_options() {
|
||||
global $aio_wp_security;
|
||||
|
||||
if (!empty($aio_wp_security->configs->get_value('aiowps_default_captcha'))) return;
|
||||
|
||||
// Upgrade the default captcha option
|
||||
if ($aio_wp_security->configs->get_value('aiowps_default_recaptcha')) {
|
||||
$aio_wp_security->configs->set_value('aiowps_default_recaptcha', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_default_captcha', 'google-recaptcha-v2');
|
||||
} elseif ('1' == $aio_wp_security->configs->get_value('aiowps_enable_login_captcha') || '1' == $aio_wp_security->configs->get_value('aiowps_enable_registration_page_captcha')) {
|
||||
$aio_wp_security->configs->set_value('aiowps_default_captcha', 'simple-math');
|
||||
} else {
|
||||
$aio_wp_security->configs->set_value('aiowps_default_captcha', 'none');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will return an array of supported CAPTCHA options
|
||||
*
|
||||
* @return array - an array of supported CAPTCHA options
|
||||
*/
|
||||
public function get_supported_captchas() {
|
||||
return array(
|
||||
'none' => 'No CAPTCHA',
|
||||
'cloudflare-turnstile' => 'Cloudflare Turnstile',
|
||||
'google-recaptcha-v2' => 'Google reCAPTCHA V2',
|
||||
'simple-math' => 'Simple math CAPTCHA'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will return an array of supported CAPTCHA themes
|
||||
*
|
||||
* @return array - an array of supported CAPTCHA themes
|
||||
*/
|
||||
public function get_captcha_themes() {
|
||||
return array(
|
||||
'auto' => __('Auto', 'all-in-one-wp-security-and-firewall'),
|
||||
'light' => __('Light', 'all-in-one-wp-security-and-firewall'),
|
||||
'dark' => __('Dark', 'all-in-one-wp-security-and-firewall'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the CAPTCHA script for the default CAPTCHA on the standard WP login page
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function aiowps_login_enqueue() {
|
||||
global $aio_wp_security;
|
||||
|
||||
if ($aio_wp_security->is_login_lockdown_by_const()) return;
|
||||
|
||||
if ('1' != $aio_wp_security->configs->get_value('aiowps_enable_login_captcha') && '1' != $aio_wp_security->configs->get_value('aiowps_enable_registration_page_captcha')) return;
|
||||
|
||||
$default_captcha = $aio_wp_security->configs->get_value('aiowps_default_captcha');
|
||||
|
||||
switch ($default_captcha) {
|
||||
case 'cloudflare-turnstile':
|
||||
case 'google-recaptcha-v2':
|
||||
wp_enqueue_script($default_captcha, $this->get_captcha_script_url($default_captcha), array());
|
||||
// Below is needed to provide some space for the CAPTCHA form (otherwise it appears partially hidden on RHS)
|
||||
wp_add_inline_style('login', "#login { width: 340px; }");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the user is not on the WooCommerce account page, enqueue the CAPTCHA script in the wp_head for general pages
|
||||
* Caters for scenarios when CAPTCHA is used on wp comments or custom wp login form pages
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_captcha_script() {
|
||||
global $aio_wp_security;
|
||||
|
||||
// Do NOT enqueue if this is the main WooCommerce account login page because for WooCommerce page we "explicitly" render the reCAPTCHA widget
|
||||
$is_woo = false;
|
||||
|
||||
// We don't want to load for Woo account page because we have a special function for this
|
||||
if (function_exists('is_account_page')) $is_woo = is_account_page();
|
||||
|
||||
if (!empty($is_woo)) return;
|
||||
|
||||
$default_captcha = $aio_wp_security->configs->get_value('aiowps_default_captcha');
|
||||
|
||||
switch ($default_captcha) {
|
||||
case 'cloudflare-turnstile':
|
||||
case 'google-recaptcha-v2':
|
||||
wp_enqueue_script($default_captcha, $this->get_captcha_script_url($default_captcha), array());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders CAPTCHA on form produced by the wp_login_form() function, ie, custom wp login form
|
||||
*
|
||||
* @param string $cust_html_code
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function insert_captcha_custom_login($cust_html_code) {
|
||||
global $aio_wp_security;
|
||||
|
||||
if ($aio_wp_security->is_login_lockdown_by_const()) return '';
|
||||
|
||||
$default_captcha = $aio_wp_security->configs->get_value('aiowps_default_captcha');
|
||||
|
||||
switch ($default_captcha) {
|
||||
case 'cloudflare-turnstile':
|
||||
case 'google-recaptcha-v2':
|
||||
$cust_html_code .= $this->get_captcha_form($default_captcha, 0, true);
|
||||
return $cust_html_code;
|
||||
break;
|
||||
case 'simple-math':
|
||||
$maths_captcha_input_id = uniqid('aiowps-captcha-answer-'); // Generate a unique DOM-safe ID for the maths captcha input field to avoid duplicate IDs (when multiple forms appear on the same page).
|
||||
$cap_form = '<p class="aiowps-captcha"><label for="' . esc_attr($maths_captcha_input_id) . '">'.__('Please enter an answer in digits:', 'all-in-one-wp-security-and-firewall').'</label>';
|
||||
$cap_form .= '<div class="aiowps-captcha-equation"><strong>';
|
||||
$maths_question_output = $aio_wp_security->captcha_obj->generate_maths_question($maths_captcha_input_id);
|
||||
$cap_form .= $maths_question_output . '</strong></div></p>';
|
||||
|
||||
$cust_html_code .= $cap_form;
|
||||
return $cust_html_code;
|
||||
break;
|
||||
default:
|
||||
return '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts captcha into the password-protected page form.
|
||||
*
|
||||
* @param string $cust_html_code The HTML code for the password form.
|
||||
* @return string The modified HTML code with the captcha inserted.
|
||||
*/
|
||||
public function insert_captcha_password_protected($cust_html_code) {
|
||||
global $post, $aio_wp_security;
|
||||
|
||||
$loginurl = esc_url(site_url('/wp-login.php?action=postpass'));
|
||||
|
||||
$label = 'pwbox-' . empty($post->ID) ? rand() : $post->ID;
|
||||
$default_captcha = $aio_wp_security->configs->get_value('aiowps_default_captcha');
|
||||
|
||||
$cust_html_code = '<div class="container-custom">
|
||||
<form action="'.esc_attr($loginurl).'" method="post">
|
||||
<label>'.__('This content is password protected.', 'all-in-one-wp-security-and-firewall').' '.__('To view it please enter your password below:', 'all-in-one-wp-security-and-firewall').'</label>
|
||||
<label for="'.esc_attr($label).'">'.__('Password:', 'all-in-one-wp-security-and-firewall').'</label>
|
||||
<input name="post_password" id="'.esc_attr($label).'" type="password" size="20" />';
|
||||
switch ($default_captcha) {
|
||||
case 'cloudflare-turnstile':
|
||||
case 'google-recaptcha-v2':
|
||||
$cust_html_code .= $this->get_captcha_form($default_captcha, 0, true);
|
||||
$this->add_captcha_script();
|
||||
break;
|
||||
case 'simple-math':
|
||||
$maths_captcha_input_id = uniqid('aiowps-captcha-answer-'); // Generate a unique DOM-safe ID for the maths captcha input field to avoid duplicate IDs (when multiple forms appear on the same page).
|
||||
$captcha_form = '<p class="aiowps-captcha"><label for="' . esc_attr($maths_captcha_input_id) . '">'.__('Please enter an answer in digits:', 'all-in-one-wp-security-and-firewall').'</label>';
|
||||
$captcha_form .= '<div class="aiowps-captcha-equation"><strong>';
|
||||
$maths_question_output = $aio_wp_security->captcha_obj->generate_maths_question($maths_captcha_input_id);
|
||||
$captcha_form .= $maths_question_output . '</strong></div></p>';
|
||||
$cust_html_code .= $captcha_form;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
$cust_html_code .= '<input type="submit" name="Submit" class="button" value="'.__('Enter', 'all-in-one-wp-security-and-firewall').'" />
|
||||
</form>
|
||||
</div>';
|
||||
return $cust_html_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the password form on password protected pages with captcha.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function validate_password_protected_password_form_with_captcha() {
|
||||
// Check password protected page captcha
|
||||
$verify = $this->verify_captcha_submit();
|
||||
if (!$verify) {
|
||||
wp_die(__('Captcha verification failed.', 'all-in-one-wp-security-and-firewall'). ' ' . __('Please try again.', 'all-in-one-wp-security-and-firewall'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert CAPTCHA question form on WooCommerce my account page forms or other forms.
|
||||
*
|
||||
* This function determines the type of CAPTCHA to display based on the configured default CAPTCHA type.
|
||||
* It handles special cases for WooCommerce "my account" page where both login and register forms need
|
||||
* CAPTCHAs rendered explicitly. For other forms, it renders CAPTCHA normally.
|
||||
*
|
||||
* @global object $aio_wp_security The global instance of the aio_wp_security class, which holds configuration settings.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function insert_captcha_question_form() {
|
||||
global $aio_wp_security;
|
||||
|
||||
$default_captcha = $aio_wp_security->configs->get_value('aiowps_default_captcha');
|
||||
|
||||
switch ($default_captcha) {
|
||||
case 'cloudflare-turnstile':
|
||||
case 'google-recaptcha-v2':
|
||||
// WooCommerce "my account" page needs special consideration, ie,
|
||||
// need to display two CAPTCHA forms on same page (for login and register forms)
|
||||
// For this case we use the "explicit" CAPTCHA display
|
||||
$calling_hook = current_filter();
|
||||
if ('woocommerce_login_form' == $calling_hook || 'woocommerce_lostpassword_form' == $calling_hook || 'woocommerce_after_checkout_billing_form' == $calling_hook) {
|
||||
$this->get_captcha_form($default_captcha, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if ('woocommerce_register_form' == $calling_hook) {
|
||||
$this->get_captcha_form($default_captcha, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// For all other forms simply display CAPTCHA as normal
|
||||
$this->display_captcha_form($default_captcha);
|
||||
break;
|
||||
case 'simple-math':
|
||||
// Display plain maths CAPTCHA form
|
||||
$this->display_captcha_form($default_captcha);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For WooCommerce my account page - display two separate CAPTCHA forms "explicitly"
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function print_captcha_api_woo() {
|
||||
global $aio_wp_security;
|
||||
|
||||
|
||||
//captcha should only show for woo account and checkout page
|
||||
if ((function_exists('is_account_page') && !is_account_page()) && (function_exists('is_checkout') && !is_checkout()) && !apply_filters('aios_print_captcha_api_woo', false)) return;
|
||||
|
||||
$default_captcha = $aio_wp_security->configs->get_value('aiowps_default_captcha');
|
||||
|
||||
if ('cloudflare-turnstile' == $default_captcha) :
|
||||
$site_key = esc_html($aio_wp_security->configs->get_value('aiowps_turnstile_site_key'));
|
||||
?>
|
||||
<script src='https://challenges.cloudflare.com/turnstile/v0/api.js' async defer></script>
|
||||
<?php
|
||||
elseif ('google-recaptcha-v2' == $default_captcha) :
|
||||
$site_key = esc_html($aio_wp_security->configs->get_value('aiowps_recaptcha_site_key'));
|
||||
?>
|
||||
<script>
|
||||
var verifyCallback = function(response) {
|
||||
alert(response);
|
||||
};
|
||||
var onloadCallback = function() {
|
||||
if (jQuery('.aios-wc-captcha').length) {
|
||||
jQuery('.aios-wc-captcha').each(function(index, element) {
|
||||
grecaptcha.render(element, {
|
||||
'sitekey' : '<?php echo $site_key; ?>',
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<script src='https://www.google.com/recaptcha/api.js?hl=<?php echo $this->get_google_recaptcha_compatible_site_locale(); ?>&onload=onloadCallback&render=explicit' async defer></script>
|
||||
<?php
|
||||
endif;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays CAPTCHA form
|
||||
*
|
||||
* @param string $default_captcha - the default CAPTCHA
|
||||
* @param boolean $return_instead_of_echo - if we should return the form rather than echo it to page
|
||||
*
|
||||
* @return string|void
|
||||
*/
|
||||
public function display_captcha_form($default_captcha, $return_instead_of_echo = false) {
|
||||
global $aio_wp_security;
|
||||
|
||||
if ($aio_wp_security->configs->get_value('aiowps_enable_bp_register_captcha') == '1' && defined('BP_VERSION')) {
|
||||
//if buddy press feature active add action hook so buddy press can display our errors properly on bp registration form
|
||||
do_action('bp_aiowps-captcha-answer_errors');
|
||||
}
|
||||
|
||||
switch ($default_captcha) {
|
||||
case 'cloudflare-turnstile':
|
||||
if ('1' == $aio_wp_security->configs->get_value('aios_cloudflare_turnstile_invalid_configuration')) return;
|
||||
if ($return_instead_of_echo) return $this->get_captcha_form($default_captcha, 0, $return_instead_of_echo);
|
||||
$this->get_captcha_form($default_captcha);
|
||||
break;
|
||||
case 'google-recaptcha-v2':
|
||||
if ('1' == $aio_wp_security->configs->get_value('aios_google_recaptcha_invalid_configuration')) return;
|
||||
if ($return_instead_of_echo) return $this->get_captcha_form($default_captcha, 0, $return_instead_of_echo);
|
||||
$this->get_captcha_form($default_captcha);
|
||||
break;
|
||||
case 'simple-math':
|
||||
$maths_captcha_input_id = uniqid('aiowps-captcha-answer-'); // Generate a unique DOM-safe ID for the maths captcha input field to avoid duplicate IDs (when multiple forms appear on the same page).
|
||||
$cap_form = '<p class="aiowps-captcha hide-when-displaying-tfa-input"><label for="' . esc_attr($maths_captcha_input_id) . '">'.__('Please enter an answer in digits:', 'all-in-one-wp-security-and-firewall').'</label>';
|
||||
$cap_form .= '<div class="aiowps-captcha-equation hide-when-displaying-tfa-input"><strong>';
|
||||
$maths_question_output = $this->generate_maths_question($maths_captcha_input_id);
|
||||
$cap_form .= $maths_question_output . '</strong></div></p>';
|
||||
if ($return_instead_of_echo) return $cap_form;
|
||||
echo $cap_form;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It generates a random math problem, stores the answer in the database, and returns the math problem
|
||||
*
|
||||
* @param string $maths_captcha_input_id A unique identifier used for the captcha input field's ID attribute to prevent duplicate IDs in the DOM (e.g., when multiple forms exist on a page).
|
||||
*
|
||||
* @return string - contains the HTML for the captcha.
|
||||
*/
|
||||
private function generate_maths_question($maths_captcha_input_id) {
|
||||
global $aio_wp_security;
|
||||
//For now we will only do plus, minus, multiplication
|
||||
$equation_string = '';
|
||||
$operator_type = array('+', '−', '×');
|
||||
|
||||
$operand_display = array('word', 'number');
|
||||
|
||||
//let's now generate an equation
|
||||
$operator = $operator_type[rand(0, 2)];
|
||||
|
||||
if ('×' === $operator) {
|
||||
//Don't make the question too hard if multiplication
|
||||
$first_digit = rand(1, 5);
|
||||
$second_digit = rand(1, 5);
|
||||
} else {
|
||||
$first_digit = rand(1, 20);
|
||||
$second_digit = rand(1, 20);
|
||||
}
|
||||
|
||||
if ('word' == $operand_display[rand(0, 1)]) {
|
||||
$first_operand = $this->number_word_mapping($first_digit);
|
||||
} else {
|
||||
$first_operand = $first_digit;
|
||||
}
|
||||
|
||||
if ('word' == $operand_display[rand(0, 1)]) {
|
||||
$second_operand = $this->number_word_mapping($second_digit);
|
||||
} else {
|
||||
$second_operand = $second_digit;
|
||||
}
|
||||
|
||||
//Let's calculate the result and construct the equation string
|
||||
if ('+' === $operator) {
|
||||
//Addition
|
||||
$result = $first_digit+$second_digit;
|
||||
$equation_string .= $first_operand . ' ' . $operator . ' ' . $second_operand . ' = ';
|
||||
} elseif ('−' === $operator) {
|
||||
//Subtraction
|
||||
//If we are going to be negative let's swap operands around
|
||||
if ($first_digit < $second_digit) {
|
||||
$equation_string .= $second_operand . ' ' . $operator . ' ' . $first_operand . ' = ';
|
||||
$result = $second_digit-$first_digit;
|
||||
} else {
|
||||
$equation_string .= $first_operand . ' ' . $operator . ' ' . $second_operand . ' = ';
|
||||
$result = $first_digit-$second_digit;
|
||||
}
|
||||
} elseif ('×' === $operator) {
|
||||
//Multiplication
|
||||
$equation_string .= $first_operand . ' ' . $operator . ' ' . $second_operand . ' = ';
|
||||
$result = $first_digit*$second_digit;
|
||||
}
|
||||
|
||||
//Let's encode correct answer
|
||||
$captcha_secret_string = $aio_wp_security->configs->get_value('aiowps_captcha_secret_key');
|
||||
$current_time = time();
|
||||
$enc_result = base64_encode($current_time.$captcha_secret_string.$result);
|
||||
$random_str = AIOWPSecurity_Utility::generate_alpha_numeric_random_string(10);
|
||||
if (is_multisite()) {
|
||||
update_site_option('aiowps_captcha_string_info_'.$random_str, $enc_result);
|
||||
update_site_option('aiowps_captcha_string_info_time_'.$random_str, $current_time);
|
||||
} else {
|
||||
update_option('aiowps_captcha_string_info_'.$random_str, $enc_result, false);
|
||||
update_option('aiowps_captcha_string_info_time_'.$random_str, $current_time, false);
|
||||
}
|
||||
$equation_string .= '<input type="hidden" name="aiowps-captcha-string-info" class="aiowps-captcha-string-info" value="'.$random_str.'" />';
|
||||
$equation_string .= '<input type="hidden" name="aiowps-captcha-temp-string" class="aiowps-captcha-temp-string" value="'.$current_time.'" />';
|
||||
$equation_string .= '<input type="text" size="2" id="' . esc_attr($maths_captcha_input_id) . '" class="aiowps-captcha-answer" name="aiowps-captcha-answer" value="" autocomplete="off" />';
|
||||
return $equation_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function takes a number and returns the word that represents that number
|
||||
*
|
||||
* @param integer $num - the number we want to map to a word
|
||||
*
|
||||
* @return string - the mapped word
|
||||
*/
|
||||
private function number_word_mapping($num) {
|
||||
$number_map = array(
|
||||
1 => __('one', 'all-in-one-wp-security-and-firewall'),
|
||||
2 => __('two', 'all-in-one-wp-security-and-firewall'),
|
||||
3 => __('three', 'all-in-one-wp-security-and-firewall'),
|
||||
4 => __('four', 'all-in-one-wp-security-and-firewall'),
|
||||
5 => __('five', 'all-in-one-wp-security-and-firewall'),
|
||||
6 => __('six', 'all-in-one-wp-security-and-firewall'),
|
||||
7 => __('seven', 'all-in-one-wp-security-and-firewall'),
|
||||
8 => __('eight', 'all-in-one-wp-security-and-firewall'),
|
||||
9 => __('nine', 'all-in-one-wp-security-and-firewall'),
|
||||
10 => __('ten', 'all-in-one-wp-security-and-firewall'),
|
||||
11 => __('eleven', 'all-in-one-wp-security-and-firewall'),
|
||||
12 => __('twelve', 'all-in-one-wp-security-and-firewall'),
|
||||
13 => __('thirteen', 'all-in-one-wp-security-and-firewall'),
|
||||
14 => __('fourteen', 'all-in-one-wp-security-and-firewall'),
|
||||
15 => __('fifteen', 'all-in-one-wp-security-and-firewall'),
|
||||
16 => __('sixteen', 'all-in-one-wp-security-and-firewall'),
|
||||
17 => __('seventeen', 'all-in-one-wp-security-and-firewall'),
|
||||
18 => __('eighteen', 'all-in-one-wp-security-and-firewall'),
|
||||
19 => __('nineteen', 'all-in-one-wp-security-and-firewall'),
|
||||
20 => __('twenty', 'all-in-one-wp-security-and-firewall'),
|
||||
);
|
||||
return $number_map[$num];
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will return the CAPTCHA script URL
|
||||
*
|
||||
* @param string $default_captcha - the default CAPTCHA
|
||||
*
|
||||
* @return string - the CAPTCHA script URL
|
||||
*/
|
||||
private function get_captcha_script_url($default_captcha) {
|
||||
$url = '';
|
||||
switch ($default_captcha) {
|
||||
case 'cloudflare-turnstile':
|
||||
$url = 'https://challenges.cloudflare.com/turnstile/v0/api.js';
|
||||
break;
|
||||
case 'google-recaptcha-v2':
|
||||
$url = 'https://www.google.com/recaptcha/api.js?hl=' . $this->get_google_recaptcha_compatible_site_locale();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will return the CAPTCHA form
|
||||
*
|
||||
* @param string $default_captcha - the default CAPTCHA
|
||||
* @param integer $include_wc_id - the WooCommerce form id to include, if 0 no id is included if 1 dynamic id included
|
||||
* @param boolean $return_instead_of_echo - if we should return the form rather than echo it to page
|
||||
*
|
||||
* @return string - can return the CAPTCHA form
|
||||
*/
|
||||
public function get_captcha_form($default_captcha, $include_wc_id = 0, $return_instead_of_echo = false) {
|
||||
global $aio_wp_security;
|
||||
static $aios_wc_element_id;
|
||||
$captcha_form = '';
|
||||
$captcha_data_callback = '';
|
||||
$wc_form_id = !empty($include_wc_id) ? 'id="woo_recaptcha_'.(++$aios_wc_element_id).'"' : '';
|
||||
$captcha_data_callback = apply_filters('aios_captcha_data_callback', false) ? ' data-callback="data_callback"' : '';
|
||||
|
||||
switch ($default_captcha) {
|
||||
case 'cloudflare-turnstile':
|
||||
$site_key = esc_html($aio_wp_security->configs->get_value('aiowps_turnstile_site_key'));
|
||||
$turnstile_theme = esc_html($aio_wp_security->configs->get_value('aiowps_turnstile_theme'));
|
||||
if (empty($turnstile_theme)) $turnstile_theme = 'auto';
|
||||
$captcha_form = '<div class="cf-turnstile-wrap" style="padding:10px 0 10px 0"><div '. $wc_form_id .' class="cf-turnstile'. (!empty($include_wc_id) ? ' aios-wc-captcha' : '') . '" data-sitekey="'.$site_key.'" data-theme="'.$turnstile_theme.'"'.$captcha_data_callback.'></div></div>';
|
||||
break;
|
||||
case 'google-recaptcha-v2':
|
||||
$site_key = esc_html($aio_wp_security->configs->get_value('aiowps_recaptcha_site_key'));
|
||||
$captcha_form = '<div class="g-recaptcha-wrap" style="padding:10px 0 10px 0"><div '. $wc_form_id .' class="g-recaptcha'. (!empty($include_wc_id) ? ' aios-wc-captcha' : '') . '" data-sitekey="'.$site_key.'"'.$captcha_data_callback.'></div></div>';
|
||||
break;
|
||||
default:
|
||||
return '';
|
||||
break;
|
||||
}
|
||||
|
||||
if ($return_instead_of_echo) return $captcha_form;
|
||||
echo $captcha_form;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the math or Google reCAPTCHA v2 forms
|
||||
* Returns TRUE if correct answer.
|
||||
* Returns FALSE on wrong CAPTCHA result or missing data.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function verify_captcha_submit() {
|
||||
global $aio_wp_security;
|
||||
|
||||
$default_captcha = $aio_wp_security->configs->get_value('aiowps_default_captcha');
|
||||
|
||||
switch ($default_captcha) {
|
||||
case 'cloudflare-turnstile':
|
||||
// Cloudflare Turnstile enabled
|
||||
if ('1' == $aio_wp_security->configs->get_value('aios_cloudflare_turnstile_invalid_configuration')) return true;
|
||||
|
||||
// Expected CAPTCHA field in $_POST but got none!
|
||||
if (!array_key_exists('cf-turnstile-response', $_POST)) return false;
|
||||
|
||||
$cf_turnstile_response = isset($_POST['cf-turnstile-response']) ? stripslashes($_POST['cf-turnstile-response']) : '';
|
||||
$verify_captcha = $this->verify_turnstile_recaptcha($cf_turnstile_response);
|
||||
return $verify_captcha;
|
||||
break;
|
||||
case 'google-recaptcha-v2':
|
||||
// Google reCAPTCHA enabled
|
||||
if ('1' == $aio_wp_security->configs->get_value('aios_google_recaptcha_invalid_configuration')) return true;
|
||||
|
||||
// Expected CAPTCHA field in $_POST but got none!
|
||||
if (!array_key_exists('g-recaptcha-response', $_POST)) return false;
|
||||
|
||||
$g_recaptcha_response = isset($_POST['g-recaptcha-response']) ? stripslashes($_POST['g-recaptcha-response']) : '';
|
||||
$verify_captcha = $this->verify_google_recaptcha($g_recaptcha_response);
|
||||
return $verify_captcha;
|
||||
break;
|
||||
case 'simple-math':
|
||||
// Math CAPTCHA is enabled
|
||||
if (!array_key_exists('aiowps-captcha-answer', $_POST)) return false;
|
||||
$captcha_answer = isset($_POST['aiowps-captcha-answer']) ? stripslashes($_POST['aiowps-captcha-answer']) : '';
|
||||
|
||||
$verify_captcha = $this->verify_math_captcha_answer($captcha_answer);
|
||||
return $verify_captcha;
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the math CAPTCHA answer entered by the user
|
||||
*
|
||||
* @param type $captcha_answer
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function verify_math_captcha_answer($captcha_answer = '') {
|
||||
global $aio_wp_security;
|
||||
$captcha_secret_string = $aio_wp_security->configs->get_value('aiowps_captcha_secret_key');
|
||||
$captcha_temp_string = sanitize_text_field($_POST['aiowps-captcha-temp-string']);
|
||||
$submitted_encoded_string = base64_encode($captcha_temp_string.$captcha_secret_string.$captcha_answer);
|
||||
$trans_handle = sanitize_text_field($_POST['aiowps-captcha-string-info']);
|
||||
if (is_multisite()) {
|
||||
$captcha_string_info_option = get_site_option('aiowps_captcha_string_info_'.$trans_handle);
|
||||
} else {
|
||||
$captcha_string_info_option = get_option('aiowps_captcha_string_info_'.$trans_handle);
|
||||
}
|
||||
if ($submitted_encoded_string === $captcha_string_info_option) {
|
||||
return true;
|
||||
} else {
|
||||
return false; // wrong answer was entered
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a query to Cloudflare API to verify Turnstile submission
|
||||
*
|
||||
* @param string $resp_token
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function verify_turnstile_recaptcha($resp_token = '') {
|
||||
global $aio_wp_security;
|
||||
|
||||
$url = $this->cloudflare_verify_turnstile_url;
|
||||
$secret = $aio_wp_security->configs->get_value('aiowps_turnstile_secret_key');
|
||||
return $this->verify_captcha_response($url, $secret, $resp_token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a query to Google API to verify reCAPTCHA submission
|
||||
*
|
||||
* @param string $resp_token
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function verify_google_recaptcha($resp_token = '') {
|
||||
global $aio_wp_security;
|
||||
|
||||
$url = $this->google_verify_recaptcha_url;
|
||||
$secret = $aio_wp_security->configs->get_value('aiowps_recaptcha_secret_key');
|
||||
return $this->verify_captcha_response($url, $secret, $resp_token);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function sends a remote request to verify the captcha response.
|
||||
*
|
||||
* @param string $url - The URL to the CAPTCHA API.
|
||||
* @param string $secret - The secret key you got from the CAPTCHA provider.
|
||||
* @param string $resp_token - The value of the CAPTCHA response form field.
|
||||
*
|
||||
* @return boolean - true if valid otherwise false
|
||||
*/
|
||||
private function verify_captcha_response($url, $secret, $resp_token) {
|
||||
|
||||
$is_humanoid = false;
|
||||
|
||||
if (empty($resp_token)) return $is_humanoid;
|
||||
|
||||
$ip_address = AIOWPSecurity_Utility_IP::get_user_ip_address();
|
||||
$response = wp_safe_remote_post($url, array(
|
||||
'body' => array(
|
||||
'secret' => $secret,
|
||||
'response' => $resp_token,
|
||||
'remoteip' => $ip_address,
|
||||
),
|
||||
));
|
||||
|
||||
if (wp_remote_retrieve_response_code($response) != 200) return $is_humanoid;
|
||||
|
||||
$response = wp_remote_retrieve_body($response);
|
||||
$response = json_decode($response, true);
|
||||
|
||||
if (isset($response['success']) && true == $response['success']) $is_humanoid = true;
|
||||
|
||||
// We did not get a success response so check for the "timeout-or-duplicate" error code because it's possible we have sent this request a second time if another plugin has recalled the WP authentication code and this error code means the captcha has already been solved so return success
|
||||
if (isset($response['error-codes']) && in_array('timeout-or-duplicate', $response['error-codes'])) $is_humanoid = true;
|
||||
|
||||
return $is_humanoid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get site locale code for Google reCaptcha.
|
||||
*
|
||||
* @return string The site locale code.
|
||||
*/
|
||||
private function get_google_recaptcha_compatible_site_locale() {
|
||||
$google_recaptcha_locale_codes = AIOS_Abstracted_Ids::get_google_recaptcha_locale_codes();
|
||||
$locale = str_replace('_', '-', determine_locale());
|
||||
|
||||
if (in_array($locale, $google_recaptcha_locale_codes, true)) {
|
||||
return $locale;
|
||||
}
|
||||
|
||||
// Return 2 letter locale code.
|
||||
$locale = explode('-', $locale);
|
||||
return $locale[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify Cloudflare Turnstile configuration.
|
||||
*
|
||||
* @param String $site_key
|
||||
* @param String $secret_key
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function cloudflare_turnstile_verify_configuration($site_key, $secret_key) {
|
||||
if (empty($site_key) || empty($secret_key)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify Google reCAPTCHA configuration.
|
||||
*
|
||||
* @param String $site_key
|
||||
* @param String $secret_key
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
public function google_recaptcha_verify_configuration($site_key, $secret_key) {
|
||||
$site_key_verify_params = array('k' => $site_key, 'size' => 'checkbox');
|
||||
$site_key_verify_url = esc_url(add_query_arg($site_key_verify_params, 'https://www.google.com/recaptcha/api2/anchor'));
|
||||
$site_key_verify_response_body = wp_remote_retrieve_body(wp_remote_get($site_key_verify_url));
|
||||
|
||||
$secret_key_verify_params = array('secret' => $secret_key);
|
||||
$secret_key_verify_url = esc_url(add_query_arg($secret_key_verify_params, $this->google_verify_recaptcha_url));
|
||||
$secret_key_verify_response_body = wp_remote_retrieve_body(wp_remote_get($secret_key_verify_url));
|
||||
$secret_key_verify_json = json_decode($secret_key_verify_response_body, true);
|
||||
|
||||
if (false !== strpos($site_key_verify_response_body, 'Invalid site key') || is_null($secret_key_verify_json) || (isset($secret_key_verify_json['error-codes']) && in_array('invalid-input-secret', $secret_key_verify_json['error-codes']))) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function adds captcha to contact form 7
|
||||
*
|
||||
* @param array $form_properties - this is the array containing properties for the form
|
||||
*
|
||||
* @return array $form_properties - containing the edited form with the captcha if everything is set
|
||||
*/
|
||||
public function add_contact_form_7_captcha($form_properties) {
|
||||
global $aio_wp_security;
|
||||
|
||||
if (!class_exists('WPCF7_RECAPTCHA') || is_admin()) return $form_properties; // if wpc7_recaptcha does not exist or the call is from the admin page
|
||||
|
||||
$recaptcha_service = WPCF7_RECAPTCHA::get_instance();
|
||||
|
||||
// if recaptcha is active return form
|
||||
if ($recaptcha_service->is_active()) return $form_properties;
|
||||
|
||||
$default_captcha = $aio_wp_security->configs->get_value('aiowps_default_captcha');
|
||||
|
||||
// check if default captcha configuration is correct
|
||||
if (!$this->verify_captcha_configuration($default_captcha)) {
|
||||
$aio_wp_security->debug_logger->log_debug("The captcha $default_captcha is not correctly configured", 4);
|
||||
return $form_properties;
|
||||
}
|
||||
$form = isset($form_properties['form']) ? $form_properties['form'] : '';
|
||||
if (empty($form)) return $form_properties;
|
||||
|
||||
// enqueue script
|
||||
wp_enqueue_script($default_captcha, $this->get_captcha_script_url($default_captcha), array());
|
||||
|
||||
$field = $this->display_captcha_form($default_captcha, true);
|
||||
$field .= "<span class='wpcf7-form-control-wrap' data-name='aiowps-captcha'></span>"; // add validation field for the captcha
|
||||
$captcha_shortcode = sprintf('[%s]', AIOWPSEC_CAPTCHA_SHORTCODE);
|
||||
|
||||
if (false !== strpos($form, $captcha_shortcode)) {
|
||||
$replacement_string = $captcha_shortcode;
|
||||
} else {
|
||||
$replacement_string = '[submit';
|
||||
// if shortcode doesn't exist in form then use the submit button as placement for the captcha
|
||||
if (false !== stripos($form, $replacement_string)) {
|
||||
$field .= $replacement_string;
|
||||
} else {
|
||||
$pattern = '/<input[^>]+type\s*=\s*(["\']?)submit\\1[^>]*>/i';
|
||||
|
||||
if (preg_match($pattern, $form, $matches)) {
|
||||
$field .= $matches[0];
|
||||
$replacement_string = $matches[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
// replace first occurence of replacement string
|
||||
$form = preg_replace('/' . preg_quote($replacement_string, '/') . '/', $field, $form, 1);
|
||||
$form_properties['form'] = $form;
|
||||
|
||||
return $form_properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* This verifies contact form 7 captcha
|
||||
*
|
||||
* @param WPCF7_Validation $result - This is the form result from contact form 7 plugin
|
||||
* @return WPCF7_Validation - The validation for a contact form 7 form
|
||||
*/
|
||||
public function verify_contact_form_7_captcha($result) {
|
||||
if (!class_exists('WPCF7_Submission') || !class_exists('WPCF7_RECAPTCHA')) return $result;
|
||||
|
||||
$recaptcha_service = WPCF7_RECAPTCHA::get_instance();
|
||||
// if recaptcha is active return result
|
||||
if ($recaptcha_service->is_active()) return $result;
|
||||
|
||||
$post = WPCF7_Submission::get_instance();
|
||||
$message = __('Your CAPTCHA answer was incorrect - please try again.', 'all-in-one-wp-security-and-firewall');
|
||||
|
||||
if (!empty($post)) {
|
||||
$data = $post->get_posted_data();
|
||||
$field_name = $this->get_contact_form_7_captcha_post_field_name();
|
||||
|
||||
if (empty($field_name) || false === array_key_exists($field_name, $data)) return $result; // if field name is empty or field doesn't exist return
|
||||
|
||||
if (empty($data[$field_name])) {
|
||||
$result->invalidate(array('type' => 'captcha', 'name' => 'aiowps-captcha'), $message);
|
||||
return $result;
|
||||
}
|
||||
|
||||
$verify = $this->verify_captcha_submit();
|
||||
if (!$verify) {
|
||||
$result->invalidate(array('type' => 'captcha', 'name' => 'aiowps-captcha'), $message);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function gets the field name for the captcha
|
||||
*
|
||||
* @return string - The field name for the CAPTCHA if the feature is activated, an empty string if it's not
|
||||
*/
|
||||
private function get_contact_form_7_captcha_post_field_name() {
|
||||
global $aio_wp_security;
|
||||
|
||||
$default_captcha = $aio_wp_security->configs->get_value('aiowps_default_captcha');
|
||||
$field = '';
|
||||
|
||||
switch ($default_captcha) {
|
||||
case 'cloudflare-turnstile':
|
||||
$field = 'cf-turnstile-response';
|
||||
break;
|
||||
case 'google-recaptcha-v2':
|
||||
$field = 'g-recaptcha-response';
|
||||
break;
|
||||
case 'simple-math':
|
||||
$field = 'aiowps-captcha-answer';
|
||||
break;
|
||||
}
|
||||
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* This checks if the default captcha is configured correctly
|
||||
*
|
||||
* @param string $default_captcha - the default CAPTCHA
|
||||
*
|
||||
* @return bool - True if the captcha configuration is correct, otherwise false.
|
||||
*/
|
||||
public function verify_captcha_configuration($default_captcha) {
|
||||
global $aio_wp_security;
|
||||
|
||||
if (empty($default_captcha)) return false;
|
||||
|
||||
$verify = true;
|
||||
|
||||
switch ($default_captcha) {
|
||||
case 'cloudflare-turnstile':
|
||||
$verify = $this->cloudflare_turnstile_verify_configuration($aio_wp_security->configs->get_value('aiowps_turnstile_site_key'), $aio_wp_security->configs->get_value('aiowps_turnstile_secret_key'));
|
||||
break;
|
||||
case 'google-recaptcha-v2':
|
||||
$verify = $this->google_recaptcha_verify_configuration($aio_wp_security->configs->get_value('aiowps_recaptcha_site_key'), $aio_wp_security->configs->get_value('aiowps_recaptcha_secret_key'));
|
||||
break;
|
||||
}
|
||||
|
||||
return $verify;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function adds the aiowps contact form 7 CAPTCHA
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add_contact_form_7_captcha_tag_generator_button() {
|
||||
if (!class_exists('WPCF7_TagGenerator')) return;
|
||||
|
||||
$tag_generator = WPCF7_TagGenerator::get_instance();
|
||||
$tag_generator->add('aios-captcha', sprintf(__('%s captcha', 'all-in-one-wp-security-and-firewall'), 'aios'), array($this, 'contact_form_7_tag_generator_button'), '');
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is the callback for adding the captcha tag
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function contact_form_7_tag_generator_button() {
|
||||
$type = AIOWPSEC_CAPTCHA_SHORTCODE;
|
||||
?>
|
||||
<div class="control-box">
|
||||
<fieldset>
|
||||
<legend><?php echo esc_html(sprintf(__("Generate a form-tag to use %s CAPTCHA", 'all-in-one-wp-security-and-firewall'), 'AIOS')); ?></legend>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="insert-box">
|
||||
<input type="text" name="<?php echo esc_attr($type); ?>" class="tag code" readonly="readonly" onfocus="this.select()" />
|
||||
<div class="submitbox">
|
||||
<input type="button" class="button button-primary insert-tag" value="<?php echo esc_attr(__('Insert tag', 'all-in-one-wp-security-and-firewall')); ?>" />
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the captcha on the MemberPress lost password form.
|
||||
*
|
||||
* This function checks if the captcha verification on the MemberPress lost password form was successful.
|
||||
* If the captcha is incorrect, an error message is added to the errors array.
|
||||
*
|
||||
* @param array $errors An array of error messages.
|
||||
* @return array The modified array of error messages.
|
||||
*/
|
||||
public function verify_memberpress_form($errors) {
|
||||
if (!$this->verify_captcha_submit()) {
|
||||
$errors[] = sprintf(__('%s: Your answer was incorrect - please try again.', 'all-in-one-wp-security-and-firewall'), '<strong>' . __('ERROR', 'all-in-one-wp-security-and-firewall') . '</strong>');
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;//Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* AIOWPSecurity_Cleanup class for clean up database etc.
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
class AIOWPSecurity_Cleanup {
|
||||
|
||||
/**
|
||||
* Class constructor added action
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action('aiowps_perform_db_cleanup_tasks', array($this, 'aiowps_scheduled_db_cleanup_handler'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up unnecessary old data from aiowps tables.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function aiowps_scheduled_db_cleanup_handler() {
|
||||
//Check the events table because this can grow quite large especially when 404 events are being logged
|
||||
$events_table_name = AIOWPSEC_TBL_EVENTS;
|
||||
$purge_events_records_after_days = AIOS_PURGE_EVENTS_RECORDS_AFTER_DAYS; //purge older records in the events table
|
||||
$purge_events_records_after_days = apply_filters('aios_purge_events_records_after_days', $purge_events_records_after_days);
|
||||
AIOWPSecurity_Utility::purge_table_records($events_table_name, $purge_events_records_after_days, 'created');
|
||||
|
||||
//Check the login lockout table
|
||||
$login_lockout_table_name = AIOWPSEC_TBL_LOGIN_LOCKOUT;
|
||||
$purge_login_lockout_records_after_days = AIOS_PURGE_LOGIN_LOCKOUT_RECORDS_AFTER_DAYS; //purge older records in the events table
|
||||
$purge_login_lockout_records_after_days = apply_filters('aios_purge_login_lockout_records_after_days', $purge_login_lockout_records_after_days);
|
||||
AIOWPSecurity_Utility::purge_table_records($login_lockout_table_name, $purge_login_lockout_records_after_days, 'created');
|
||||
|
||||
//Check the global meta table
|
||||
$global_meta_table_name = AIOWPSEC_TBL_GLOBAL_META_DATA;
|
||||
$purge_global_meta_records_after_days = AIOS_PURGE_GLOBAL_META_DATA_RECORDS_AFTER_DAYS; //purge older records in global meta table
|
||||
$purge_global_meta_records_after_days = apply_filters('aios_purge_global_meta_records_after_days', $purge_global_meta_records_after_days);
|
||||
AIOWPSecurity_Utility::purge_table_records($global_meta_table_name, $purge_global_meta_records_after_days, 'created');
|
||||
|
||||
//Delete any expired _aiowps_captcha_string_info_xxxx option
|
||||
AIOWPSecurity_Utility::delete_expired_captcha_options();
|
||||
//Keep adding other DB cleanup tasks as they arise...
|
||||
}
|
||||
}
|
||||
+423
@@ -0,0 +1,423 @@
|
||||
<?php
|
||||
|
||||
if (!defined('ABSPATH')) die('No direct access allowed');
|
||||
|
||||
if (class_exists('AIOWPSecurity_Commands')) return;
|
||||
|
||||
if (!trait_exists('AIOWPSecurity_Log_Commands_Trait')) require_once(AIO_WP_SECURITY_PATH.'/classes/commands/wp-security-log-commands.php');
|
||||
if (!trait_exists('AIOWPSecurity_Ip_Commands_Trait')) require_once(AIO_WP_SECURITY_PATH.'/classes/commands/wp-security-ip-commands.php');
|
||||
if (!trait_exists('AIOWPSecurity_Brute_Force_Commands_Trait')) require_once(AIO_WP_SECURITY_PATH.'/classes/commands/wp-brute-force-commands.php');
|
||||
if (!trait_exists('AIOWPSecurity_Comment_Commands_Trait')) require_once(AIO_WP_SECURITY_PATH.'/classes/commands/wp-security-comment-commands.php');
|
||||
if (!trait_exists('AIOWPSecurity_User_Security_Commands_Trait')) require_once(AIO_WP_SECURITY_PATH.'/classes/commands/wp-security-user-security-commands.php');
|
||||
if (!trait_exists('AIOWPSecurity_Settings_Commands_Trait')) require_once(AIO_WP_SECURITY_PATH.'/classes/commands/wp-security-settings-commands.php');
|
||||
if (!trait_exists('AIOWPSecurity_Files_Commands_Trait')) require_once(AIO_WP_SECURITY_PATH.'/classes/commands/wp-security-files-commands.php');
|
||||
if (!trait_exists('AIOWPSecurity_Firewall_Commands_Trait')) require_once(AIO_WP_SECURITY_PATH.'/classes/commands/wp-security-firewall-commands.php');
|
||||
if (!trait_exists('AIOWPSecurity_Tools_Commands_Trait')) require_once(AIO_WP_SECURITY_PATH.'/classes/commands/wp-security-tools-commands.php');
|
||||
if (!trait_exists('AIOWPSecurity_File_Scan_Commands_Trait')) require_once(AIO_WP_SECURITY_PATH.'/classes/commands/wp-security-file-scan-commands.php');
|
||||
if (!trait_exists('AIOWPSecurity_Tfa_Commands_Trait')) require_once(AIO_WP_SECURITY_PATH.'/classes/commands/wp-security-tfa-commands.php');
|
||||
class AIOWPSecurity_Commands {
|
||||
|
||||
use AIOWPSecurity_Log_Commands_Trait;
|
||||
use AIOWPSecurity_Ip_Commands_Trait;
|
||||
use AIOWPSecurity_Brute_Force_Commands_Trait;
|
||||
use AIOWPSecurity_Comment_Commands_Trait;
|
||||
use AIOWPSecurity_User_Security_Commands_Trait;
|
||||
use AIOWPSecurity_Settings_Commands_Trait;
|
||||
use AIOWPSecurity_Files_Commands_Trait;
|
||||
use AIOWPSecurity_Firewall_Commands_Trait;
|
||||
use AIOWPSecurity_Tools_Commands_Trait;
|
||||
use AIOWPSecurity_File_Scan_Commands_Trait;
|
||||
use AIOWPSecurity_Tfa_Commands_Trait;
|
||||
|
||||
/**
|
||||
* This variable holds an instance of AIOWPSecurity_Feature_Item_Manager.
|
||||
*
|
||||
* @var AIOWPSecurity_Feature_Item_Manager $aiowps_feature_mgr
|
||||
*/
|
||||
private $aiowps_feature_mgr;
|
||||
|
||||
/**
|
||||
* The initializes the AIOWPS Feature Manager
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function feature_mgr_init() {
|
||||
static $initialized = false;
|
||||
if ($initialized && !empty($this->aiowps_feature_mgr)) return true;
|
||||
|
||||
$this->aiowps_feature_mgr = new AIOWPSecurity_Feature_Item_Manager();
|
||||
|
||||
$initialized = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the feature manager object.
|
||||
*
|
||||
* This method initializes the feature manager if necessary and returns the
|
||||
* AIOWPSecurity_Feature_Item_Manager instance. If the initialization fails or
|
||||
* the feature manager object is empty, it returns a WP_Error.
|
||||
*
|
||||
* @return AIOWPSecurity_Feature_Item_Manager|WP_Error
|
||||
*/
|
||||
private function get_feature_mgr_object() {
|
||||
|
||||
$do_init = $this->feature_mgr_init();
|
||||
|
||||
if (true === $do_init && !empty($this->aiowps_feature_mgr)) return $this->aiowps_feature_mgr;
|
||||
|
||||
return new WP_Error('not_initialized', __('The feature item manager could not be initialized.', 'all-in-one-wp-security-and-firewall'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get IP address of given method.
|
||||
*
|
||||
* @param array $data - the request data
|
||||
*
|
||||
* @return array|WP_Error - an array response or a WP_Error if there was an error
|
||||
*/
|
||||
public function get_ip_address_of_given_method($data) {
|
||||
$ip_method_id = $data['ip_retrieve_method'];
|
||||
$ip_retrieve_methods = AIOS_Abstracted_Ids::get_ip_retrieve_methods();
|
||||
if (isset($ip_retrieve_methods[$ip_method_id])) {
|
||||
return array(
|
||||
'ip_address' => isset($_SERVER[$ip_retrieve_methods[$ip_method_id]]) ? sanitize_text_field(wp_unslash($_SERVER[$ip_retrieve_methods[$ip_method_id]])) : '',
|
||||
);
|
||||
} else {
|
||||
return new WP_Error('aios-invalid-ip-retrieve-method', __('Invalid IP retrieve method.', 'all-in-one-wp-security-and-firewall'));
|
||||
}
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss a notice
|
||||
*
|
||||
* @param array $data - the request data contains the notice to dismiss
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function dismiss_notice($data) {
|
||||
global $aio_wp_security;
|
||||
|
||||
$time_now = $aio_wp_security->notices->get_time_now();
|
||||
|
||||
if (in_array($data['notice'], array('dismissdashnotice', 'dismiss_season'))) {
|
||||
$aio_wp_security->configs->set_value($data['notice'], $time_now + (366 * 86400));
|
||||
} elseif (in_array($data['notice'], array('dismiss_page_notice_until', 'dismiss_notice'))) {
|
||||
$aio_wp_security->configs->set_value($data['notice'], $time_now + (84 * 86400));
|
||||
} elseif ('dismiss_review_notice' == $data['notice']) {
|
||||
if (empty($data['dismiss_forever'])) {
|
||||
$aio_wp_security->configs->set_value($data['notice'], $time_now + (84 * 86400));
|
||||
} else {
|
||||
$aio_wp_security->configs->set_value($data['notice'], $time_now + (100 * 365.25 * 86400));
|
||||
}
|
||||
} elseif ('dismiss_automated_database_backup_notice' == $data['notice']) {
|
||||
$aio_wp_security->delete_automated_backup_configs();
|
||||
} elseif ('dismiss_ip_retrieval_settings_notice' == $data['notice']) {
|
||||
$aio_wp_security->configs->set_value($data['notice'], 1);
|
||||
} elseif ('dismiss_ip_retrieval_settings_notice' == $data['notice']) {
|
||||
$aio_wp_security->configs->set_value('aiowps_is_login_whitelist_disabled_on_upgrade', 1);
|
||||
} elseif ('dismiss_login_whitelist_disabled_on_upgrade_notice' == $data['notice']) {
|
||||
if (isset($data['turn_it_back_on']) && '1' == $data['turn_it_back_on']) {
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_whitelisting', '1');
|
||||
}
|
||||
$aio_wp_security->configs->delete_value('aiowps_is_login_whitelist_disabled_on_upgrade');
|
||||
} elseif ('dismiss_ip_blacklist_notice' == $data['notice']) {
|
||||
if (isset($data['turn_it_back_on']) && '1' == $data['turn_it_back_on']) {
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_blacklisting', '1');
|
||||
AIOWPSecurity_Configure_Settings::set_blacklist_ip_firewall_configs();
|
||||
AIOWPSecurity_Configure_Settings::set_user_agent_firewall_configs();
|
||||
}
|
||||
$aio_wp_security->configs->delete_value('aiowps_is_ip_blacklist_settings_notice_on_upgrade');
|
||||
} elseif ('dismiss_firewall_settings_disabled_on_upgrade_notice' == $data['notice']) {
|
||||
$is_reactivated = (isset($data['turn_it_back_on']) && '1' == $data['turn_it_back_on']);
|
||||
if ($is_reactivated) {
|
||||
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
|
||||
$active_settings = $aio_wp_security->configs->get_value('aiowps_firewall_active_upgrade');
|
||||
|
||||
if (!empty($active_settings)) {
|
||||
$active_settings = json_decode($active_settings);
|
||||
if (!empty($active_settings)) {
|
||||
foreach ($active_settings as $setting) {
|
||||
$aiowps_firewall_config->set_value($setting, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$aio_wp_security->configs->delete_value('aiowps_firewall_active_upgrade');
|
||||
} elseif ('php_56_eol_dismiss_forever' == $data['notice']) {
|
||||
$aio_wp_security->configs->set_value('php_56_eol_dismiss_forever', $time_now + (100 * 365.25 * 86400));
|
||||
}
|
||||
|
||||
|
||||
$aio_wp_security->configs->save_config();
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is a helper function to save settings options using key/value pairs
|
||||
*
|
||||
* @param array $options - An array of options to save to the config
|
||||
* @param null $callback - A callback function to call when the options are saved
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function save_settings($options, $callback = null) {
|
||||
global $aio_wp_security;
|
||||
|
||||
$aiowps_feature_mgr = $this->get_feature_mgr_object();
|
||||
if (is_wp_error($aiowps_feature_mgr)) return false;
|
||||
|
||||
|
||||
foreach ($options as $key => $value) {
|
||||
$aio_wp_security->configs->set_value($key, $value);
|
||||
}
|
||||
//commit the config changes
|
||||
$aio_wp_security->configs->save_config();
|
||||
|
||||
if (is_callable($callback)) {
|
||||
call_user_func($callback, $options);
|
||||
}
|
||||
|
||||
$aiowps_feature_mgr->calculate_total_feature_points();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a helper function to get the output feature details badge
|
||||
*
|
||||
* @param string $feature_id - the id of the feature we want to get the badge for
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_feature_details_badge($feature_id) {
|
||||
$aiowps_feature_mgr = $this->get_feature_mgr_object();
|
||||
if (is_wp_error($aiowps_feature_mgr)) return '';
|
||||
return $aiowps_feature_mgr->output_feature_details_badge($feature_id, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the IDs and HTML content for features.
|
||||
*
|
||||
* This method processes an array of features and returns an associative array containing
|
||||
* the IDs and corresponding HTML content for each feature badge.
|
||||
*
|
||||
* @param array $features - An array containing the features to retrieve IDs and HTML for.
|
||||
*
|
||||
* @return array An associative array containing the IDs and HTML content for each feature badge.
|
||||
*/
|
||||
public function get_features_id_and_html($features) {
|
||||
$result = array();
|
||||
foreach ($features as $feature) {
|
||||
$result[] = array(
|
||||
'id' => '#' . $feature . '-badge',
|
||||
'html' => $this->get_feature_details_badge($feature)
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares and returns a structured response for AJAX commands.
|
||||
*
|
||||
* @param bool $success Indicates whether the operation was successful (true for success, false for failure).
|
||||
* @param string|bool $message The message to include in the response (optional).
|
||||
* If false, no message is passed with the response.
|
||||
* If empty string, it defaults to a success or error message based on the $success flag.
|
||||
* @param array $args Optional. An associative array of additional response data, such as badges, info, values, or content.
|
||||
*
|
||||
* @return array The constructed response array containing status, message, and any additional data from $args.
|
||||
*/
|
||||
public function handle_response($success, $message = '', $args = array()) {
|
||||
$response = array(
|
||||
'status' => $success ? 'success' : 'error',
|
||||
);
|
||||
|
||||
if (false !== $message) {
|
||||
$response['message'] = $this->get_message($success, $message);
|
||||
}
|
||||
|
||||
$allowed_keys = array('badges', 'info', 'values', 'content', 'extra_args');
|
||||
foreach ($allowed_keys as $key) {
|
||||
if (!empty($args[$key])) {
|
||||
$response[$key] = 'badges' === $key ? $this->get_features_id_and_html($args[$key]) : $args[$key];
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the appropriate message based on success flag and provided message.
|
||||
*
|
||||
* @param bool $success Indicates whether the operation was successful.
|
||||
* @param string $message The provided message.
|
||||
*
|
||||
* @return string The final message to be used in the response.
|
||||
*/
|
||||
private function get_message($success, $message) {
|
||||
if ('' === $message) {
|
||||
return $success ? __('The settings have been successfully updated.', 'all-in-one-wp-security-and-firewall') : __('The settings update was unsuccessful.', 'all-in-one-wp-security-and-firewall');
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get antibot keys for the spam detection
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_antibot_keys() {
|
||||
global $aio_wp_security;
|
||||
|
||||
$response = array(
|
||||
'status' => 'success',
|
||||
'data' => array(),
|
||||
);
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- PCP warning. It is the nonce.
|
||||
$nonce = empty($_POST['nonce']) ? '' : sanitize_key(wp_unslash($_POST['nonce']));
|
||||
if (!wp_verify_nonce($nonce, 'wp-security-ajax-nonce')) {
|
||||
$response['status'] = false;
|
||||
$response['error_code'] = 'invalid_nonce';
|
||||
$response['error_message'] = 'Invalid nonce (wp-security-ajax-nonce) provided for this action.';
|
||||
} else {
|
||||
$key_map_arr = AIOWPSecurity_Comment::generate_antibot_keys(true);
|
||||
$response['data'] = $key_map_arr[0];
|
||||
if ('1' == $aio_wp_security->configs->get_value('aiowps_spambot_detect_usecookies')) {
|
||||
AIOWPSecurity_Comment::insert_antibot_keys_in_cookie();
|
||||
}
|
||||
}
|
||||
|
||||
echo wp_json_encode($response);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will send the diagnostic report email
|
||||
*
|
||||
* @param array $data - the request data
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function send_report_email($data) {
|
||||
global $aio_wp_security;
|
||||
|
||||
// Sanitize the email address first.
|
||||
$sanitized_email = !empty($data['report_email']) ? sanitize_email($data['report_email']) : '';
|
||||
|
||||
if ('' === $sanitized_email || !is_email($sanitized_email)) {
|
||||
return array(
|
||||
'status' => 'error',
|
||||
'message' => __('Invalid email address.', 'all-in-one-wp-security-and-firewall'),
|
||||
);
|
||||
}
|
||||
|
||||
$result = $aio_wp_security->debug_obj->send_report($sanitized_email, wp_kses_post(html_entity_decode($data['report_sections'])));
|
||||
|
||||
if ($result) {
|
||||
return array(
|
||||
'status' => 'success',
|
||||
'message' => __('The diagnostic report has been sent successfully.', 'all-in-one-wp-security-and-firewall'),
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'status' => 'error',
|
||||
'message' => __('There was an error sending the diagnostic report.', 'all-in-one-wp-security-and-firewall'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of translations used by the AIOS plugin. Primarily used for UpdraftCentral consumption.
|
||||
*
|
||||
* @param array $params - The parameters passed to the function.
|
||||
*
|
||||
* @return array - The AIOS translations array
|
||||
*/
|
||||
public function get_js_translation($params) {
|
||||
$translations = array();
|
||||
|
||||
if (isset($params['return_formatted_response'])) {
|
||||
return array(
|
||||
'error' => false,
|
||||
'data' => array(
|
||||
'translations' => $translations,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $translations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get multiple widgets data. Primarily used for UpdraftCentral consumption.
|
||||
*
|
||||
* @param array $args - The arguments containing the widgets to retrieve data for.
|
||||
*
|
||||
* @return array - An array containing error status and data for each widget.
|
||||
*/
|
||||
public function get_widgets_data($args) {
|
||||
// Widgets.
|
||||
$widgets = isset($args['widgets']) ? $args['widgets'] : array();
|
||||
|
||||
// Return early if no widgets supplied.
|
||||
if (!is_array($widgets) || empty($widgets)) {
|
||||
return array('error' => false, 'data' => array());
|
||||
}
|
||||
|
||||
// Get the data for each widget.
|
||||
$data = array();
|
||||
|
||||
// Loop through the widgets and get their data.
|
||||
foreach ($widgets as $widget) {
|
||||
$method = 'get_' . $widget . '_data';
|
||||
|
||||
if (method_exists($this, $method)) {
|
||||
$data[$widget] = $this->$method($args);
|
||||
}
|
||||
}
|
||||
|
||||
return array('error' => false, 'data' => $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the AIOS premium upsell data for UDC widget.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_is_premium_data() {
|
||||
$is_premium = AIOWPSecurity_Utility_Permissions::is_premium_installed();
|
||||
$upgrade_to_premium_data = array();
|
||||
|
||||
if (false === $is_premium) {
|
||||
$upgrade_to_premium_data = array(
|
||||
'heading' => __('AIOS premium', 'all-in-one-wp-security-and-firewall'),
|
||||
'checklist' => array(
|
||||
__('Advanced malware scanning', 'all-in-one-wp-security-and-firewall'),
|
||||
__('Real-time response time monitoring', 'all-in-one-wp-security-and-firewall'),
|
||||
__('Custom two-factor authentication', 'all-in-one-wp-security-and-firewall'),
|
||||
__('404 error protection & bot blocking', 'all-in-one-wp-security-and-firewall'),
|
||||
__('Country-based traffic blocking', 'all-in-one-wp-security-and-firewall'),
|
||||
__('Country whitelist management', 'all-in-one-wp-security-and-firewall'),
|
||||
__('Guaranteed premium support', 'all-in-one-wp-security-and-firewall'),
|
||||
),
|
||||
'cta' => array(
|
||||
'text' => __('Upgrade Now', 'all-in-one-wp-security-and-firewall'),
|
||||
'url' => 'https://teamupdraft.com/all-in-one-security/pricing/',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'is_premium' => $is_premium,
|
||||
'upgrade_to_premium_data' => $upgrade_to_premium_data,
|
||||
);
|
||||
}
|
||||
}
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;//Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles Comment related hooks.
|
||||
*/
|
||||
class AIOWPSecurity_Comment {
|
||||
|
||||
/**
|
||||
* Class constructor. Add action hooks.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter('pre_comment_user_ip', array($this, 'pre_comment_user_ip'));
|
||||
add_action('comment_spam_to_approved', array($this, 'comment_spam_status_change'));
|
||||
add_action('comment_spam_to_unapproved', array($this, 'comment_spam_status_change'));
|
||||
add_action('aios_perform_update_antibot_keys', array($this, 'update_antibot_keys'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set comment user IP for local server setup.
|
||||
*
|
||||
* @param string $comment_user_ip comment user IP.
|
||||
* @return string Comment user IP.
|
||||
*/
|
||||
public function pre_comment_user_ip($comment_user_ip) {
|
||||
if (in_array($comment_user_ip, array('', '127.0.0.1', '::1'))) {
|
||||
$external_ip_address = AIOS_Helper::get_external_ip_address();
|
||||
if (false != $external_ip_address) {
|
||||
$comment_user_ip = $external_ip_address;
|
||||
}
|
||||
}
|
||||
return $comment_user_ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move spam comments to trash.
|
||||
*/
|
||||
public static function trash_spam_comments() {
|
||||
global $aio_wp_security;
|
||||
if ('1' == $aio_wp_security->configs->get_value('aiowps_enable_trash_spam_comments') && absint($aio_wp_security->configs->get_value('aiowps_trash_spam_comments_after_days'))) {
|
||||
$date_before = absint($aio_wp_security->configs->get_value('aiowps_trash_spam_comments_after_days')).' days ago';
|
||||
$comment_ids = get_comments(array(
|
||||
'fields' => 'ids',
|
||||
'status' => 'spam',
|
||||
'date_query' => array(
|
||||
array(
|
||||
'before' => $date_before,
|
||||
'inclusive' => true,
|
||||
),
|
||||
)
|
||||
));
|
||||
|
||||
if (!empty($comment_ids)) {
|
||||
foreach ($comment_ids as $comment_id) {
|
||||
wp_trash_comment($comment_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete ip from aiowps_permanent_block table once the comment's spam status changed.
|
||||
*
|
||||
* @param object $comment_data comment object.
|
||||
*/
|
||||
public function comment_spam_status_change($comment_data) {
|
||||
global $wpdb, $aio_wp_security;
|
||||
$comment_ip = $comment_data->comment_author_IP;
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery -- Direct query necessary. No caching required.
|
||||
$total_spam_comment = $wpdb->get_var(
|
||||
$wpdb->prepare("SELECT COUNT(*) FROM {$wpdb->comments} WHERE comment_author_IP = %s AND comment_approved = 'spam'", $comment_ip)
|
||||
);
|
||||
$min_comment_before_block = $aio_wp_security->configs->get_value('aiowps_spam_ip_min_comments_block');
|
||||
if ($total_spam_comment < $min_comment_before_block) {
|
||||
$where = array('blocked_ip' => $comment_ip, 'block_reason' => 'spam');
|
||||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery -- Direct query necessary. No caching required.
|
||||
$wpdb->delete(AIOWPSEC_TBL_PERM_BLOCK, $where, array('%s'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if comment posted by spambot.
|
||||
*
|
||||
* @return boolean.
|
||||
*/
|
||||
public static function is_comment_spam_detected() {
|
||||
$return = false;
|
||||
if (!is_user_logged_in()) {
|
||||
if (empty($_SERVER['HTTP_REFERER']) || false === stristr(sanitize_url(wp_unslash($_SERVER['HTTP_REFERER'])), wp_parse_url(home_url(), PHP_URL_HOST)) || empty($_SERVER['HTTP_USER_AGENT'])) {
|
||||
$return = true;
|
||||
} elseif (self::is_bot_detected()) {
|
||||
$return = true;
|
||||
}
|
||||
}
|
||||
return apply_filters('aiowps_is_comment_spam_detected', $return);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if bot posted comment form based on form posted fields and cookie values
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_bot_detected() {
|
||||
global $aio_wp_security;
|
||||
$return = false;
|
||||
$key_map_arr = self::generate_antibot_keys();
|
||||
foreach ($key_map_arr[0] as $key) {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing -- PCP warning. Nonce checked in earlier function.
|
||||
if (empty($_POST[$key[0]]) || sanitize_text_field(wp_unslash($_POST[$key[0]])) != $key[1]) {
|
||||
$return = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$return && '1' == $aio_wp_security->configs->get_value('aiowps_spambot_detect_usecookies')) {
|
||||
foreach ($key_map_arr[1] as $key) {
|
||||
if (AIOWPSecurity_Utility::get_cookie_value($key[0]) != $key[1]) {
|
||||
$return = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return apply_filters('aios_is_bot_detected', $return);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cookies in browser for antibot check
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function insert_antibot_keys_in_cookie() {
|
||||
$key_map_arr = self::generate_antibot_keys();
|
||||
$expiry_seconds = AIOS_UPDATE_ANTIBOT_KEYS_AFTER_DAYS * 86400;
|
||||
if (!empty($key_map_arr[1])) {
|
||||
foreach ($key_map_arr[1] as $key) {
|
||||
AIOWPSecurity_Utility::set_cookie_value($key[0], $key[1], $expiry_seconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Comment Form to post back hidden fields for antibot check
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function insert_antibot_keys_in_comment_form() {
|
||||
$html_antibot_hidden_fields = '<p class="comment-form-aios-antibot-keys">%1$s</p>';
|
||||
$antibot_hidden_fields = '';
|
||||
$key_map_arr = self::generate_antibot_keys();
|
||||
foreach ($key_map_arr[0] as $key) {
|
||||
$antibot_hidden_fields .='<input type="hidden" name="' . esc_attr($key[0]) . '" value="'.esc_attr($key[1]).'" >';
|
||||
}
|
||||
if (isset($key_map_arr[2])) {
|
||||
$antibot_hidden_fields .='<input type="hidden" name="aios_antibot_keys_expiry" id="aios_antibot_keys_expiry" value="'.esc_attr($key_map_arr[2]).'">';
|
||||
}
|
||||
wp_register_script('aios-front-js', AIO_WP_SECURITY_URL. '/js/wp-security-front-script.js', array('jquery'), AIO_WP_SECURITY_VERSION, true);
|
||||
wp_enqueue_script('aios-front-js');
|
||||
wp_localize_script('aios-front-js', 'AIOS_FRONT', array(
|
||||
'ajaxurl' => admin_url('admin-ajax.php'), // URL to wp-admin/admin-ajax.php to process the request
|
||||
'ajax_nonce' => wp_create_nonce('wp-security-ajax-nonce'),
|
||||
));
|
||||
$html_antibot_hidden_fields = sprintf($html_antibot_hidden_fields, $antibot_hidden_fields);
|
||||
return $html_antibot_hidden_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get antibot key-value pairs to check on post back
|
||||
*
|
||||
* @param boolean $update generate and save in database
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function generate_antibot_keys($update = false) {
|
||||
$key_map_arr = get_site_option('aios_antibot_key_map_info');
|
||||
if (!$update && is_array($key_map_arr)) {
|
||||
return $key_map_arr;
|
||||
}
|
||||
if ($update && is_array($key_map_arr) && isset($key_map_arr[2]) && $key_map_arr[2] > time()) {
|
||||
return $key_map_arr;
|
||||
}
|
||||
$key_map_arr = array();
|
||||
|
||||
// values for to check post back key
|
||||
$max = wp_rand(2, 4);
|
||||
for ($i = 1; $i <= $max; $i++) {
|
||||
$string1 = AIOWPSecurity_Utility::generate_alpha_numeric_random_string(8);
|
||||
$string2 = AIOWPSecurity_Utility::generate_alpha_numeric_random_string(12);
|
||||
$key_map_arr[0][] = array($string1, $string2);
|
||||
}
|
||||
|
||||
// values for to check for cookie back key
|
||||
$max = wp_rand(2, 4);
|
||||
for ($i = 1; $i <= $max; $i++) {
|
||||
$string1 = AIOWPSecurity_Utility::generate_alpha_numeric_random_string(8);
|
||||
$string2 = AIOWPSecurity_Utility::generate_alpha_numeric_random_string(12);
|
||||
$key_map_arr[1][] = array($string1, $string2);
|
||||
}
|
||||
// expiration time of keys
|
||||
$current_time = time();
|
||||
$key_map_arr[2] = (($current_time - ($current_time % 86400)) + AIOS_UPDATE_ANTIBOT_KEYS_AFTER_DAYS * 86400);
|
||||
update_site_option('aios_antibot_key_map_info', $key_map_arr);
|
||||
return $key_map_arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update antibot key-value pairs to rotate values so it is not valid forever
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function update_antibot_keys() {
|
||||
if ((intval(gmdate('z')) % AIOS_UPDATE_ANTIBOT_KEYS_AFTER_DAYS) == 0) self::generate_antibot_keys(true);
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;//Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Config {
|
||||
|
||||
public $configs;
|
||||
|
||||
public static $_this;
|
||||
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
public function load_config() {
|
||||
$this->configs = get_option('aio_wp_security_configs');
|
||||
}
|
||||
|
||||
public function get_value($key) {
|
||||
return isset($this->configs[$key]) ? $this->configs[$key] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets for main site config value for a given $key.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return string|array
|
||||
*/
|
||||
public function get_site_value($key) {
|
||||
if (is_multisite() && !is_main_site()) {
|
||||
$mainsite_aio_config = get_blog_option(get_main_site_id(), 'aio_wp_security_configs');
|
||||
return isset($mainsite_aio_config[$key]) ? $mainsite_aio_config[$key] : '';
|
||||
} else {
|
||||
return $this->get_value($key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a given config $value for a given $key.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @param boolean $save_config - Whether or not to also save the $configs array to the database.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function set_value($key, $value, $save_config = false) {
|
||||
$this->configs[$key] = $value;
|
||||
|
||||
if ($save_config) {
|
||||
return $this->save_config();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function add_value($key, $value) {
|
||||
if (!is_array($this->configs)) {
|
||||
$this->configs = array();
|
||||
}
|
||||
|
||||
if (array_key_exists($key, $this->configs)) {
|
||||
//Don't update the value for this key
|
||||
} else {//It is safe to update the value for this key
|
||||
$this->configs[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save configuration that are set.
|
||||
*
|
||||
* @return boolean True on save config, Otherwise false.
|
||||
*/
|
||||
public function save_config() {
|
||||
return update_option('aio_wp_security_configs', $this->configs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove key element from config.
|
||||
*
|
||||
* @param String $key config key
|
||||
*
|
||||
* @return boolean True if removed, otherwise false.
|
||||
*/
|
||||
public function delete_value($key) {
|
||||
if (!is_array($this->configs)) {
|
||||
$this->configs = array();
|
||||
}
|
||||
|
||||
if (array_key_exists($key, $this->configs)) {
|
||||
unset($this->configs[$key]);
|
||||
if (!isset($this->configs[$key])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function get_instance() {
|
||||
if (empty(self::$_this)) {
|
||||
self::$_this = new AIOWPSecurity_Config();
|
||||
self::$_this->load_config();
|
||||
return self::$_this;
|
||||
}
|
||||
return self::$_this;
|
||||
}
|
||||
}
|
||||
Executable
+739
@@ -0,0 +1,739 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;//Exit if accessed directly
|
||||
}
|
||||
|
||||
use AIOWPS\Firewall\Allow_List;
|
||||
|
||||
class AIOWPSecurity_Configure_Settings {
|
||||
|
||||
/**
|
||||
* Set default settings.
|
||||
*
|
||||
* @return boolean True if the settings options was updated, false otherwise.
|
||||
*/
|
||||
public static function set_default_settings() {
|
||||
global $aio_wp_security;
|
||||
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
|
||||
|
||||
$blog_email_address = get_bloginfo('admin_email'); // Get the blog admin email address - we will use as the default value
|
||||
|
||||
//Debug
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_debug', '');//Checkbox
|
||||
|
||||
//PHP backtrace
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_php_backtrace_in_email', '');//Checkbox
|
||||
|
||||
//WP Generator Meta Tag feature
|
||||
$aio_wp_security->configs->set_value('aiowps_remove_wp_generator_meta_info', '');//Checkbox
|
||||
|
||||
//Prevent Image Hotlinks
|
||||
$aio_wp_security->configs->set_value('aiowps_prevent_hotlinking', '');//Checkbox
|
||||
//General Settings Page
|
||||
|
||||
//User password feature
|
||||
|
||||
//Lockout feature
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_login_lockdown', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_allow_unlock_requests', '1'); // Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_max_login_attempts', '3');
|
||||
$aio_wp_security->configs->set_value('aiowps_retry_time_period', '5');
|
||||
$aio_wp_security->configs->set_value('aiowps_lockout_time_length', '5');
|
||||
$aio_wp_security->configs->set_value('aiowps_max_lockout_time_length', '60');
|
||||
$aio_wp_security->configs->set_value('aiowps_set_generic_login_msg', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_email_notify', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_email_address', $blog_email_address);//text field
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_forced_logout', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_logout_time_period', '60');
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_invalid_username_lockdown', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_instantly_lockout_specific_usernames', array()); // Textarea (list of strings)
|
||||
$aio_wp_security->configs->set_value('aiowps_unlock_request_secret_key', AIOWPSecurity_Utility::generate_alpha_numeric_random_string(20));//Hidden secret value which will be used to do some unlock request processing. This will be assigned a random string generated when lockdown settings saved
|
||||
$aio_wp_security->configs->set_value('aiowps_lockdown_enable_whitelisting', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_lockdown_allowed_ip_addresses', '');
|
||||
|
||||
// HTTP authentication
|
||||
$aio_wp_security->configs->set_value('aiowps_http_authentication_admin', ''); // Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_http_authentication_frontend', ''); // Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_http_authentication_username', 'root');
|
||||
$aio_wp_security->configs->set_value('aiowps_http_authentication_password', 'password');
|
||||
$aio_wp_security->configs->set_value('aiowps_http_authentication_failure_message', '<h1>Unauthorized</h1>');
|
||||
|
||||
// CAPTCHA feature
|
||||
$aio_wp_security->configs->set_value('aiowps_default_captcha', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_login_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_custom_login_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_password_protected_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_woo_login_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_woo_lostpassword_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_woo_register_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_woo_checkout_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_lost_password_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_contact_form_7_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_captcha_secret_key', AIOWPSecurity_Utility::generate_alpha_numeric_random_string(20)); // Hidden secret value which will be used to do some CAPTCHA processing. This will be assigned a random string generated when CAPTCHA settings saved
|
||||
|
||||
//Login Whitelist feature
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_whitelisting', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_allowed_ip_addresses', '');
|
||||
|
||||
//User registration
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_manual_registration_approval', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_registration_page_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_registration_honeypot', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enforce_strong_password', '');//Checkbox
|
||||
|
||||
//DB Security feature
|
||||
//$aio_wp_security->configs->set_value('aiowps_new_manual_db_pefix', ''); //text field
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_random_prefix', '');//Checkbox
|
||||
|
||||
//Filesystem Security feature
|
||||
AIOWPSecurity_Utility::enable_file_edits();
|
||||
$aio_wp_security->configs->set_value('aiowps_disable_file_editing', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_prevent_default_wp_file_access', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_auto_delete_default_wp_files', ''); // Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_system_log_file', 'error_log');
|
||||
|
||||
//Blacklist feature
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_blacklisting', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_banned_ip_addresses', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_banned_user_agents', '');
|
||||
|
||||
//Firewall features
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_basic_firewall', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_max_file_upload_size', AIOS_FIREWALL_MAX_FILE_UPLOAD_LIMIT_MB); //Default
|
||||
$aio_wp_security->configs->set_value('aiowps_disable_xmlrpc_pingback_methods', '');//Checkbox - Disables only pingback methods in XMLRPC functionality
|
||||
$aio_wp_security->configs->set_value('aiowps_disable_rss_and_atom_feeds', ''); // Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_block_debug_log_file_access', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_disable_index_views', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_disable_trace_and_track', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_5g_firewall', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_6g_firewall', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_custom_rules', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_place_custom_rules_at_top', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_custom_rules', '');
|
||||
|
||||
// Upgrade unsafe HTTP calls
|
||||
$aio_wp_security->configs->set_value('aiowps_upgrade_unsafe_http_calls', ''); // Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_upgrade_unsafe_http_calls_url_exceptions', '');
|
||||
|
||||
//404 detection
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_404_logging', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_404_IP_lockout', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_404_lockout_time_length', '60');
|
||||
$aio_wp_security->configs->set_value('aiowps_404_lock_redirect_url', 'http://127.0.0.1');
|
||||
|
||||
//Brute Force features
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_rename_login_page', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_login_honeypot', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_disable_application_password', '');//Checkbox
|
||||
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_brute_force_attack_prevention', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_brute_force_secret_word', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_cookie_brute_test', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_cookie_based_brute_force_redirect_url', 'http://127.0.0.1');
|
||||
$aio_wp_security->configs->set_value('aiowps_brute_force_attack_prevention_pw_protected_exception', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_brute_force_attack_prevention_ajax_exception', '');//Checkbox
|
||||
|
||||
//Maintenance menu - Visitor lockout feature
|
||||
$aio_wp_security->configs->set_value('aiowps_site_lockout', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_site_lockout_msg', '');//Text area/msg box
|
||||
|
||||
// Spam prevention menu
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_comment_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_autoblock_spam_ip', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_spam_ip_min_comments_block', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_bp_register_captcha', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_bbp_new_topic_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_spambot_detecting', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_spambot_detect_usecookies', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_spam_comments_should', '');
|
||||
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_trash_spam_comments', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_trash_spam_comments_after_days', '14');
|
||||
|
||||
//Filescan features
|
||||
//File change detection feature
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_automated_fcd_scan', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_fcd_scan_frequency', '4');
|
||||
$aio_wp_security->configs->set_value('aiowps_fcd_scan_interval', '2'); //Dropdown box where (0,1,2) => (hours,days,weeks)
|
||||
$aio_wp_security->configs->set_value('aiowps_fcd_exclude_filetypes', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_fcd_exclude_files', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_send_fcd_scan_email', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_fcd_scan_email_address', $blog_email_address);
|
||||
$aio_wp_security->configs->set_value('aiowps_fcds_change_detected', false); //used to display a global alert on site when file change detected
|
||||
|
||||
//Misc Options
|
||||
//Copy protection feature
|
||||
$aio_wp_security->configs->set_value('aiowps_copy_protection', '');//Checkbox
|
||||
//Prevent others from displaying your site in iframe
|
||||
$aio_wp_security->configs->set_value('aiowps_prevent_site_display_inside_frame', '');//Checkbox
|
||||
//Prevent users enumeration
|
||||
$aio_wp_security->configs->set_value('aiowps_prevent_users_enumeration', '');//Checkbox
|
||||
|
||||
//REST API Security
|
||||
$aio_wp_security->configs->set_value('aiowps_disallow_unauthorized_rest_requests', '');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aios_roles_disallowed_rest_requests', array());
|
||||
$aio_wp_security->configs->set_value('aios_whitelisted_rest_routes', array());
|
||||
|
||||
// IP retrieval setting
|
||||
$aio_wp_security->configs->set_value('aiowps_ip_retrieve_method', '0'); // Default is $_SERVER['REMOTE_ADDR']
|
||||
|
||||
// Cloudflare Turnstile
|
||||
$aio_wp_security->configs->set_value('aiowps_turnstile_site_key', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_turnstile_secret_key', '');
|
||||
|
||||
// Google reCAPTCHA
|
||||
$aio_wp_security->configs->set_value('aiowps_recaptcha_site_key', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_recaptcha_secret_key', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_default_recaptcha', ''); // Not used since 5.1.2
|
||||
|
||||
// Deactivation Handler
|
||||
$aio_wp_security->configs->set_value('aiowps_on_uninstall_delete_db_tables', '1'); //Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_on_uninstall_delete_configs', '1'); //Checkbox
|
||||
|
||||
// Reset the PHP 5.6 end of support notice
|
||||
$aio_wp_security->configs->delete_value('php_56_eol_dismiss_forever');
|
||||
|
||||
//TODO - keep adding default options for any fields that require it
|
||||
|
||||
if (is_main_site()) {
|
||||
$aiowps_firewall_config->set_value('aiowps_enable_pingback_firewall', false);//Checkbox - blocks all access to XMLRPC
|
||||
$aiowps_firewall_config->set_value('aiowps_forbid_proxy_comments', false);//Checkbox
|
||||
$aiowps_firewall_config->set_value('aiowps_deny_bad_query_strings', false);//Checkbox
|
||||
$aiowps_firewall_config->set_value('aiowps_advanced_char_string_filter', false);//Checkbox
|
||||
$aiowps_firewall_config->set_value('aiowps_ban_post_blank_headers', false); // Checkbox
|
||||
$aiowps_firewall_config->set_value('aiowps_block_fake_googlebots', false); // Checkbox
|
||||
$aiowps_firewall_config->set_value('aiowps_googlebot_ip_ranges', array());
|
||||
|
||||
self::turn_off_all_6g_firewall_configs();
|
||||
self::set_cookie_based_bruteforce_firewall_configs();
|
||||
self::set_user_agent_firewall_configs();
|
||||
self::set_ip_retrieve_method_configs();
|
||||
self::set_blacklist_ip_firewall_configs();
|
||||
}
|
||||
|
||||
// Save it
|
||||
return $aio_wp_security->configs->save_config();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add config settings.
|
||||
*
|
||||
* @return Void
|
||||
*/
|
||||
public static function add_option_values() {
|
||||
|
||||
global $aio_wp_security;
|
||||
|
||||
$blog_email_address = get_bloginfo('admin_email'); //Get the blog admin email address - we will use as the default value
|
||||
|
||||
$aio_wp_security->configs->load_config();
|
||||
|
||||
//Debug
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_debug', '');//Checkbox
|
||||
|
||||
//PHP backtrace
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_php_backtrace_in_email', '');//Checkbox
|
||||
|
||||
//WP Generator Meta Tag feature
|
||||
$aio_wp_security->configs->add_value('aiowps_remove_wp_generator_meta_info', '');//Checkbox
|
||||
|
||||
//Prevent Image Hotlinks
|
||||
$aio_wp_security->configs->add_value('aiowps_prevent_hotlinking', '');//Checkbox
|
||||
|
||||
//General Settings Page
|
||||
|
||||
//User password feature
|
||||
|
||||
//Lockout feature
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_login_lockdown', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_allow_unlock_requests', '1'); // Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_max_login_attempts', '3');
|
||||
$aio_wp_security->configs->add_value('aiowps_retry_time_period', '5');
|
||||
$aio_wp_security->configs->add_value('aiowps_lockout_time_length', '5');
|
||||
$aio_wp_security->configs->add_value('aiowps_max_lockout_time_length', '60');
|
||||
$aio_wp_security->configs->add_value('aiowps_set_generic_login_msg', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_email_notify', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_email_address', $blog_email_address);//text field
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_forced_logout', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_logout_time_period', '60');
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_invalid_username_lockdown', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_instantly_lockout_specific_usernames', array()); // Textarea (list of strings)
|
||||
$aio_wp_security->configs->add_value('aiowps_unlock_request_secret_key', AIOWPSecurity_Utility::generate_alpha_numeric_random_string(20));//Hidden secret value which will be used to do some unlock request processing. This will be assigned a random string generated when lockdown settings saved
|
||||
$aio_wp_security->configs->add_value('aiowps_lockdown_enable_whitelisting', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_lockdown_allowed_ip_addresses', '');
|
||||
|
||||
// HTTP authentication
|
||||
$aio_wp_security->configs->add_value('aiowps_http_authentication_admin', ''); // Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_http_authentication_frontend', ''); // Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_http_authentication_username', 'root');
|
||||
$aio_wp_security->configs->add_value('aiowps_http_authentication_password', 'password');
|
||||
$aio_wp_security->configs->add_value('aiowps_http_authentication_failure_message', '<h1>Unauthorized</h1>');
|
||||
|
||||
//Login Whitelist feature
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_whitelisting', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_allowed_ip_addresses', '');
|
||||
|
||||
// CAPTCHA feature
|
||||
$aio_wp_security->configs->add_value('aiowps_default_captcha', '');
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_login_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_custom_login_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_password_protected_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_woo_login_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_woo_register_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_woo_checkout_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_woo_lostpassword_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_contact_form_7_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_captcha_secret_key', AIOWPSecurity_Utility::generate_alpha_numeric_random_string(20)); // Hidden secret value which will be used to do some CAPTCHA processing. This will be assigned a random string generated when CAPTCHA settings saved
|
||||
|
||||
//User registration
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_manual_registration_approval', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_registration_page_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_registration_honeypot', ''); // Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enforce_strong_password', ''); // Checkbox
|
||||
|
||||
//DB Security feature
|
||||
//$aio_wp_security->configs->add_value('aiowps_new_manual_db_pefix', ''); //text field
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_random_prefix', '');//Checkbox
|
||||
|
||||
//Filesystem Security feature
|
||||
$aio_wp_security->configs->add_value('aiowps_disable_file_editing', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_prevent_default_wp_file_access', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_auto_delete_default_wp_files', ''); // Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_system_log_file', 'error_log');
|
||||
|
||||
//Blacklist feature
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_blacklisting', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_banned_ip_addresses', '');
|
||||
|
||||
//Firewall features
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_basic_firewall', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_max_file_upload_size', AIOS_FIREWALL_MAX_FILE_UPLOAD_LIMIT_MB);
|
||||
$aio_wp_security->configs->add_value('aiowps_disable_xmlrpc_pingback_methods', '');//Checkbox - Disables only pingback methods in XMLRPC functionality
|
||||
$aio_wp_security->configs->add_value('aiowps_disable_rss_and_atom_feeds', ''); // Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_block_debug_log_file_access', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_disable_index_views', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_disable_trace_and_track', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_5g_firewall', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_6g_firewall', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_custom_rules', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_place_custom_rules_at_top', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_custom_rules', '');
|
||||
|
||||
// Upgrade unsafe HTTP calls
|
||||
$aio_wp_security->configs->add_value('aiowps_upgrade_unsafe_http_calls', ''); // Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_upgrade_unsafe_http_calls_url_exceptions', '');
|
||||
|
||||
//404 detection
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_404_logging', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_404_IP_lockout', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_404_lockout_time_length', '60');
|
||||
$aio_wp_security->configs->add_value('aiowps_404_lock_redirect_url', 'http://127.0.0.1');
|
||||
|
||||
//Brute Force features
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_rename_login_page', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_login_honeypot', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_disable_application_password', ''); // Checkbox
|
||||
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_brute_force_attack_prevention', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_brute_force_secret_word', '');
|
||||
$aio_wp_security->configs->add_value('aiowps_cookie_brute_test', '');
|
||||
$aio_wp_security->configs->add_value('aiowps_cookie_based_brute_force_redirect_url', 'http://127.0.0.1');
|
||||
$aio_wp_security->configs->add_value('aiowps_brute_force_attack_prevention_pw_protected_exception', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_brute_force_attack_prevention_ajax_exception', '');//Checkbox
|
||||
|
||||
//Maintenance menu - Visitor lockout feature
|
||||
$aio_wp_security->configs->add_value('aiowps_site_lockout', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_site_lockout_msg', '');//Text area/msg box
|
||||
|
||||
// Spam prevention menu
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_spambot_blocking', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_comment_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_spam_ip_min_comments_block', '');
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_bp_register_captcha', '');
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_bbp_new_topic_captcha', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_spambot_detecting', '');
|
||||
$aio_wp_security->configs->add_value('aiowps_spambot_detect_usecookies', '');
|
||||
$aio_wp_security->configs->add_value('aiowps_spam_comments_should', '');
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_trash_spam_comments', '');
|
||||
$aio_wp_security->configs->add_value('aiowps_trash_spam_comments_after_days', '14');
|
||||
|
||||
//Filescan features
|
||||
//File change detection feature
|
||||
$aio_wp_security->configs->add_value('aiowps_enable_automated_fcd_scan', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_fcd_scan_frequency', '4');
|
||||
$aio_wp_security->configs->add_value('aiowps_fcd_scan_interval', '2'); //Dropdown box where (0,1,2) => (hours,days,weeks)
|
||||
$aio_wp_security->configs->add_value('aiowps_fcd_exclude_filetypes', '');
|
||||
$aio_wp_security->configs->add_value('aiowps_fcd_exclude_files', '');
|
||||
$aio_wp_security->configs->add_value('aiowps_send_fcd_scan_email', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_fcd_scan_email_address', $blog_email_address);
|
||||
$aio_wp_security->configs->add_value('aiowps_fcds_change_detected', false); //used to display a global alert on site when file change detected
|
||||
|
||||
//Misc Options
|
||||
//Copy protection feature
|
||||
$aio_wp_security->configs->add_value('aiowps_copy_protection', '');//Checkbox
|
||||
//Prevent others from displaying your site in iframe
|
||||
$aio_wp_security->configs->add_value('aiowps_prevent_site_display_inside_frame', '');//Checkbox
|
||||
//Prevent users enumeration
|
||||
$aio_wp_security->configs->add_value('aiowps_prevent_users_enumeration', '');//Checkbox
|
||||
|
||||
//REST API Security
|
||||
$aio_wp_security->configs->add_value('aiowps_disallow_unauthorized_rest_requests', '');//Checkbox
|
||||
$aio_wp_security->configs->add_value('aios_roles_disallowed_rest_requests', array());
|
||||
$aio_wp_security->configs->add_value('aios_whitelisted_rest_routes', array());
|
||||
|
||||
// IP retrieval setting
|
||||
// Commented the below code line because the IP retrieve method will be configured when the AIOS plugin is activated for the first time.
|
||||
// $aio_wp_security->configs->add_value('aiowps_ip_retrieve_method', '0'); // Default is $_SERVER['REMOTE_ADDR']
|
||||
|
||||
// Cloudflare Turnstile
|
||||
$aio_wp_security->configs->add_value('aiowps_turnstile_site_key', '');
|
||||
$aio_wp_security->configs->add_value('aiowps_turnstile_secret_key', '');
|
||||
|
||||
// Google reCAPTCHA
|
||||
$aio_wp_security->configs->add_value('aiowps_recaptcha_site_key', '');
|
||||
$aio_wp_security->configs->add_value('aiowps_recaptcha_secret_key', '');
|
||||
$aio_wp_security->configs->add_value('aiowps_default_recaptcha', ''); // Not used since 5.1.2
|
||||
|
||||
// Deactivation Handler
|
||||
$aio_wp_security->configs->add_value('aiowps_on_uninstall_delete_db_tables', '1'); //Checkbox
|
||||
$aio_wp_security->configs->add_value('aiowps_on_uninstall_delete_configs', '1'); //Checkbox
|
||||
|
||||
$aio_wp_security->configs->add_value('installed-at', current_time('timestamp', true));
|
||||
|
||||
//TODO - keep adding default options for any fields that require it
|
||||
|
||||
//Save it
|
||||
$aio_wp_security->configs->save_config();
|
||||
|
||||
// For Cookie based brute force prevention backward compatibility
|
||||
if (!headers_sent() && '1' == $aio_wp_security->configs->get_value('aiowps_enable_brute_force_attack_prevention')) {
|
||||
$brute_force_secret_word = $aio_wp_security->configs->get_value('aiowps_brute_force_secret_word');
|
||||
if (empty($brute_force_secret_word)) {
|
||||
$brute_force_secret_word = AIOS_DEFAULT_BRUTE_FORCE_FEATURE_SECRET_WORD;
|
||||
}
|
||||
AIOWPSecurity_Utility::set_cookie_value(AIOWPSecurity_Utility::get_brute_force_secret_cookie_name(), AIOS_Helper::get_hash($brute_force_secret_word));
|
||||
}
|
||||
|
||||
// Login whitelisting started to work on non-apache server from db_version 1.9.5
|
||||
if (is_main_site() && version_compare(get_option('aiowpsec_db_version'), '1.9.6', '<') && '1' == $aio_wp_security->configs->get_value('aiowps_enable_whitelisting') && !empty($aio_wp_security->configs->get_value('aiowps_allowed_ip_addresses'))) {
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_whitelisting', '0');
|
||||
$aio_wp_security->configs->set_value('aiowps_is_login_whitelist_disabled_on_upgrade', '1');
|
||||
$aio_wp_security->configs->save_config();
|
||||
}
|
||||
|
||||
if (is_main_site() && version_compare(get_option('aiowpsec_db_version'), '2.0.0', '<') && '1' == $aio_wp_security->configs->get_value('aiowps_enable_blacklisting') && !empty($aio_wp_security->configs->get_value('aiowps_banned_ip_addresses')) && (false !== strpos($aio_wp_security->configs->get_value('aiowps_banned_ip_addresses'), '*') || false !== strpos($aio_wp_security->configs->get_value('aiowps_banned_ip_addresses'), '/'))) {
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_blacklisting', '0');
|
||||
$aio_wp_security->configs->set_value('aiowps_is_ip_blacklist_settings_notice_on_upgrade', '1');
|
||||
$aio_wp_security->configs->save_config();
|
||||
self::set_user_agent_firewall_configs();
|
||||
self::send_blacklist_manager_disabled_email();
|
||||
}
|
||||
|
||||
if (is_main_site() && version_compare(get_option('aiowpsec_db_version'), '2.0.0', '<') && '1' == $aio_wp_security->configs->get_value('aiowps_enable_spambot_blocking')) {
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_spambot_detecting', '1');
|
||||
$aio_wp_security->configs->set_value('aiowps_spambot_detect_usecookies', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_spam_comments_should', '0');
|
||||
$aio_wp_security->configs->save_config();
|
||||
}
|
||||
|
||||
|
||||
if (is_main_site() && version_compare(get_option('aiowpsec_db_version'), '2.0.3', '<')) {
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_pingback_firewall', '0');//Checkbox - blocks all access to XMLRPC
|
||||
$aio_wp_security->configs->set_value('aiowps_forbid_proxy_comments', '0');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_deny_bad_query_strings', '0');//Checkbox
|
||||
$aio_wp_security->configs->set_value('aiowps_advanced_char_string_filter', '0');//Checkbox
|
||||
$aio_wp_security->configs->save_config();
|
||||
}
|
||||
|
||||
if (is_main_site()) {
|
||||
AIOWPSecurity_Utility_Htaccess::write_to_htaccess(false);
|
||||
}
|
||||
|
||||
// Add expiration for antibot keys for previous versions
|
||||
if (version_compare(get_option('aiowpsec_db_version'), '2.1.1', '<')) {
|
||||
AIOWPSecurity_Comment::generate_antibot_keys(true);
|
||||
}
|
||||
|
||||
// Add ContactForm7 related authentication scheme for salt postfix
|
||||
if (version_compare(get_option('aiowpsec_db_version'), '2.1.4', '<') && '1' == $aio_wp_security->configs->get_value('aiowps_enable_salt_postfix')) {
|
||||
$salt_postfixes = $aio_wp_security->configs->get_value('aiowps_salt_postfixes');
|
||||
$salt_postfixes['wpcf7_submission'] = wp_generate_password(64, true, true);
|
||||
$aio_wp_security->configs->set_value('aiowps_salt_postfixes', $salt_postfixes, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to update the plugin db version.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function update_aiowpsec_db_version() {
|
||||
update_option('aiowpsec_db_version', AIO_WP_SECURITY_DB_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrades from the old config to the firewall's config
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function upgrade_basic_firewall_rules_configs() {
|
||||
global $aio_wp_security;
|
||||
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
|
||||
|
||||
$settings = array(
|
||||
'aiowps_enable_pingback_firewall',
|
||||
'aiowps_forbid_proxy_comments',
|
||||
'aiowps_deny_bad_query_strings',
|
||||
'aiowps_advanced_char_string_filter',
|
||||
);
|
||||
|
||||
// The settings that have been activated by the user
|
||||
$active = array();
|
||||
|
||||
foreach ($settings as $setting) {
|
||||
if (('1' === $aio_wp_security->configs->get_value($setting))) {
|
||||
$active[] = $setting;
|
||||
$aiowps_firewall_config->set_value($setting, false);
|
||||
$aio_wp_security->configs->delete_value($setting);
|
||||
$aio_wp_security->configs->save_config();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!empty($active)) {
|
||||
$aio_wp_security->configs->set_value('aiowps_firewall_active_upgrade', wp_json_encode($active));
|
||||
$aio_wp_security->configs->save_config();
|
||||
self::send_basic_firewall_upgrade_email();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an email notifying that the upgraded settings have been disabled
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function send_basic_firewall_upgrade_email() {
|
||||
global $aio_wp_security;
|
||||
$dashboard_link = 'admin.php?page=aiowpsec';
|
||||
$dashboard_link = is_multisite() ? network_admin_url($dashboard_link) : admin_url($dashboard_link);
|
||||
$subject = __('Basic firewall settings disabled', 'all-in-one-wp-security-and-firewall');
|
||||
/* translators: %s: Dashboard link. */
|
||||
$email_msg = __('Our basic firewall rules have been upgraded and to prevent any unexpected site issues we have disabled the features.', 'all-in-one-wp-security-and-firewall') . "\n\n" . __('You can enable the features again by logging into your WordPress dashboard.', 'all-in-one-wp-security-and-firewall') . "\n\n" .sprintf(__('Go to dashboard: %s', 'all-in-one-wp-security-and-firewall'), $dashboard_link) . "\n\n" . __('Once logged in you will see a notification where you can decide on which course of action you wish to take.', 'all-in-one-wp-security-and-firewall') . "\n";
|
||||
$email = get_bloginfo('admin_email');
|
||||
if (false === wp_mail($email, $subject, $email_msg)) {
|
||||
$aio_wp_security->debug_logger->log_debug("Basic firewall rules notification email failed to send to " . $email, 4);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function send blacklist ip manager disabled email.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function send_blacklist_manager_disabled_email() {
|
||||
global $aio_wp_security;
|
||||
$dashboard_link = 'admin.php?page=aiowpsec';
|
||||
$dashboard_link = is_multisite() ? network_admin_url($dashboard_link) : admin_url($dashboard_link);
|
||||
$subject = '['. get_option('siteurl'). '] '. __('Blacklist manager disabled notification', 'all-in-one-wp-security-and-firewall');
|
||||
/* translators: %s: Dashboard link */
|
||||
$email_msg = __('The blacklist manager feature has been updated and to prevent any unexpected site lockouts we have disabled the feature.', 'all-in-one-wp-security-and-firewall') . "\n\n" . __('You can enable the feature again by logging into your WordPress dashboard.', 'all-in-one-wp-security-and-firewall') . "\n\n" .sprintf(__('Go to dashboard: %s', 'all-in-one-wp-security-and-firewall'), $dashboard_link) . "\n\n" . __('Once logged in before turning the blacklist manger on please double check your settings to ensure you have not entered your own details.', 'all-in-one-wp-security-and-firewall') . "\n";
|
||||
$email = get_bloginfo('admin_email');
|
||||
$mail_sent = wp_mail($email, $subject, $email_msg);
|
||||
if (false === $mail_sent) {
|
||||
$aio_wp_security->debug_logger->log_debug("Blacklist IP manager disabled notification email failed to send to " . $email, 4);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Firewall configs set based on version.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function set_firewall_configs() {
|
||||
if (is_main_site()) {
|
||||
$firewall_version = get_option('aiowpsec_firewall_version');
|
||||
if (version_compare($firewall_version, '1.0.1', '<')) {
|
||||
self::set_cookie_based_bruteforce_firewall_configs();
|
||||
}
|
||||
if (version_compare($firewall_version, '1.0.3', '<')) {
|
||||
self::set_ip_retrieve_method_configs();
|
||||
}
|
||||
if (version_compare($firewall_version, '1.0.4', '<')) {
|
||||
self::set_blacklist_ip_firewall_configs();
|
||||
}
|
||||
if (version_compare($firewall_version, '1.0.5', '<')) {
|
||||
self::upgrade_basic_firewall_rules_configs();
|
||||
}
|
||||
if (version_compare($firewall_version, '1.0.6', '<')) { //1.0.2 set but here making sure the blank user agent is not saved in settings.php which may show a 403 error due to not empty user agent check removed from the rule
|
||||
self::set_user_agent_firewall_configs();
|
||||
}
|
||||
if (version_compare($firewall_version, '1.0.8', '<')) {
|
||||
self::port_block_fake_googlebots_config();
|
||||
}
|
||||
}
|
||||
update_option('aiowpsec_firewall_version', AIO_WP_SECURITY_FIREWALL_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blacklist IP firewall configs set.
|
||||
*
|
||||
* @return void.
|
||||
*/
|
||||
public static function set_blacklist_ip_firewall_configs() {
|
||||
global $aio_wp_security;
|
||||
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
|
||||
$aiowps_firewall_config->set_value('aiowps_ip_retrieve_method', $aio_wp_security->configs->get_value('aiowps_ip_retrieve_method'));
|
||||
if ('1' == $aio_wp_security->configs->get_value('aiowps_enable_blacklisting') && !empty($aio_wp_security->configs->get_value('aiowps_banned_ip_addresses'))) {
|
||||
$aiowps_firewall_config->set_value('aiowps_blacklist_ips', explode("\n", preg_replace("/\r/", "", trim($aio_wp_security->configs->get_value('aiowps_banned_ip_addresses')))));
|
||||
} else {
|
||||
$aiowps_firewall_config->set_value('aiowps_blacklist_ips', array());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cookie based bruteforce firewall configs set.
|
||||
*
|
||||
* @return void.
|
||||
*/
|
||||
public static function set_cookie_based_bruteforce_firewall_configs() {
|
||||
global $aio_wp_security;
|
||||
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
|
||||
|
||||
$aiowps_firewall_config->set_value('aios_enable_rename_login_page', $aio_wp_security->configs->get_value('aiowps_enable_rename_login_page'));
|
||||
|
||||
$aiowps_firewall_config->set_value('aios_login_page_slug', $aio_wp_security->configs->get_value('aiowps_login_page_slug'));
|
||||
|
||||
$aios_enable_brute_force_attack_prevention = $aio_wp_security->configs->get_value('aiowps_enable_brute_force_attack_prevention');
|
||||
$aiowps_firewall_config->set_value('aios_enable_brute_force_attack_prevention', $aios_enable_brute_force_attack_prevention);
|
||||
|
||||
$aiowps_firewall_config->set_value('aios_brute_force_secret_word', $aio_wp_security->configs->get_value('aiowps_brute_force_secret_word'));
|
||||
|
||||
$aiowps_firewall_config->set_value('aios_cookie_based_brute_force_redirect_url', $aio_wp_security->configs->get_value('aiowps_cookie_based_brute_force_redirect_url'));
|
||||
|
||||
$aiowps_firewall_config->set_value('aios_brute_force_attack_prevention_pw_protected_exception', $aio_wp_security->configs->get_value('aiowps_brute_force_attack_prevention_pw_protected_exception'));
|
||||
|
||||
$aiowps_firewall_config->set_value('aios_brute_force_attack_prevention_ajax_exception', $aio_wp_security->configs->get_value('aiowps_brute_force_attack_prevention_ajax_exception'));
|
||||
|
||||
$aiowps_firewall_config->set_value('aios_brute_force_secret_cookie_name', AIOWPSecurity_Utility::get_brute_force_secret_cookie_name());
|
||||
}
|
||||
|
||||
/**
|
||||
* User agent firewall configs set.
|
||||
*
|
||||
* @return void.
|
||||
*/
|
||||
public static function set_user_agent_firewall_configs() {
|
||||
global $aio_wp_security;
|
||||
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
|
||||
if ('1' == $aio_wp_security->configs->get_value('aiowps_enable_blacklisting') && !empty($aio_wp_security->configs->get_value('aiowps_banned_user_agents'))) {
|
||||
$aiowps_firewall_config->set_value('aiowps_blacklist_user_agents', explode("\n", preg_replace("/\r/", "", trim($aio_wp_security->configs->get_value('aiowps_banned_user_agents')))));
|
||||
} else {
|
||||
$aiowps_firewall_config->set_value('aiowps_blacklist_user_agents', array());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Port block fake Googlebots config to firewall config.
|
||||
*
|
||||
* @global AIO_WP_Security $aio_wp_security
|
||||
* @global AIOWPS\Firewall\Config $aiowps_firewall_config
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private static function port_block_fake_googlebots_config() {
|
||||
global $aio_wp_security;
|
||||
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
|
||||
|
||||
if ('1' == $aio_wp_security->configs->get_value('aiowps_block_fake_googlebots')) {
|
||||
$aiowps_firewall_config->set_value('aiowps_block_fake_googlebots', true);
|
||||
|
||||
$validated_ip_list_array = AIOWPSecurity_Utility::get_googlebot_ip_ranges();
|
||||
|
||||
if (!is_wp_error($validated_ip_list_array)) {
|
||||
$aiowps_firewall_config->set_value('aiowps_googlebot_ip_ranges', $validated_ip_list_array);
|
||||
}
|
||||
} else {
|
||||
$aiowps_firewall_config->set_value('aiowps_block_fake_googlebots', false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IP retrieve method configs set.
|
||||
*
|
||||
* @return void.
|
||||
*/
|
||||
public static function set_ip_retrieve_method_configs() {
|
||||
global $aio_wp_security;
|
||||
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
|
||||
$aiowps_firewall_config->set_value('aios_ip_retrieve_method', $aio_wp_security->configs->get_value('aiowps_ip_retrieve_method'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn off all security features.
|
||||
*
|
||||
* @return void.
|
||||
*/
|
||||
public static function turn_off_all_security_features() {
|
||||
global $aio_wp_security;
|
||||
AIOWPSecurity_Configure_Settings::set_default_settings();
|
||||
|
||||
//Refresh the .htaccess file based on the new settings
|
||||
$serverType = AIOWPSecurity_Utility::get_server_type();
|
||||
if (!in_array($serverType, array('-1', 'nginx', 'iis'))) {
|
||||
$res = AIOWPSecurity_Utility_Htaccess::write_to_htaccess();
|
||||
} else {
|
||||
$res = true;
|
||||
}
|
||||
if (!$res) {
|
||||
$aio_wp_security->debug_logger->log_debug(__METHOD__ . " - Could not write to the .htaccess file. Please check the file permissions.", 4);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn off 6g firewall configs.
|
||||
*
|
||||
* @return void.
|
||||
*/
|
||||
public static function turn_off_all_6g_firewall_configs() {
|
||||
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
|
||||
$aiowps_firewall_config->set_value('aiowps_6g_block_request_methods', array());
|
||||
$aiowps_firewall_config->set_value('aiowps_6g_block_query', false);
|
||||
$aiowps_firewall_config->set_value('aiowps_6g_block_request', false);
|
||||
$aiowps_firewall_config->set_value('aiowps_6g_block_referrers', false);
|
||||
$aiowps_firewall_config->set_value('aiowps_6g_block_agents', false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Turn off all firewall configs.
|
||||
*
|
||||
* @return void.
|
||||
*/
|
||||
public static function turn_off_firewall_configs() {
|
||||
global $aiowps_firewall_config, $aio_wp_security;
|
||||
$aio_wp_security->configs->set_value('aiowps_disable_xmlrpc_pingback_methods', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_disable_rss_and_atom_feeds', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_basic_firewall', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_max_file_upload_size', AIOS_FIREWALL_MAX_FILE_UPLOAD_LIMIT_MB); //Default
|
||||
$aio_wp_security->configs->set_value('aiowps_block_debug_log_file_access', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_disable_index_views', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_disable_trace_and_track', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_blacklisting', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_5g_firewall', '');
|
||||
$aio_wp_security->configs->set_value('aiowps_disallow_unauthorized_rest_requests', '');
|
||||
$aio_wp_security->configs->save_config();
|
||||
|
||||
self::turn_off_all_6g_firewall_configs();
|
||||
$aiowps_firewall_config->set_value('aiowps_enable_pingback_firewall', false);
|
||||
$aiowps_firewall_config->set_value('aiowps_forbid_proxy_comments', false);
|
||||
$aiowps_firewall_config->set_value('aiowps_deny_bad_query_strings', false);
|
||||
$aiowps_firewall_config->set_value('aiowps_advanced_char_string_filter', false);
|
||||
$aiowps_firewall_config->set_value('aiowps_ban_post_blank_headers', false);
|
||||
$aiowps_firewall_config->set_value('aiowps_block_fake_googlebots', false);
|
||||
$aiowps_firewall_config->set_value('aiowps_googlebot_ip_ranges', array());
|
||||
$aiowps_firewall_config->set_value('aiowps_blacklist_user_agents', array());
|
||||
$aiowps_firewall_config->set_value('aiowps_blacklist_ips', array());
|
||||
Allow_List::add_ips(array()); // Remove firewall whitelisted IPs
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+157
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;//Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles all cron jobs
|
||||
*/
|
||||
class AIOWPSecurity_Cronjob_Handler {
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
add_filter('cron_schedules', array('AIOWPSecurity_Cronjob_Handler', 'cron_schedules'));
|
||||
|
||||
add_action('aios_15_minutes_cron_event', array($this, 'aios_15_minutes_cron_event'));
|
||||
add_action('aiowps_hourly_cron_event', array($this, 'aiowps_hourly_cron_event_handler'));
|
||||
add_action('aiowps_daily_cron_event', array($this, 'aiowps_daily_cron_event_handler'));
|
||||
add_action('aios_change_auth_keys_and_salt', 'AIOWPSecurity_Utility::change_salt_postfixes');
|
||||
add_action('aiowps_purge_old_debug_logs', array($this, 'purge_old_debug_logs'));
|
||||
add_action('aiowps_send_lockout_email', array($this, 'send_lockout_email'));
|
||||
add_action('aios_update_googlebot_ip_ranges', array($this, 'aios_update_googlebot_ip_ranges'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom cron schedule for every 5 minutes.
|
||||
*
|
||||
* @param array $schedules An array of cron schedules.
|
||||
* @return array Filtered array of cron schedules.
|
||||
*/
|
||||
public static function cron_schedules($schedules) {
|
||||
$schedules['aios-every-15-minutes'] = array(
|
||||
'interval' => 900, // 15 * 60
|
||||
'display' => __('Every 15 minutes', 'all-in-one-wp-security-and-firewall')
|
||||
);
|
||||
return $schedules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run cron event every 5 minute.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function aios_15_minutes_cron_event() {
|
||||
global $aio_wp_security;
|
||||
|
||||
if (!class_exists('Updraft_Semaphore_3_0')) {
|
||||
require_once(AIO_WP_SECURITY_PATH.'/vendor/team-updraft/common-libs/src/updraft-semaphore/class-updraft-semaphore.php');
|
||||
}
|
||||
|
||||
$fifteen_minutes_cron_semaphore = new Updraft_Semaphore_3_0('aios_15_minutes_cron_event', 60);
|
||||
|
||||
if ($fifteen_minutes_cron_semaphore->lock(2)) {
|
||||
try {
|
||||
$aio_wp_security->user_login_obj->send_login_lockout_emails();
|
||||
} catch (Exception $e) {
|
||||
$log_message = 'Exception ('.get_class($e).') occurred during the 5 minutes cron event action call: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
|
||||
$aio_wp_security->debug_logger->log_debug($log_message, 4);
|
||||
// @codingStandardsIgnoreLine
|
||||
} catch (Error $e) {
|
||||
$log_message = 'PHP Fatal error ('.get_class($e).') occurred during the the 5 minutes cron event action call. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
|
||||
$aio_wp_security->debug_logger->log_debug($log_message, 4);
|
||||
}
|
||||
|
||||
$fifteen_minutes_cron_semaphore->release();
|
||||
} else {
|
||||
$aio_wp_security->debug_logger->log_debug('The 15 minutes cron event lock could not get the lock.');
|
||||
}
|
||||
}
|
||||
|
||||
public function aiowps_hourly_cron_event_handler() {
|
||||
//Do stuff that needs checking hourly
|
||||
AIOWPSecurity_Comment::trash_spam_comments();
|
||||
do_action('aiowps_perform_fcd_scan_tasks');
|
||||
do_action('delete_expired_logged_in_users_event');
|
||||
}
|
||||
|
||||
/**
|
||||
* Run daily cron event
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function aiowps_daily_cron_event_handler() {
|
||||
do_action('aiowps_perform_db_cleanup_tasks');
|
||||
do_action('aiowps_purge_old_debug_logs');
|
||||
do_action('aiowps_send_lockout_email');
|
||||
do_action('aios_perform_update_antibot_keys');
|
||||
do_action('aios_update_googlebot_ip_ranges');
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges debug logs older than 90 days
|
||||
* The 90 days can be modified using the constant AIOWPSEC_PURGE_DEBUG_LOGS_AFTER_DAYS
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function purge_old_debug_logs() {
|
||||
|
||||
global $wpdb, $aio_wp_security;
|
||||
|
||||
$debug_tbl_name = AIOWPSEC_TBL_DEBUG_LOG;
|
||||
$after_days = 90;
|
||||
|
||||
if (defined('AIOWPSEC_PURGE_DEBUG_LOGS_AFTER_DAYS')) {
|
||||
$after_days = abs(AIOWPSEC_PURGE_DEBUG_LOGS_AFTER_DAYS);
|
||||
}
|
||||
|
||||
$after_days = empty($after_days) ? 90 : $after_days;
|
||||
$older_than_date_time = strtotime('-' . $after_days . ' days', time());
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery -- PCP warning. Ignore
|
||||
$ret = $wpdb->query($wpdb->prepare('DELETE FROM ' . $debug_tbl_name . ' WHERE logtime < %s', $older_than_date_time));
|
||||
if (false === $ret) {
|
||||
$error_msg = empty($wpdb->last_error) ? 'Could not receive the reason for the failure' : $wpdb->last_error;
|
||||
$aio_wp_security->debug_logger->log_debug_cron("Failed to purge older debug logs : {$error_msg}", 4);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Send email notification to an user who has flag is_lockout_email_sent is 0.
|
||||
*
|
||||
* @return Void
|
||||
*/
|
||||
public function send_lockout_email() {
|
||||
global $aio_wp_security;
|
||||
$aio_wp_security->user_login_obj->send_login_lockout_emails();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Googlebot IP ranges for the firewall configuration.
|
||||
*
|
||||
* This function updates the Googlebot IP ranges in the firewall configuration.
|
||||
* It checks if the 'Block fake Googlebots' feature is enabled and then retrieves
|
||||
* the validated Googlebot IP ranges. If the IP ranges are retrieved successfully,
|
||||
* they are saved to the firewall configuration. If there is an error in retrieving
|
||||
* the IP ranges, a debug message is logged.
|
||||
*
|
||||
* @global object $aio_wp_security The main AIO WP Security object.
|
||||
* @global object $aiowps_firewall_config The AIO WP Security firewall configuration object.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function aios_update_googlebot_ip_ranges() {
|
||||
global $aio_wp_security;
|
||||
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
|
||||
|
||||
if ($aiowps_firewall_config->get_value('aiowps_block_fake_googlebots')) {
|
||||
$validated_ip_list_array = AIOWPSecurity_Utility::get_googlebot_ip_ranges();
|
||||
|
||||
if (is_wp_error($validated_ip_list_array)) {
|
||||
$aio_wp_security->debug_logger->log_debug('The attempt to save the IP addresses failed, because it was not possible to validate the Googlebot IP addresses: ' . $validated_ip_list_array->get_error_message(), 4);
|
||||
}
|
||||
|
||||
$aiowps_firewall_config->set_value('aiowps_googlebot_ip_ranges', $validated_ip_list_array);
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
require_once(AIO_WP_SECURITY_PATH.'/classes/wp-security-base-tasks.php');
|
||||
|
||||
class AIOWPSecurity_Deactivation_Tasks extends AIOWPSecurity_Base_Tasks {
|
||||
|
||||
/**
|
||||
* Run deactivation task for a single site.
|
||||
*
|
||||
* This method overrides {@see AIOWPSecurity_Base_Tasks::run_for_a_site()}.
|
||||
* It ensures .htaccess rules, firewall, and user login activity
|
||||
* are properly cleaned up during deactivation.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function run_for_a_site() {
|
||||
global $aio_wp_security;
|
||||
|
||||
$aio_wp_security->configs->load_config();
|
||||
|
||||
if (is_main_site()) {
|
||||
// Remove all firewall and other .htaccess rules and remove all settings from .htaccess file that were added by this plugin.
|
||||
AIOWPSecurity_Utility_Htaccess::delete_from_htaccess();
|
||||
|
||||
// Remove user meta info so next activation if force logout on it do not logs user out
|
||||
AIOWPSecurity_User_Login::remove_login_activity();
|
||||
|
||||
// Deactivate PHP-based firewall.
|
||||
AIOWPSecurity_Utility_Firewall::remove_firewall();
|
||||
}
|
||||
|
||||
self::clear_cron_events();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function which clears aiowps cron events
|
||||
*/
|
||||
private static function clear_cron_events() {
|
||||
wp_clear_scheduled_hook('aiowps_hourly_cron_event');
|
||||
wp_clear_scheduled_hook('aiowps_daily_cron_event');
|
||||
wp_clear_scheduled_hook('aios_15_minutes_cron_event');
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+100
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
/**
|
||||
* Logs debug data to a file. Here is an example usage
|
||||
* global $aio_wp_security;
|
||||
* $aio_wp_security->debug_logger->log_debug("Log messaged goes here");
|
||||
*/
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;//Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Logger {
|
||||
|
||||
private $debug_enabled = false;
|
||||
|
||||
private $debug_readable_level = array('SUCCESS','STATUS','NOTICE','WARNING','FAILURE','CRITICAL');
|
||||
|
||||
public function __construct($debug_enabled) {
|
||||
$this->debug_enabled = $debug_enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the level code to its readable form
|
||||
*
|
||||
* @param integer $level_code - The level code to translate (e.g: 2)
|
||||
* @return string - The level as its readable value (e.g: NOTICE)
|
||||
*/
|
||||
private function get_readable_level_from_code($level_code) {
|
||||
return isset($this->debug_readable_level[$level_code]) ? $this->debug_readable_level[$level_code] : 'UNKNOWN';
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the debug logs
|
||||
*
|
||||
* @return int|WP_Error the amount of records deleted or WP_Error if query failed
|
||||
*/
|
||||
public function clear_logs() {
|
||||
global $wpdb;
|
||||
|
||||
$debug_log_tbl = AIOWPSEC_TBL_DEBUG_LOG;
|
||||
|
||||
if (is_super_admin()) {
|
||||
$ret = $wpdb->query("DELETE FROM $debug_log_tbl"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- We can't use %i because our plugin supports wordpress < 6.2.
|
||||
} else {
|
||||
$ret = $wpdb->query($wpdb->prepare("DELETE FROM $debug_log_tbl WHERE site_id = %d", get_current_blog_id())); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- We can't use %i because our plugin supports wordpress < 6.2.
|
||||
}
|
||||
|
||||
if (false === $ret) {
|
||||
$error_msg = empty($wpdb->last_error) ? __('Unable to get the reason why', 'all-in-one-wp-security-and-firewall') : $wpdb->last_error;
|
||||
$ret = new WP_Error('db_unable_delete', __('Unable to clear the logs', 'all-in-one-wp-security-and-firewall'), $error_msg);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the debug messages to the database
|
||||
*
|
||||
* @param string $message - The main debug message
|
||||
* @param integer $level_code - The level code which indicates the severity of the message
|
||||
* @param string $type - The type of debug message. Separates general debug messages from those generated by the cron, for example.
|
||||
* @return void
|
||||
*/
|
||||
public function log_debug($message, $level_code = 0, $type = 'debug') {
|
||||
|
||||
if (!$this->debug_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$debug_tbl_name = AIOWPSEC_TBL_DEBUG_LOG;
|
||||
|
||||
$data = array(
|
||||
'created' => current_time('mysql'),
|
||||
'level' => $this->get_readable_level_from_code($level_code),
|
||||
'network_id' => get_current_network_id(),
|
||||
'site_id' => get_current_blog_id(),
|
||||
'message' => $message,
|
||||
'type' => $type,
|
||||
);
|
||||
|
||||
$ret = $wpdb->query($wpdb->prepare("INSERT INTO ".$debug_tbl_name." (created, logtime, level, network_id, site_id, message, type) VALUES (%s, UNIX_TIMESTAMP(), %s, %d, %d, %s, %s)", $data['created'], $data['level'], $data['network_id'], $data['site_id'], $data['message'], $data['type'])); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared -- We can't use %i because our plugin supports wordpress < 6.2.
|
||||
if (false === $ret) {
|
||||
$error_msg = empty($wpdb->last_error) ? 'Could not write to the debug log' : $wpdb->last_error;
|
||||
error_log("All In One WP Security : {$error_msg}"); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- We can't replace error_log with our debug logger here because the debug logger has failed.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs the debug messages that relate to the cron
|
||||
*
|
||||
* @param string $message - The main debug message
|
||||
* @param integer $level_code - The level code which indicates the severity of the message
|
||||
* @return void
|
||||
*/
|
||||
public function log_debug_cron($message, $level_code = 0) {
|
||||
$this->log_debug($message, $level_code, 'cron_debug');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+588
@@ -0,0 +1,588 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; //Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Debug {
|
||||
|
||||
// Some of the contents of this file doesn't need to be translated as it is for developers
|
||||
|
||||
/**
|
||||
* List of sections to include in the debug report
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $sections = array(
|
||||
'AIOS plugin information' => 'get_aios_info',
|
||||
'Server information' => 'get_server_info',
|
||||
'WordPress information' => 'get_wordpress_info',
|
||||
'PHP information' => 'get_php_info',
|
||||
'Database information' => 'get_database_info',
|
||||
'Plugin information' => 'get_plugins_list',
|
||||
'Must-use plugin information' => 'get_mu_plugins_list',
|
||||
'Drop-in information' => 'get_dropin_plugins_list',
|
||||
'Theme information' => 'get_themes_list',
|
||||
'IP detection methods' => 'get_ip_detection_methods',
|
||||
'Cron information' => 'get_cron_jobs_list',
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct() {
|
||||
add_action('aiowp_security_additional_report_actions', array($this, 'add_sender_report_actions'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get AIOS Information
|
||||
*
|
||||
* @return array AIOS information
|
||||
*/
|
||||
private static function get_aios_info() {
|
||||
|
||||
$aios_info = array(
|
||||
'AIOS plugin version' => AIO_WP_SECURITY_VERSION,
|
||||
'AIOS DB version' => AIO_WP_SECURITY_DB_VERSION,
|
||||
'AIOS firewall version' => AIO_WP_SECURITY_FIREWALL_VERSION,
|
||||
'AIOS Premium installed' => AIOWPSecurity_Utility_Permissions::is_premium_installed() ? 'Yes' : 'No',
|
||||
);
|
||||
|
||||
if (AIOWPSecurity_Utility_Permissions::is_premium_installed()) {
|
||||
$aios_info['AIOS Premium version'] = AIOWPS_PREMIUM_VERSION;
|
||||
}
|
||||
|
||||
return apply_filters('aiowp_security_get_aios_info', $aios_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Server Information
|
||||
*
|
||||
* @return array Server information
|
||||
*/
|
||||
private static function get_server_info() {
|
||||
|
||||
$server_info = array(
|
||||
'Operating system' => php_uname('s') . ' ' . php_uname('r'),
|
||||
'Server' => $_SERVER['SERVER_SOFTWARE'],
|
||||
'Memory usage' => AIOWPSecurity_Utility::convert_numeric_size_to_text(memory_get_peak_usage(true)),
|
||||
);
|
||||
|
||||
if (function_exists('disk_total_space')) {
|
||||
$server_info = array_merge($server_info, array(
|
||||
'Total space' => AIOWPSecurity_Utility::convert_numeric_size_to_text(disk_total_space('/')),
|
||||
'Used space' => AIOWPSecurity_Utility::convert_numeric_size_to_text(disk_total_space('/') - disk_free_space('/')),
|
||||
));
|
||||
}
|
||||
return apply_filters('aiowp_security_get_server_info', $server_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PHP Information
|
||||
*
|
||||
* @return array PHP information
|
||||
*/
|
||||
private static function get_php_info() {
|
||||
|
||||
$php_info = array(
|
||||
'PHP version' => phpversion(),
|
||||
'PHP expose php' => ini_get('expose_php') ? 'Active' : 'Inactive',
|
||||
'PHP allow url fopen' => ini_get('allow_url_fopen') ? 'Active' : 'Inactive',
|
||||
'PHP memory limit' => ini_get('memory_limit'),
|
||||
'PHP upload max filesize' => ini_get('upload_max_filesize'),
|
||||
'PHP post max size' => ini_get('post_max_size'),
|
||||
'PHP max execution time' => ini_get('max_execution_time'),
|
||||
'PHP max input time' => ini_get('max_input_time'),
|
||||
'Process owner' => (function_exists('posix_geteuid') && function_exists('posix_getpwuid')) ? posix_getpwuid(posix_geteuid())['name'] : 'Unknown',
|
||||
'OpenSSL support' => extension_loaded('openssl') ? 'OK' : 'Not Installed',
|
||||
'OpenSSL version' => extension_loaded('openssl') ? OPENSSL_VERSION_TEXT : 'Unknown',
|
||||
'cURL support' => function_exists('curl_init') ? 'OK' : 'Not Installed',
|
||||
'cURL features code' => function_exists('curl_version') ? curl_version()['features'] : 'Unknown',
|
||||
'cURL host' => function_exists('curl_version') ? curl_version()['host'] : 'Unknown',
|
||||
'cURL support protocols' => function_exists('curl_version') ? implode(', ', curl_version()['protocols']) : 'Unknown',
|
||||
'cURL SSL version' => function_exists('curl_version') ? curl_version()['ssl_version'] : 'Unknown',
|
||||
'cURL libz version' => function_exists('curl_version') ? curl_version()['libz_version'] : 'Unknown',
|
||||
'Checking display_errors' => ini_get('display_errors') ? 'Enabled' : 'Disabled',
|
||||
);
|
||||
|
||||
return apply_filters('aiowp_security_get_php_info', $php_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves detailed information about the WordPress configuration.
|
||||
*
|
||||
* @return array WordPress configuration information
|
||||
*/
|
||||
private static function get_wordpress_info() {
|
||||
|
||||
// Include version.php to retrieve the actual WordPress version
|
||||
require_once ABSPATH . WPINC . '/version.php';
|
||||
|
||||
global $wp_version;
|
||||
|
||||
$wp_info = array(
|
||||
'WordPress version' => $wp_version,
|
||||
'Multisite' => is_multisite() ? 'Yes' : 'No', // Checking if the site is multisite
|
||||
'ABSPATH' => ABSPATH,
|
||||
'WP_DEBUG' => (defined('WP_DEBUG') && WP_DEBUG) ? 'On' : 'Off',
|
||||
'WP_DEBUG_LOG' => (defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) ? 'Enabled' : 'Disabled',
|
||||
'WP_DEBUG_DISPLAY' => (defined('WP_DEBUG_DISPLAY') && WP_DEBUG_DISPLAY) ? 'Enabled' : 'Disabled',
|
||||
'SCRIPT_DEBUG' => (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? 'On' : 'Off',
|
||||
'SAVEQUERIES' => (defined('SAVEQUERIES') && SAVEQUERIES) ? 'On' : 'Off',
|
||||
'DB_CHARSET' => defined('DB_CHARSET') ? DB_CHARSET : '(not set)',
|
||||
'DB_COLLATE' => defined('DB_COLLATE') ? DB_COLLATE : '(not set)',
|
||||
'WP_SITEURL' => defined('WP_SITEURL') ? WP_SITEURL : '(not set)',
|
||||
'WP_HOME' => defined('WP_HOME') ? WP_HOME : '(not set)',
|
||||
'WP_CONTENT_DIR' => defined('WP_CONTENT_DIR') ? WP_CONTENT_DIR : '(not set)',
|
||||
'WP_CONTENT_URL' => defined('WP_CONTENT_URL') ? WP_CONTENT_URL : '(not set)',
|
||||
'WP_PLUGIN_DIR' => defined('WP_PLUGIN_DIR') ? WP_PLUGIN_DIR : '(not set)',
|
||||
'WP_LANG_DIR' => defined('WP_LANG_DIR') ? WP_LANG_DIR : '(not set)',
|
||||
'WPLANG' => defined('WPLANG') ? WPLANG : '(not set)',
|
||||
'UPLOADS' => defined('UPLOADS') ? UPLOADS : '(not set)',
|
||||
'TEMPLATEPATH' => defined('TEMPLATEPATH') ? TEMPLATEPATH : '(not set)',
|
||||
'STYLESHEETPATH' => defined('STYLESHEETPATH') ? STYLESHEETPATH : '(not set)',
|
||||
'AUTOSAVE_INTERVAL' => defined('AUTOSAVE_INTERVAL') ? AUTOSAVE_INTERVAL : '',
|
||||
'WP_POST_REVISIONS' => defined('WP_POST_REVISIONS') ? WP_POST_REVISIONS : 'Unlimited',
|
||||
'COOKIE_DOMAIN' => defined('COOKIE_DOMAIN') ? COOKIE_DOMAIN : '(not set)',
|
||||
'COOKIEPATH' => defined('COOKIEPATH') ? COOKIEPATH : '/',
|
||||
'SITECOOKIEPATH' => defined('SITECOOKIEPATH') ? SITECOOKIEPATH : '/',
|
||||
'ADMIN_COOKIE_PATH' => defined('ADMIN_COOKIE_PATH') ? ADMIN_COOKIE_PATH : '/',
|
||||
'PLUGINS_COOKIE_PATH' => defined('PLUGINS_COOKIE_PATH') ? PLUGINS_COOKIE_PATH : '/wp-content/plugins',
|
||||
'NOBLOGREDIRECT' => defined('NOBLOGREDIRECT') ? NOBLOGREDIRECT : '(not set)',
|
||||
'CONCATENATE_SCRIPTS' => defined('CONCATENATE_SCRIPTS') ? (CONCATENATE_SCRIPTS ? 'Yes' : 'No') : 'No', // Checking if CONCATENATE_SCRIPTS is defined
|
||||
'WP_MEMORY_LIMIT' => defined('WP_MEMORY_LIMIT') ? WP_MEMORY_LIMIT : '',
|
||||
'WP_MAX_MEMORY_LIMIT' => defined('WP_MAX_MEMORY_LIMIT') ? WP_MAX_MEMORY_LIMIT : '',
|
||||
'WP_CACHE' => defined('WP_CACHE') ? (WP_CACHE ? 'Enabled' : 'Disabled') : 'Disabled',
|
||||
'CUSTOM_USER_TABLE' => defined('CUSTOM_USER_TABLE') ? CUSTOM_USER_TABLE : '(not set)',
|
||||
'CUSTOM_USER_META_TABLE' => defined('CUSTOM_USER_META_TABLE') ? CUSTOM_USER_META_TABLE : '(not set)',
|
||||
'FS_CHMOD_DIR' => defined('FS_CHMOD_DIR') ? FS_CHMOD_DIR : '(not set)',
|
||||
'FS_CHMOD_FILE' => defined('FS_CHMOD_FILE') ? FS_CHMOD_FILE : '(not set)',
|
||||
'ALTERNATE_WP_CRON' => defined('ALTERNATE_WP_CRON') ? (ALTERNATE_WP_CRON ? 'Enabled' : 'Disabled') : 'Disabled',
|
||||
'DISABLE_WP_CRON' => defined('DISABLE_WP_CRON') ? (DISABLE_WP_CRON ? 'Cron is disabled' : 'Cron is enabled') : 'Cron is enabled',
|
||||
'WP_CRON_LOCK_TIMEOUT' => defined('WP_CRON_LOCK_TIMEOUT') ? WP_CRON_LOCK_TIMEOUT : '',
|
||||
'EMPTY_TRASH_DAYS' => defined('EMPTY_TRASH_DAYS') ? EMPTY_TRASH_DAYS : '',
|
||||
'WP_ALLOW_REPAIR' => defined('WP_ALLOW_REPAIR') ? (WP_ALLOW_REPAIR ? 'Enabled' : 'Disabled') : 'Disabled',
|
||||
'DO_NOT_UPGRADE_GLOBAL_TABLES' => defined('DO_NOT_UPGRADE_GLOBAL_TABLES') ? (DO_NOT_UPGRADE_GLOBAL_TABLES ? 'Yes' : 'No') : 'No', // Checking if DO_NOT_UPGRADE_GLOBAL_TABLES is defined
|
||||
'DISALLOW_FILE_EDIT' => defined('DISALLOW_FILE_EDIT') ? (DISALLOW_FILE_EDIT ? 'Yes' : 'No') : 'No', // Checking if DISALLOW_FILE_EDIT is defined
|
||||
'DISALLOW_FILE_MODS' => defined('DISALLOW_FILE_MODS') ? (DISALLOW_FILE_MODS ? 'Yes' : 'No') : 'No', // Checking if DISALLOW_FILE_MODS is defined
|
||||
'IMAGE_EDIT_OVERWRITE' => defined('IMAGE_EDIT_OVERWRITE') ? (IMAGE_EDIT_OVERWRITE ? 'Yes' : 'No') : 'No', // Checking if IMAGE_EDIT_OVERWRITE is defined
|
||||
'FORCE_SSL_ADMIN' => defined('FORCE_SSL_ADMIN') ? (FORCE_SSL_ADMIN ? 'Yes' : 'No') : 'No', // Checking if FORCE_SSL_ADMIN is defined
|
||||
'WP_HTTP_BLOCK_EXTERNAL' => defined('WP_HTTP_BLOCK_EXTERNAL') ? (WP_HTTP_BLOCK_EXTERNAL ? 'Yes' : 'No') : 'No', // Checking if WP_HTTP_BLOCK_EXTERNAL is defined
|
||||
'WP_ACCESSIBLE_HOSTS' => defined('WP_ACCESSIBLE_HOSTS') ? WP_ACCESSIBLE_HOSTS : '(not set)',
|
||||
'WP_AUTO_UPDATE_CORE' => defined('WP_AUTO_UPDATE_CORE') ? WP_AUTO_UPDATE_CORE : 'Default',
|
||||
'WP_PROXY_HOST' => defined('WP_PROXY_HOST') ? WP_PROXY_HOST : '(not set)',
|
||||
'WP_PROXY_PORT' => defined('WP_PROXY_PORT') ? WP_PROXY_PORT : '(not set)',
|
||||
'MULTISITE' => defined('MULTISITE') ? (MULTISITE ? 'Yes' : 'No') : 'No', // Checking if MULTISITE is defined
|
||||
'WP_ALLOW_MULTISITE' => defined('WP_ALLOW_MULTISITE') ? (WP_ALLOW_MULTISITE ? 'Yes' : 'No') : 'No', // Checking if WP_ALLOW_MULTISITE is defined
|
||||
'SUNRISE' => defined('SUNRISE') ? (SUNRISE ? 'Yes' : 'No') : 'No', // Checking if SUNRISE is defined
|
||||
'SUBDOMAIN_INSTALL' => defined('SUBDOMAIN_INSTALL') ? (SUBDOMAIN_INSTALL ? 'Yes' : 'No') : 'No', // Checking if SUBDOMAIN_INSTALL is defined
|
||||
'VHOST' => defined('VHOST') ? (VHOST ? 'Yes' : 'No') : 'No', // Checking if VHOST is defined
|
||||
'DOMAIN_CURRENT_SITE' => defined('DOMAIN_CURRENT_SITE') ? DOMAIN_CURRENT_SITE : '(not set)',
|
||||
'PATH_CURRENT_SITE' => defined('PATH_CURRENT_SITE') ? PATH_CURRENT_SITE : '(not set)',
|
||||
'BLOG_ID_CURRENT_SITE' => defined('BLOG_ID_CURRENT_SITE') ? BLOG_ID_CURRENT_SITE : '(not set)',
|
||||
'WP_DISABLE_FATAL_ERROR_HANDLER' => defined('WP_DISABLE_FATAL_ERROR_HANDLER') ? (WP_DISABLE_FATAL_ERROR_HANDLER ? 'Yes' : 'No') : 'No', // Checking if WP_DISABLE_FATAL_ERROR_HANDLER is defined
|
||||
'AUTOMATIC_UPDATER_DISABLED' => defined('AUTOMATIC_UPDATER_DISABLED') ? (AUTOMATIC_UPDATER_DISABLED ? 'Yes' : 'No') : 'No' // Checking if AUTOMATIC_UPDATER_DISABLED is defined
|
||||
);
|
||||
|
||||
return apply_filters('aiowp_security_get_wordpress_info', $wp_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of active and inactive plugins.
|
||||
*
|
||||
* @return array List of plugins with their status (active/inactive).
|
||||
*/
|
||||
private static function get_plugins_list() {
|
||||
$plugins = get_plugins();
|
||||
$active_plugins = get_option('active_plugins', array());
|
||||
|
||||
$plugins_list = array();
|
||||
|
||||
if (empty($plugins)) {
|
||||
return array('No plugins found' => '-');
|
||||
}
|
||||
|
||||
foreach ($plugins as $plugin_path => $plugin_info) {
|
||||
$plugin_slug = strtolower(basename($plugin_path, '.php'));
|
||||
$plugin_status = in_array($plugin_path, $active_plugins) ? 'Active' : 'Inactive';
|
||||
$plugin_version = $plugin_info['Version'];
|
||||
$plugin_name = $plugin_info['Name'];
|
||||
|
||||
$plugin_key = "$plugin_name ($plugin_slug) [$plugin_version]";
|
||||
|
||||
$plugins_list[$plugin_key] = $plugin_status;
|
||||
}
|
||||
|
||||
return apply_filters('aiowp_security_get_plugins_info', $plugins_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of themes with their status (active/inactive), version, and slug.
|
||||
*
|
||||
* @return array List of themes.
|
||||
*/
|
||||
private static function get_themes_list() {
|
||||
$themes = wp_get_themes();
|
||||
$active_theme = wp_get_theme();
|
||||
$themes_list = array();
|
||||
|
||||
if (empty($themes)) {
|
||||
return array('No themes found' => '-');
|
||||
}
|
||||
|
||||
foreach ($themes as $theme_slug => $theme_info) {
|
||||
$theme_name = $theme_info->get('Name');
|
||||
$theme_version = $theme_info->get('Version');
|
||||
$theme_status = $theme_info->get_stylesheet() === $active_theme->get_stylesheet() ? 'Active' : 'Inactive';
|
||||
|
||||
$theme_key = "$theme_name ($theme_slug) [$theme_version]";
|
||||
$themes_list[$theme_key] = $theme_status;
|
||||
}
|
||||
|
||||
return apply_filters('aiowp_security_get_themes_info', $themes_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get database information
|
||||
*
|
||||
* @return array Database information
|
||||
*/
|
||||
private static function get_database_info() {
|
||||
global $wpdb;
|
||||
|
||||
$database_info = array(
|
||||
'Database version' => $wpdb->db_version(),
|
||||
'DELETE' => self::check_mysql_privilege('DELETE'),
|
||||
'INSERT' => self::check_mysql_privilege('INSERT'),
|
||||
'UPDATE' => self::check_mysql_privilege('UPDATE'),
|
||||
'SELECT' => self::check_mysql_privilege('SELECT'),
|
||||
'CREATE TABLE' => self::check_mysql_privilege('CREATE TABLE'),
|
||||
'ALTER TABLE' => self::check_mysql_privilege('ALTER TABLE'),
|
||||
'DROP' => self::check_mysql_privilege('DROP'),
|
||||
'TRUNCATE' => self::check_mysql_privilege('TRUNCATE')
|
||||
);
|
||||
|
||||
return apply_filters('aiowp_security_get_database_info', $database_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of Must Use (MU) plugins.
|
||||
*
|
||||
* @return array List of MU plugins.
|
||||
*/
|
||||
private static function get_mu_plugins_list() {
|
||||
$mu_plugins = get_mu_plugins();
|
||||
$mu_plugins_list = array();
|
||||
|
||||
if (empty($mu_plugins)) {
|
||||
return array('No Must-use plugins found' => '-');
|
||||
}
|
||||
|
||||
foreach ($mu_plugins as $mu_plugin_path => $mu_plugin_info) {
|
||||
$mu_plugin_slug = basename($mu_plugin_path, '.php');
|
||||
$mu_plugin_version = $mu_plugin_info['Version'];
|
||||
$mu_plugin_name = $mu_plugin_info['Name'];
|
||||
|
||||
$mu_plugin_key = "$mu_plugin_name ($mu_plugin_slug) [$mu_plugin_version]";
|
||||
|
||||
$mu_plugins_list[$mu_plugin_key] = 'Active';
|
||||
}
|
||||
|
||||
return apply_filters('aiowp_security_get_mu_plugins_info', $mu_plugins_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of drop-in plugins.
|
||||
*
|
||||
* @return array List of drop-in plugins.
|
||||
*/
|
||||
private static function get_dropin_plugins_list() {
|
||||
$dropins = _get_dropins();
|
||||
$dropins_list = array();
|
||||
|
||||
if ('' === $dropins) {
|
||||
return array('No drop-in plugins found' => '-');
|
||||
}
|
||||
|
||||
foreach ($dropins as $dropin_file => $dropin_info) {
|
||||
$dropin_status = true === $dropin_info[1] ? 'Active' : 'Inactive';
|
||||
$dropin_description = $dropin_info[0];
|
||||
|
||||
$dropin_key = "$dropin_file [$dropin_description]";
|
||||
$dropins_list[$dropin_key] = $dropin_status;
|
||||
}
|
||||
|
||||
return apply_filters('aiowp_security_get_dropin_plugins_info', $dropins_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of cron jobs scheduled in WordPress.
|
||||
*
|
||||
* @return array List of cron jobs.
|
||||
*/
|
||||
public static function get_cron_jobs_list() {
|
||||
$cron_jobs = _get_cron_array();
|
||||
$cron_jobs_list = array();
|
||||
|
||||
$failed_jobs = 0;
|
||||
|
||||
if (empty($cron_jobs)) {
|
||||
return array(); // Return an empty array if no cron jobs are found.
|
||||
}
|
||||
|
||||
$current_timestamp = time();
|
||||
$cron_jobs_list['Failed cron jobs'] = $failed_jobs;
|
||||
foreach ($cron_jobs as $timestamp => $cron_events) {
|
||||
foreach ($cron_events as $event_hook => $event_data) {
|
||||
foreach ($event_data as $schedule => $callback) {
|
||||
if ($timestamp < $current_timestamp) {
|
||||
$failed_jobs++;
|
||||
}
|
||||
$schedule = $callback['schedule'];
|
||||
$cron_jobs_list[$event_hook] = $schedule;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return apply_filters('aiowp_security_get_cron_jobs_info', $cron_jobs_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get debug log
|
||||
*
|
||||
* @param bool $html Whether to return the debug log as HTML
|
||||
*
|
||||
* @return string Debug log
|
||||
*/
|
||||
private static function get_debug_log($html = false) {
|
||||
global $wpdb, $aio_wp_security;
|
||||
|
||||
if ('1' === $aio_wp_security->configs->get_value('aiowps_enable_debug')) {
|
||||
|
||||
$debug_log_tbl = AIOWPSEC_TBL_DEBUG_LOG;
|
||||
|
||||
$where_sql = is_super_admin() ? '' : 'WHERE site_id = %d';
|
||||
$query = "SELECT * FROM {$debug_log_tbl} {$where_sql} ORDER BY id DESC LIMIT 100";
|
||||
|
||||
$debug_logs = is_super_admin() ? $wpdb->get_results($query, ARRAY_A) : $wpdb->get_results($wpdb->prepare($query, get_current_blog_id()), ARRAY_A);
|
||||
|
||||
if ($html) {
|
||||
$debug_log = "<br><h3>Debug log</h3>";
|
||||
} else {
|
||||
$debug_log = "\n --- Debug log --- \n\n";
|
||||
}
|
||||
|
||||
foreach ($debug_logs as $log) {
|
||||
$date_time = esc_html($log['created']);
|
||||
$level = esc_html($log['level']);
|
||||
$message = esc_html($log['message']);
|
||||
$type = esc_html($log['type']);
|
||||
|
||||
if ($html) {
|
||||
$debug_log .= "<strong>Only the most recent 100 logs are displayed</strong><br>";
|
||||
$debug_log .= "<strong>Date and time:</strong> $date_time<br>";
|
||||
$debug_log .= "<strong>Level:</strong> $level<br>";
|
||||
$debug_log .= "<strong>Message:</strong> $message<br>";
|
||||
$debug_log .= "<strong>Type:</strong> $type<br><br>";
|
||||
} else {
|
||||
$debug_log .= "Only the most recent 100 logs are displayed.\n";
|
||||
$debug_log .= "Date and time: $date_time\n";
|
||||
$debug_log .= "Level: $level\n";
|
||||
$debug_log .= "Message: $message\n";
|
||||
$debug_log .= "Type: $type\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
return apply_filters('aiowp_security_get_debug_log_info', $debug_log);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the IP address detection methods and their status
|
||||
*
|
||||
* @return array IP detection methods and their status
|
||||
*/
|
||||
private static function get_ip_detection_methods() {
|
||||
global $aio_wp_security;
|
||||
$ip_detection_methods = AIOS_Abstracted_Ids::get_ip_retrieve_methods();
|
||||
|
||||
$active_method = $aio_wp_security->configs->get_site_value('aiowps_ip_retrieve_method'); // In a multisite network, this setting is available for the main site only.
|
||||
$active_method = empty($active_method) ? 0 : (int) $active_method;
|
||||
|
||||
$ip_detection_list = array();
|
||||
|
||||
foreach ($ip_detection_methods as $method => $variable) {
|
||||
$status = ($method === $active_method) ? ' - ' . __('status', 'all-in-one-wp-security-and-firewall') . ': ' . __('On', 'all-in-one-wp-security-and-firewall') : '';
|
||||
$ip_address = (!empty($_SERVER[$variable])) ? $_SERVER[$variable] : '';
|
||||
$ip_detection_list[$variable] = __('IP', 'all-in-one-wp-security-and-firewall') . ': ' . $ip_address . $status;
|
||||
}
|
||||
|
||||
return $ip_detection_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current MySQL user has the specified privilege
|
||||
*
|
||||
* @param string $privilege Privilege to check
|
||||
*
|
||||
* @return string 'OK' if the user has the privilege, 'Not OK' otherwise
|
||||
*/
|
||||
private static function check_mysql_privilege($privilege) {
|
||||
global $wpdb;
|
||||
$grants = $wpdb->get_results("SHOW GRANTS FOR CURRENT_USER", ARRAY_N);
|
||||
foreach ($grants as $grant) {
|
||||
foreach ($grant as $grant_string) {
|
||||
if (strpos(strtoupper($grant_string), 'ALL PRIVILEGES') !== false || strpos(strtoupper($grant_string), strtoupper($privilege)) !== false) {
|
||||
return 'OK';
|
||||
}
|
||||
}
|
||||
}
|
||||
return 'Not OK';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the debug report
|
||||
*
|
||||
* @return string Debug report
|
||||
*/
|
||||
public static function generate_report() {
|
||||
|
||||
$data = '';
|
||||
$section_content = array();
|
||||
|
||||
foreach (self::$sections as $title => $method) {
|
||||
$section_title = esc_html($title);
|
||||
$section_content = self::$method();
|
||||
|
||||
// Check if the section content is empty, and handle accordingly.
|
||||
if (empty($section_content)) {
|
||||
$section_content = array('No data available');
|
||||
}
|
||||
|
||||
$data .= AIOWPSecurity_Reporting::generate_report_sections('table', $section_content, $section_title);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a report for the diagnostics page
|
||||
*
|
||||
* @param string $title The title of the report
|
||||
*
|
||||
* @return string Report textarea
|
||||
*/
|
||||
public static function generate_report_textarea($title) {
|
||||
$main_content = '';
|
||||
|
||||
$report_content = $title . "\n\n===================================\n";
|
||||
|
||||
foreach (self::$sections as $title => $method) {
|
||||
$section_title = esc_html($title);
|
||||
$section_content = self::$method();
|
||||
$main_content .= AIOWPSecurity_Reporting::generate_report_sections('text', $section_content, $section_title);
|
||||
}
|
||||
|
||||
$main_content .= self::get_debug_log();
|
||||
|
||||
$report_content = apply_filters('aiowp_security_generate_report_content', $report_content . $main_content);
|
||||
|
||||
$escaped_content = esc_textarea($report_content);
|
||||
|
||||
return '<textarea id="report-textarea" rows="25" cols="100">' . $escaped_content . '</textarea>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sender action button and field
|
||||
*
|
||||
* @return string sender action button and email field
|
||||
*/
|
||||
public static function add_sender_report_actions() {
|
||||
$report_sections = '';
|
||||
foreach (self::$sections as $title => $method) {
|
||||
$section_title = esc_html($title);
|
||||
$section_content = self::$method();
|
||||
$report_sections .= AIOWPSecurity_Reporting::generate_report_sections('table', $section_content, $section_title);
|
||||
}
|
||||
$encoded_report_sections = htmlentities($report_sections);
|
||||
|
||||
$data = '<div><form action="' . esc_url(admin_url('admin-post.php')) . '" method="POST"><br>';
|
||||
$data .= '<input type="email" id="report_email" placeholder="' . esc_attr__('Enter your email address', 'all-in-one-wp-security-and-firewall') . '" value="' . esc_attr(self::get_current_user_email()) . '" required><br><br>';
|
||||
$data .= '<input type="hidden" id="report_sections" value="'.$encoded_report_sections.'">';
|
||||
$data .= '<button class="button" id="send-report">' . esc_html__('Send report', 'all-in-one-wp-security-and-firewall') . '</button>';
|
||||
$data .= '</form><div id="report-response"></div></div>';
|
||||
|
||||
// Allow only safe HTML in the response
|
||||
echo wp_kses($data, array(
|
||||
'div' => array(),
|
||||
'form' => array(
|
||||
'action' => true,
|
||||
'method' => true,
|
||||
),
|
||||
'input' => array(
|
||||
'type' => true,
|
||||
'id' => true,
|
||||
'placeholder' => true,
|
||||
'value' => true,
|
||||
'required' => true,
|
||||
),
|
||||
'button' => array(
|
||||
'class' => true,
|
||||
'id' => true,
|
||||
),
|
||||
'br' => array(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current user email if admin or the site admin email
|
||||
*
|
||||
* @return string The email address
|
||||
*/
|
||||
private static function get_current_user_email() {
|
||||
$user = wp_get_current_user();
|
||||
if ($user && in_array('administrator', (array) $user->roles, true)) {
|
||||
return $user->user_email;
|
||||
} else {
|
||||
return get_option('admin_email');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the report email
|
||||
*
|
||||
* @global AIO_WP_Security $aio_wp_security
|
||||
*
|
||||
* @param string $email The email address to send the report to.
|
||||
* @param string $sections The report sections html.
|
||||
*
|
||||
* @return boolean True if the email was sent successfully, false otherwise
|
||||
*/
|
||||
public static function send_report($email, $sections) {
|
||||
global $aio_wp_security;
|
||||
|
||||
$report = '<html><head><style>table{border-collapse:collapse;width:100%}th,td{border:1px solid #ddd;padding:8px}tr:nth-child(2n){background-color:#f2f2f2}tr:hover{background-color:#ddd}h3{margin-top:20px;}</style><head>';
|
||||
$report .= '<body><h2>' . esc_html('All-In-One Security diagnostics report') . '</h2>';
|
||||
|
||||
$site_name = esc_html(get_bloginfo('name'));
|
||||
$report .= '<p>' . 'Site name' . ': ' . $site_name . "</p>";
|
||||
|
||||
$site_url = esc_url(get_bloginfo('url'));
|
||||
$report .= '<p>' . 'Site URL' . ': ' . '<a href="' . esc_url($site_url) . '">' . $site_url . '</a></p>';
|
||||
|
||||
$current_datetime = date_i18n(get_option('date_format') . ' ' . get_option('time_format'));
|
||||
$report .= '<p>' . 'Date and time' . ': ' . $current_datetime . "</p>";
|
||||
|
||||
$report .= $sections;
|
||||
|
||||
$report .= self::get_debug_log(true);
|
||||
|
||||
$report .= '<br><br><p>' . esc_html('This report was generated by the All-In-One Security plugin.') . '</p></body></html>';
|
||||
$subject = esc_html('All-In-One Security - diagnostic report');
|
||||
|
||||
$result = $aio_wp_security->sender_obj->send_email($email, $subject, $report);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
+381
@@ -0,0 +1,381 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;//Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Scan {
|
||||
|
||||
public function __construct() {
|
||||
add_action('aiowps_perform_fcd_scan_tasks', array($this, 'aiowps_scheduled_fcd_scan_handler'));
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will recursively scan through all directories starting from the specified location
|
||||
* It will store the path/filename, last_modified and filesize values in a multi-dimensional associative array
|
||||
*/
|
||||
|
||||
/**
|
||||
* Will recursively scan through all directories starting from ABSPATH.
|
||||
* Will return array with the path/filename, last_modified and filesize values
|
||||
*
|
||||
* @global AIO_WP_Security $aio_wp_security
|
||||
* @return boolean|array
|
||||
*/
|
||||
public function execute_file_change_detection_scan() {
|
||||
global $aio_wp_security;
|
||||
$scan_result = array();
|
||||
$fcd_filename = $aio_wp_security->configs->get_value('aiowps_fcd_filename');
|
||||
if (empty($fcd_filename)) {
|
||||
// means that we haven't done a scan before, or,
|
||||
// the fcd file containing the results doesn't exist
|
||||
$random_suffix = AIOWPSecurity_Utility::generate_alpha_numeric_random_string(10);
|
||||
$fcd_filename = 'aiowps_fcd_data_' . $random_suffix;
|
||||
$aio_wp_security->configs->set_value('aiowps_fcd_filename', $fcd_filename, true);
|
||||
}
|
||||
|
||||
$fcd_data = self::get_fcd_data(); // get previous scan data if any
|
||||
|
||||
if (false === $fcd_data) {
|
||||
// an error occurred so return
|
||||
return false;
|
||||
}
|
||||
|
||||
$scanned_data = $this->do_file_change_scan();
|
||||
|
||||
if (empty($fcd_data)) {
|
||||
$this->save_fcd_data($scanned_data);
|
||||
$scan_result['initial_scan'] = '1';
|
||||
return $scan_result;
|
||||
} else {
|
||||
|
||||
$scan_result = $this->compare_scan_data($fcd_data['file_scan_data'], $scanned_data);
|
||||
|
||||
$scan_result['initial_scan'] = '';
|
||||
$this->save_fcd_data($scanned_data, $scan_result);
|
||||
if (!empty($scan_result['files_added']) || !empty($scan_result['files_removed']) || !empty($scan_result['files_changed'])) {
|
||||
//This means there was a change detected
|
||||
$aio_wp_security->configs->set_value('aiowps_fcds_change_detected', true, true);
|
||||
$aio_wp_security->debug_logger->log_debug(__METHOD__ . " - change to filesystem detected!");
|
||||
|
||||
$this->aiowps_send_file_change_alert_email($scan_result); //Send file change scan results via email if applicable
|
||||
} else {
|
||||
//Reset the change flag
|
||||
$aio_wp_security->configs->set_value('aiowps_fcds_change_detected', false, true);
|
||||
}
|
||||
return $scan_result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send email with notification about file changes detected by last scan.
|
||||
*
|
||||
* @global AIO_WP_Security $aio_wp_security
|
||||
* @param array $scan_result Array with scan result returned by compare_scan_data() method.
|
||||
*/
|
||||
public function aiowps_send_file_change_alert_email($scan_result) {
|
||||
global $aio_wp_security;
|
||||
if ('1' == $aio_wp_security->configs->get_value('aiowps_send_fcd_scan_email')) {
|
||||
$subject = __('All In One WP Security - File change detected', 'all-in-one-wp-security-and-firewall') . ' ' . AIOWPSecurity_Utility::convert_timestamp(null, 'l, F jS, Y \a\\t g:i a');
|
||||
//$attachment = array();
|
||||
$message = __('A file change was detected on your system for site URL', 'all-in-one-wp-security-and-firewall') . ' ' . network_site_url() . __('. Scan was generated on', 'all-in-one-wp-security-and-firewall') . ' ' . AIOWPSecurity_Utility::convert_timestamp(null, 'l, F jS, Y \a\\t g:i a');
|
||||
$message .= "\r\n\r\n".__('A summary of the scan results is shown below:', 'all-in-one-wp-security-and-firewall');
|
||||
$message .= "\r\n\r\n";
|
||||
$message .= self::get_file_change_summary($scan_result);
|
||||
$message .= "\r\n".__('Login to your site to view the scan details.', 'all-in-one-wp-security-and-firewall');
|
||||
|
||||
// Get the email address(es).
|
||||
$addresses = AIOWPSecurity_Utility::get_array_from_textarea_val($aio_wp_security->configs->get_value('aiowps_fcd_scan_email_address'));
|
||||
// If no explicit email address(es) are given, send email to site admin.
|
||||
$to = empty($addresses) ? array(get_site_option('admin_email')) : $addresses;
|
||||
if (!wp_mail($to, $subject, $message)) {
|
||||
$aio_wp_security->debug_logger->log_debug(__METHOD__ . " - File change notification email failed to send.", 4);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called via the following filter 'aiowps_perform_fcd_scan_tasks' and will start the file scan
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function aiowps_scheduled_fcd_scan_handler() {
|
||||
global $aio_wp_security;
|
||||
|
||||
if ('1' != $aio_wp_security->configs->get_value('aiowps_enable_automated_fcd_scan')) return;
|
||||
|
||||
$aio_wp_security->debug_logger->log_debug_cron(__METHOD__ . " - Scheduled fcd_scan is enabled. Checking now to see if scan needs to be done...");
|
||||
|
||||
$current_time = time();
|
||||
|
||||
$next_fcd_scan_time = self::get_next_scheduled_scan();
|
||||
|
||||
if ($next_fcd_scan_time <= $current_time) {
|
||||
// It's time to do a filescan
|
||||
$result = $this->execute_file_change_detection_scan();
|
||||
if (false === $result) {
|
||||
$aio_wp_security->debug_logger->log_debug(__METHOD__ . " - Scheduled filescan operation failed.", 4);
|
||||
} else {
|
||||
$aio_wp_security->configs->set_value('aiowps_last_fcd_scan_time', $current_time, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will get the next scheduled scan timestamp and return it
|
||||
*
|
||||
* @return int|bool - the next scheduled scan timestamp, or false if the scheduled scan is not setup
|
||||
*/
|
||||
public static function get_next_scheduled_scan() {
|
||||
global $aio_wp_security;
|
||||
|
||||
if ('1' != $aio_wp_security->configs->get_value('aiowps_enable_automated_fcd_scan')) return false;
|
||||
|
||||
$fcd_scan_frequency = $aio_wp_security->configs->get_value('aiowps_fcd_scan_frequency'); // Number of hours or days or months interval
|
||||
$interval_setting = $aio_wp_security->configs->get_value('aiowps_fcd_scan_interval'); // Hours/Days/Months
|
||||
switch ($interval_setting) {
|
||||
case '0':
|
||||
$interval = 'hours';
|
||||
break;
|
||||
case '1':
|
||||
$interval = 'days';
|
||||
break;
|
||||
case '2':
|
||||
$interval = 'weeks';
|
||||
break;
|
||||
}
|
||||
$last_fcd_scan_time = $aio_wp_security->configs->get_value('aiowps_last_fcd_scan_time');
|
||||
if (null == $last_fcd_scan_time) {
|
||||
// Set the last scan time to now so it can trigger for the next scheduled period
|
||||
$last_fcd_scan_time = time();
|
||||
$aio_wp_security->configs->set_value('aiowps_last_fcd_scan_time', $last_fcd_scan_time, true);
|
||||
} elseif (is_string($last_fcd_scan_time)) {
|
||||
$last_fcd_scan_time = strtotime($last_fcd_scan_time);
|
||||
$aio_wp_security->configs->set_value('aiowps_last_fcd_scan_time', $last_fcd_scan_time, true);
|
||||
}
|
||||
$next_fcd_scan_time = strtotime("+".abs($fcd_scan_frequency).$interval, $last_fcd_scan_time);
|
||||
|
||||
return $next_fcd_scan_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last filechange detection data which is stored in the special file.
|
||||
*
|
||||
* @global AIO_WP_Security $aio_wp_security
|
||||
* @return bool|array - false on failure, array on success
|
||||
*/
|
||||
public static function get_fcd_data() {
|
||||
// phpcs:disable WordPress.WP.AlternativeFunctions -- Silence wp_filesystem method suggestion. We cannot use that method.
|
||||
global $aio_wp_security;
|
||||
$aiowps_backup_dir = WP_CONTENT_DIR.'/'.AIO_WP_SECURITY_BACKUPS_DIR_NAME;
|
||||
|
||||
$fcd_filename = $aio_wp_security->configs->get_value('aiowps_fcd_filename');
|
||||
if (empty($fcd_filename)) {
|
||||
// means that we haven't done a scan before, or,
|
||||
// the fcd file containing the results doesn't exist
|
||||
$random_suffix = AIOWPSecurity_Utility::generate_alpha_numeric_random_string(10);
|
||||
$fcd_filename = 'aiowps_fcd_data_' . $random_suffix;
|
||||
$aio_wp_security->configs->set_value('aiowps_fcd_filename', $fcd_filename, true);
|
||||
}
|
||||
$results_file = $aiowps_backup_dir. '/'. $fcd_filename;
|
||||
|
||||
if (!is_file($results_file)) {
|
||||
if (is_dir($results_file)) {
|
||||
$new_dir_name = $results_file . '_backup';
|
||||
rename($results_file, $new_dir_name); //Rename the folder to create backup of the folder. This condition should not really happen, but if it does (user sets some non-sensible value), then it's better to not nuke the existing folder
|
||||
}
|
||||
$fp = @fopen($results_file, 'w'); //open for write - will create file if doesn't exist
|
||||
return array();
|
||||
}
|
||||
|
||||
if (empty(filesize($results_file))) {
|
||||
return array(); // if newly created file return empty array
|
||||
}
|
||||
|
||||
$fp = @fopen($results_file, 'r'); //open for read and write - will create file if doesn't exist
|
||||
if (false === $fp) {
|
||||
// Error
|
||||
$aio_wp_security->debug_logger->log_debug(__METHOD__ . " - fopen returned false when opening fcd data file");
|
||||
return false;
|
||||
}
|
||||
|
||||
$contents = fread($fp, filesize($results_file));
|
||||
fclose($fp);
|
||||
if (false === $contents) {
|
||||
// Error
|
||||
$aio_wp_security->debug_logger->log_debug(__METHOD__ . " - fread returned false when reading fcd data file");
|
||||
return false;
|
||||
} else {
|
||||
|
||||
$fcd_file_contents = json_decode($contents, true);
|
||||
if (isset($fcd_file_contents['file_scan_data'])) {
|
||||
return $fcd_file_contents;
|
||||
} else {
|
||||
return array();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// phpcs:enable WordPress.WP.AlternativeFunctions -- Silence wp_filesystem method suggestion. We cannot use that method.
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively scan the entire $start_dir directory and return file size
|
||||
* and last modified date of every regular file. Ignore files and file
|
||||
* types specified in file scanner settings.
|
||||
*
|
||||
* @global AIO_WP_Security $aio_wp_security
|
||||
* @param string $start_dir
|
||||
* @return array
|
||||
*/
|
||||
public function do_file_change_scan($start_dir = ABSPATH) {
|
||||
global $aio_wp_security;
|
||||
$filescan_data = array();
|
||||
// Iterator key is absolute file path, iterator value is SplFileInfo object,
|
||||
// iteration skips '..' and '.' records, because we're not interested in directories.
|
||||
$dit = new RecursiveDirectoryIterator($start_dir, FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::SKIP_DOTS);
|
||||
$rit = new RecursiveIteratorIterator($dit, RecursiveIteratorIterator::SELF_FIRST, RecursiveIteratorIterator::CATCH_GET_CHILD);
|
||||
|
||||
// Grab files/directories to skip
|
||||
$files_to_skip = AIOWPSecurity_Utility::explode_trim_filter_empty($aio_wp_security->configs->get_value('aiowps_fcd_exclude_files'));
|
||||
// Grab (lowercased) file types to skip
|
||||
$file_types_to_skip = AIOWPSecurity_Utility::explode_trim_filter_empty(strtolower($aio_wp_security->configs->get_value('aiowps_fcd_exclude_filetypes')));
|
||||
|
||||
$start_dir_length = strlen($start_dir);
|
||||
|
||||
foreach ($rit as $filename => $fileinfo) {
|
||||
|
||||
if (!file_exists($filename) || is_dir($filename)) {
|
||||
continue; // if file doesn't exist or is a directory move on to next iteration
|
||||
}
|
||||
|
||||
if ($fileinfo->getFilename() == 'wp-security-log-cron-job.txt' || $fileinfo->getFilename() == 'wp-security-log.txt') {
|
||||
continue; // Skip AIOS log files
|
||||
}
|
||||
|
||||
// Let's omit any file types from the scan which were specified in the settings if necessary
|
||||
if (!empty($file_types_to_skip)) {
|
||||
//$current_file_ext = strtolower($fileinfo->getExtension()); //getExtension() only available on PHP 5.3.6 or higher
|
||||
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||
if (in_array($ext, $file_types_to_skip)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Let's omit specific files or directories from the scan which were specified in the settings
|
||||
if (!empty($files_to_skip)) {
|
||||
|
||||
$skip_this = false;
|
||||
foreach ($files_to_skip as $f_or_dir) {
|
||||
// Expect files/dirs to be specified relatively to $start_dir,
|
||||
// so start searching at $start_dir_length offset.
|
||||
if (strpos($filename, $f_or_dir, $start_dir_length) !== false) {
|
||||
$skip_this = true;
|
||||
break; // !
|
||||
}
|
||||
}
|
||||
if ($skip_this) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$filescan_data[$filename] = array(
|
||||
'last_modified' => $fileinfo->getMTime(),
|
||||
'filesize' => $fileinfo->getSize(),
|
||||
);
|
||||
|
||||
}
|
||||
return $filescan_data;
|
||||
}
|
||||
|
||||
public function compare_scan_data($last_scan_data, $new_scanned_data) {
|
||||
// Identify new files added: get all files which are in the new scan but not present in the old scan
|
||||
$files_added = @array_diff_key($new_scanned_data, $last_scan_data);
|
||||
// Identify files deleted: get all files which are in the old scan but not present in the new scan
|
||||
$files_removed = @array_diff_key($last_scan_data, $new_scanned_data);
|
||||
// Identify existing files: get all files which are in new scan, but were not added
|
||||
$files_kept = @array_diff_key($new_scanned_data, $files_added);
|
||||
|
||||
$files_changed = array();
|
||||
|
||||
// Loop through existing files and determine, if they have been changed
|
||||
foreach ($files_kept as $filename => $new_scan_meta) {
|
||||
$last_scan_meta = $last_scan_data[$filename];
|
||||
// Check filesize and last_modified values
|
||||
if (($new_scan_meta['last_modified'] !== $last_scan_meta['last_modified']) || ($new_scan_meta['filesize'] !== $last_scan_meta['filesize'])) {
|
||||
$files_changed[$filename] = $new_scan_meta;
|
||||
}
|
||||
}
|
||||
|
||||
// Create single array of all changes
|
||||
return array(
|
||||
'files_added' => $files_added,
|
||||
'files_removed' => $files_removed,
|
||||
'files_changed' => $files_changed,
|
||||
);
|
||||
}
|
||||
|
||||
public static function get_file_change_summary($scan_result) {
|
||||
$scan_summary = "";
|
||||
if (!empty($scan_result['files_added'])) {
|
||||
//Output of files added
|
||||
$scan_summary .= "\r\n".__('The following files were added to your host', 'all-in-one-wp-security-and-firewall').":\r\n";
|
||||
foreach ($scan_result['files_added'] as $key => $value) {
|
||||
$scan_summary .= "\r\n".$key.' ('.__('modified on:', 'all-in-one-wp-security-and-firewall'). ' ' . AIOWPSecurity_Utility::convert_timestamp($value['last_modified']).')';
|
||||
}
|
||||
$scan_summary .= "\r\n======================================\r\n";
|
||||
}
|
||||
if (!empty($scan_result['files_removed'])) {
|
||||
//Output of files removed
|
||||
$scan_summary .= "\r\n".__('The following files were removed from your host', 'all-in-one-wp-security-and-firewall').":\r\n";
|
||||
foreach ($scan_result['files_removed'] as $key => $value) {
|
||||
$scan_summary .= "\r\n".$key.' ('.__('modified on:', 'all-in-one-wp-security-and-firewall'). ' ' . AIOWPSecurity_Utility::convert_timestamp($value['last_modified']).')';
|
||||
}
|
||||
$scan_summary .= "\r\n======================================\r\n";
|
||||
}
|
||||
|
||||
if (!empty($scan_result['files_changed'])) {
|
||||
//Output of files changed
|
||||
$scan_summary .= "\r\n".__('The following files were changed on your host', 'all-in-one-wp-security-and-firewall').":\r\n";
|
||||
foreach ($scan_result['files_changed'] as $key => $value) {
|
||||
$scan_summary .= "\r\n".$key.' ('.__('modified on:', 'all-in-one-wp-security-and-firewall'). ' ' . AIOWPSecurity_Utility::convert_timestamp($value['last_modified']).')';
|
||||
}
|
||||
$scan_summary .= "\r\n======================================\r\n";
|
||||
}
|
||||
|
||||
return $scan_summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves file change detection data into a special file
|
||||
*
|
||||
* @global AIO_WP_Security $aio_wp_security
|
||||
* @param type $scanned_data
|
||||
* @param type $scan_result
|
||||
* @return boolean
|
||||
*/
|
||||
public function save_fcd_data($scanned_data, $scan_result = array()) {
|
||||
global $aio_wp_security;
|
||||
|
||||
$date_time = current_time('mysql');
|
||||
$data = array('date_time' => $date_time, 'file_scan_data' => $scanned_data, 'last_scan_result' => $scan_result);
|
||||
|
||||
$fcd_filename = $aio_wp_security->configs->get_value('aiowps_fcd_filename');
|
||||
$aiowps_backup_dir = WP_CONTENT_DIR.'/'.AIO_WP_SECURITY_BACKUPS_DIR_NAME;
|
||||
|
||||
if (!AIOWPSecurity_Utility_File::create_dir($aiowps_backup_dir)) {
|
||||
$aio_wp_security->debug_logger->log_debug(__METHOD__ . " - Creation of DB backup directory failed!", 4);
|
||||
return false;
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.WP.AlternativeFunctions -- Silence wp_filesystem method suggestion. We cannot use that method.
|
||||
$results_file = $aiowps_backup_dir. '/'. $fcd_filename;
|
||||
$fp = fopen($results_file, 'w');
|
||||
fwrite($fp, json_encode($data));
|
||||
fclose($fp);
|
||||
// phpcs:enable WordPress.WP.AlternativeFunctions -- Silence wp_filesystem method suggestion. We cannot use that method.
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH') && !defined('AIOWPS_FIREWALL_DIR')) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
if (class_exists('AIOS_Firewall_Resource_Unavailable')) return;
|
||||
|
||||
/**
|
||||
* Defines the methods to run when firewall resources failed to load.
|
||||
*/
|
||||
class AIOS_Firewall_Resource_Unavailable {
|
||||
|
||||
protected $resource;
|
||||
|
||||
/**
|
||||
* Constructs an unavailable firewall resource object.
|
||||
*
|
||||
* @param string $resource
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($resource) {
|
||||
$this->resource = $resource;
|
||||
do_action('aios_firewall_unavailable_resource', $this->resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles call operation on an unavailable firewall resource.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __call($name, $arguments) {
|
||||
do_action('aios_firewall_unavailable_resource_method_call', $this->resource, $name, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles call operation on an unavailable static firewall resource.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function __callStatic($name, $arguments) {
|
||||
do_action('aios_firewall_unavailable_resource_static_method_call', $name, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles get operation on an unavailable firewall resource.
|
||||
*
|
||||
* @param string $name
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __get($name) {
|
||||
do_action('aios_firewall_unavailable_resource_get_property', $this->resource, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles set operation on an unavailable firewall resource.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __set($name, $value) {
|
||||
do_action('aios_firewall_unavailable_resource_set_property', $this->resource, $name, $value);
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH') && !defined('AIOWPS_FIREWALL_DIR')) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
if (class_exists('AIOS_Firewall_Resource')) return;
|
||||
|
||||
/**
|
||||
* Gives us access to our firewall's resources by proxy.
|
||||
*/
|
||||
class AIOS_Firewall_Resource {
|
||||
|
||||
/**
|
||||
* Different possible resources.
|
||||
*/
|
||||
const CONFIG = 'aiowps_firewall_config';
|
||||
const MESSAGE_STORE = 'aiowps_firewall_message_store';
|
||||
const CONSTANTS = 'aiowps_firewall_constants';
|
||||
const ALLOW_LIST = '\AIOWPS\Firewall\Allow_List';
|
||||
const UTILITY = '\AIOWPS\Firewall\Utility';
|
||||
|
||||
/**
|
||||
* Requests a firewall resource.
|
||||
*
|
||||
* @param string $resource
|
||||
*
|
||||
* @return \AIOWPS\Firewall\Config|\AIOWPS\Firewall\Message_Store|\AIOWPS\Firewall\Constants|\AIOWPS\Firewall\Allow_List|\AIOWPS\Firewall\Utility
|
||||
*/
|
||||
public static function request($resource) {
|
||||
switch ($resource) {
|
||||
case self::CONFIG:
|
||||
case self::MESSAGE_STORE:
|
||||
case self::CONSTANTS:
|
||||
return isset($GLOBALS[$resource]) ? $GLOBALS[$resource] : new AIOS_Firewall_Resource_Unavailable($resource);
|
||||
case self::ALLOW_LIST:
|
||||
case self::UTILITY:
|
||||
return class_exists($resource) ? $resource : new AIOS_Firewall_Resource_Unavailable($resource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if all of the firewall resources are loaded.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function all_loaded() {
|
||||
return isset($GLOBALS[self::CONFIG])
|
||||
&& isset($GLOBALS[self::MESSAGE_STORE])
|
||||
&& isset($GLOBALS[self::CONSTANTS])
|
||||
&& class_exists(self::ALLOW_LIST)
|
||||
&& class_exists(self::UTILITY);
|
||||
}
|
||||
}
|
||||
Executable
+1025
File diff suppressed because it is too large
Load Diff
+345
@@ -0,0 +1,345 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH') && !defined('AIOWPS_FIREWALL_DIR')) {
|
||||
exit; //Exit if accessed directly
|
||||
}
|
||||
|
||||
require_once(__DIR__.'/wp-security-abstract-ids.php');
|
||||
|
||||
/**
|
||||
* Helper class for firewall as shared library
|
||||
* Use core php not wordpress.
|
||||
*/
|
||||
if (class_exists('AIOS_Helper')) return;
|
||||
|
||||
class AIOS_Helper {
|
||||
|
||||
private static $messages = array();
|
||||
|
||||
/**
|
||||
* Maps a firewall rule to its admin URL
|
||||
*
|
||||
* @param string $rule - The key to the rule's URL. The key format is '<rule_name>::<rule_family>'
|
||||
* @return string
|
||||
*/
|
||||
public static function get_firewall_rule_location($rule) {
|
||||
//normalise key
|
||||
$rule = strtolower($rule);
|
||||
|
||||
$basic_firewall = array(
|
||||
'completely block xmlrpc::general' => 'page=aiowpsec_firewall',
|
||||
);
|
||||
$additional_firewall = array(
|
||||
'advanced character filter::general' => 'page=aiowpsec_firewall&tab=additional-firewall',
|
||||
'bad query strings::general' => 'page=aiowpsec_firewall&tab=additional-firewall',
|
||||
'proxy comment posting::general' => 'page=aiowpsec_firewall&tab=additional-firewall',
|
||||
);
|
||||
$bruteforce = array(
|
||||
'cookie based prevent bruteforce::bruteforce' => 'page=aiowpsec_brute_force&tab=cookie-based-brute-force-prevention',
|
||||
);
|
||||
$blacklist = array(
|
||||
'blocked ips::blacklist' => 'page=aiowpsec_blacklist',
|
||||
'blocked user agents::blacklist' => 'page=aiowpsec_blacklist',
|
||||
);
|
||||
$firewall_6g = array(
|
||||
'block request methods::6g' => 'page=aiowpsec_firewall&tab=6g-firewall',
|
||||
'block query strings::6g' => 'page=aiowpsec_firewall&tab=6g-firewall',
|
||||
'block referrer strings::6g' => 'page=aiowpsec_firewall&tab=6g-firewall',
|
||||
'block request strings::6g' => 'page=aiowpsec_firewall&tab=6g-firewall',
|
||||
'block user-agents::6g' => 'page=aiowpsec_firewall&tab=6g-firewall',
|
||||
);
|
||||
|
||||
// merge all the locations to one
|
||||
$locations = array_merge($firewall_6g, $blacklist, $bruteforce, $basic_firewall, $additional_firewall);
|
||||
return isset($locations[$rule]) ? $locations[$rule] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get server detected visitor IP Address.
|
||||
*
|
||||
* @return String visitor IP Address.
|
||||
*/
|
||||
public static function get_server_detected_user_ip_address() {
|
||||
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
|
||||
|
||||
// check if user configured custom IP retrieval method
|
||||
$ip_method_id = empty($aiowps_firewall_config) ? '' : $aiowps_firewall_config->get_value('aios_ip_retrieve_method');
|
||||
|
||||
$visitor_ip = '';
|
||||
$ip_retrieve_methods = AIOS_Abstracted_Ids::get_ip_retrieve_methods();
|
||||
|
||||
if (empty($ip_method_id) || !isset($ip_retrieve_methods[$ip_method_id])) {
|
||||
$ip_method_id = 0;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- WordPress API cannot be used here as it's loaded independently of WordPress
|
||||
$visitor_ip = isset($_SERVER[$ip_retrieve_methods[$ip_method_id]]) ? $_SERVER[$ip_retrieve_methods[$ip_method_id]] : '';
|
||||
|
||||
// Check if multiple IPs were given - these will be present as comma-separated list
|
||||
if (preg_match('/^([^,]+),/', $visitor_ip, $matches)) $visitor_ip = $matches[1];
|
||||
|
||||
// Now remove port portion if ipv4 address with port, for ipv6 it was making issue so using fiter_var valid ip checked first.
|
||||
if (!filter_var($visitor_ip, FILTER_VALIDATE_IP) && preg_match('/(.+):\d+$/', $visitor_ip, $matches)) $visitor_ip = $matches[1];
|
||||
|
||||
if (!filter_var($visitor_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && !filter_var($visitor_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- WordPress API cannot be used here as it's loaded independently of WordPress
|
||||
$visitor_ip = empty($_SERVER['REMOTE_ADDR']) ? '' : $_SERVER['REMOTE_ADDR'];
|
||||
}
|
||||
|
||||
return $visitor_ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user IP Address.
|
||||
*
|
||||
* @return string User IP Address.
|
||||
*/
|
||||
public static function get_user_ip_address() {
|
||||
static $visitor_ip;
|
||||
if (isset($visitor_ip)) {
|
||||
//already set in the page request
|
||||
return $visitor_ip;
|
||||
}
|
||||
|
||||
$visitor_ip = self::get_server_detected_user_ip_address();
|
||||
|
||||
if (in_array($visitor_ip, array('', '127.0.0.1', '::1'))) {
|
||||
$external_ip_address = self::get_external_ip_address();
|
||||
if (false != $external_ip_address) {
|
||||
$visitor_ip = $external_ip_address;
|
||||
}
|
||||
}
|
||||
|
||||
return $visitor_ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check user IP Address is with in list.
|
||||
*
|
||||
* @param array $ip_address_list
|
||||
*
|
||||
* @return boolean.
|
||||
*/
|
||||
public static function is_user_ip_address_within_list($ip_address_list) {
|
||||
|
||||
if (!(include_once AIOWPS_FIREWALL_DIR.'/../../vendor/mlocati/ip-lib/ip-lib.php')) {
|
||||
throw new \Exception("AIOS_Helper::is_user_ip_address_within_list failed to load ip-lib.php");
|
||||
}
|
||||
|
||||
if (empty($ip_address_list)) return false;
|
||||
|
||||
$user_ip = self::get_user_ip_address();
|
||||
$user_ip_address = \IPLib\Factory::parseAddressString($user_ip);
|
||||
if (null != $user_ip_address) {
|
||||
$user_ip_address_type = $user_ip_address->getAddressType();
|
||||
$user_ipv4_address_parsed = (6 == $user_ip_address_type) ? $user_ip_address->toIPv4() : $user_ip_address;
|
||||
// linear search used to compare ips list.
|
||||
foreach ($ip_address_list as $ip_address) {
|
||||
if (preg_match("/\/|\*/", $ip_address)) { // checks user ipadress with in range of ip range.
|
||||
$range = \IPLib\Factory::parseRangeString($ip_address);
|
||||
if (null != $range && $range->contains($user_ip_address)) return true;
|
||||
} elseif ($ip_address == $user_ip) {
|
||||
return true;
|
||||
} elseif (null != $user_ipv4_address_parsed) { // check for ip matches if ipv6 to ipv4 or vice versa
|
||||
$ip_address_parsed = \IPLib\Factory::parseAddressString($ip_address);
|
||||
if (null != $ip_address_parsed) {
|
||||
$ip_address_parsed_type = $ip_address_parsed->getAddressType();
|
||||
$ipv4_address_parsed = (6 == $ip_address_parsed_type) ? $ip_address_parsed->toIPv4() : $ip_address_parsed;
|
||||
if ((string) $user_ipv4_address_parsed == (string) $ipv4_address_parsed) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user IP Address using an external service.
|
||||
* This can be used as a fallback for users on localhost where
|
||||
* get_ip_address() will be a local IP and non-geolocatable.
|
||||
*
|
||||
* @return string|boolean external ip address if from one of lookup service gets response otherwise false.
|
||||
*/
|
||||
public static function get_external_ip_address() {
|
||||
$aiowps_firewall_constants = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONSTANTS);
|
||||
$external_ip_address = false;
|
||||
|
||||
//Running from cronjob REQUEST_URI is empty or DOING_CRON, if from command line cli is PHP_SAPI, constant set by user
|
||||
if (empty($_SERVER['REQUEST_URI']) || (defined('DOING_CRON') && DOING_CRON) || 'cli' == PHP_SAPI || $aiowps_firewall_constants->AIOS_DISABLE_EXTERNAL_IP_ADDR) return $external_ip_address;
|
||||
|
||||
$ip_lookup_services = AIOS_Abstracted_Ids::get_ip_lookup_services();
|
||||
$ip_lookup_services_keys = array_keys($ip_lookup_services);
|
||||
shuffle($ip_lookup_services_keys);
|
||||
|
||||
foreach ($ip_lookup_services_keys as $service_name) {
|
||||
$service_endpoint = $ip_lookup_services[$service_name];
|
||||
$response = self::request_remote($service_endpoint);
|
||||
if (!empty($response) && filter_var($response, FILTER_VALIDATE_IP)) {
|
||||
$external_ip_address = $response;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $external_ip_address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote url request.
|
||||
*
|
||||
* @global \AIOWPS\Firewall\Constants $aiowps_firewall_constants
|
||||
*
|
||||
* @param string $url url to be requested.
|
||||
* @param array $args request args array.
|
||||
*
|
||||
* @return string response.
|
||||
*/
|
||||
public static function request_remote($url, $args = array()) {
|
||||
$aiowps_firewall_constants = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONSTANTS);
|
||||
$response = '';
|
||||
$aiowps_firewall_utility = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::UTILITY);
|
||||
$root = $aiowps_firewall_utility::get_root_dir();
|
||||
$includes = isset($aiowps_firewall_constants->WPINC) ? $aiowps_firewall_constants->WPINC : 'wp-includes';
|
||||
|
||||
//WP 6.2+ the request library updated to 2.0.5, class and interface files/directory moved and namespaced name used.
|
||||
if (!class_exists('WpOrg\Requests\Autoload')) {
|
||||
if (file_exists($root . $includes . '/Requests/src/Autoload.php')) {
|
||||
require_once($root . $includes . '/Requests/src/Autoload.php');
|
||||
WpOrg\Requests\Autoload::register();
|
||||
WpOrg\Requests\Requests::set_certificate_path($root . $includes . '/certificates/ca-bundle.crt');
|
||||
} elseif (!class_exists('Requests') && file_exists($root . $includes . '/class-requests.php')) {
|
||||
require_once($root . $includes . '/class-requests.php');
|
||||
Requests::register_autoloader();
|
||||
Requests::set_certificate_path($root . $includes . '/certificates/ca-bundle.crt');
|
||||
}
|
||||
}
|
||||
$timeout = isset($aiowps_firewall_constants->AIOS_REQUEST_TIMEOUT) ? $aiowps_firewall_constants->AIOS_REQUEST_TIMEOUT : 4;
|
||||
|
||||
try {
|
||||
$headers = isset($args['headers']) ? $args['headers'] : array();
|
||||
$data = isset($args['body']) ? $args['body'] : array();
|
||||
$method = isset($args['method']) ? $args['method'] : 'GET';
|
||||
$options = array('timeout' => $timeout);
|
||||
//WP 6.2+ the request library 2.0.5 namespaced name WpOrg\Requests\Requests instead just Requests
|
||||
if (class_exists('WpOrg\Requests\Requests')) {
|
||||
$request_response = WpOrg\Requests\Requests::request($url, $headers, $data, $method, $options);
|
||||
} elseif (class_exists('Requests')) {
|
||||
$request_response = Requests::request($url, $headers, $data, $method, $options);
|
||||
}
|
||||
$response = $request_response->body;
|
||||
} catch (\Exception $e) {
|
||||
$error_message = $e->getMessage();
|
||||
// timed out exception ignore it.
|
||||
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- PCP warning. Part of AIOS error reporting.
|
||||
if (!preg_match('/timed out/i', $error_message)) error_log('AIOS_Helper::request_remote exception - ' . $error_message);
|
||||
} catch (\Error $e) { // phpcs:ignore PHPCompatibility.Classes.NewClasses.errorFound -- this won't run on PHP 5.6 so we still want to catch it on other versions
|
||||
error_log('AIOS_Helper::request_remote error - ' . $e->getMessage()); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- PCP warning. Part of AIOS error reporting.
|
||||
}
|
||||
|
||||
if (empty($response) && ini_get('allow_url_fopen')) {
|
||||
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Cannot use WP API since Firewall is loaded outside WordPress.
|
||||
$response = @file_get_contents($url, false, stream_context_create(array('http' => array("timeout" => $timeout)))); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this to silence request failed warning for IP lookup services
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs reverse IP lookup for the given IP address
|
||||
*
|
||||
* @param string $ip_address - IP address to perform reverse lookup
|
||||
*
|
||||
* @return array - Reverse lookup data
|
||||
*/
|
||||
public static function get_ip_reverse_lookup($ip_address) {
|
||||
global $aio_wp_security;
|
||||
$reverse_lookup_data = array();
|
||||
$ip_reverse_lookup_services = AIOS_Abstracted_Ids::get_reverse_ip_lookup_services();
|
||||
$ip_reverse_lookup_services_keys = array_keys($ip_reverse_lookup_services);
|
||||
shuffle($ip_reverse_lookup_services_keys);
|
||||
|
||||
foreach ($ip_reverse_lookup_services_keys as $service_name) {
|
||||
$endpoint = $ip_reverse_lookup_services[$service_name];
|
||||
$url = sprintf($endpoint, $ip_address);
|
||||
$response = wp_safe_remote_get($url, array( 'timeout' => 2 ));
|
||||
|
||||
if (!is_wp_error($response) && $response['body']) {
|
||||
$data = json_decode($response['body'], true);
|
||||
if (!$data) {
|
||||
$aio_wp_security->debug_logger->log_debug("Error decoding IP lookup result", 4);
|
||||
return $reverse_lookup_data;
|
||||
}
|
||||
switch ($service_name) {
|
||||
case 'ip-api':
|
||||
$fields_to_copy = array('org', 'as');
|
||||
foreach ($fields_to_copy as $field) {
|
||||
$reverse_lookup_data[$field] = empty($data[$field]) ? null : $data[$field];
|
||||
}
|
||||
break;
|
||||
case 'ipinfo':
|
||||
$reverse_lookup_data['org'] = empty($data['org']) ? null : $data['org'];
|
||||
$reverse_lookup_data['as'] = $reverse_lookup_data['org'];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
$reverse_lookup_data = apply_filters('aiowps_login_lockdown_lookup_result', $reverse_lookup_data, $data, $service_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $reverse_lookup_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets hash of given string using auth Authentication scheme
|
||||
*
|
||||
* @param string $data - Plain text to hash.
|
||||
*
|
||||
* @return string - Hash of $data
|
||||
*/
|
||||
public static function get_hash($data) {
|
||||
$aiowps_firewall_constants = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONSTANTS);
|
||||
$salt = $aiowps_firewall_constants->AUTH_KEY.$aiowps_firewall_constants->AUTH_SALT;
|
||||
return hash_hmac('md5', $data, $salt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a message for a specific context.
|
||||
*
|
||||
* @param string $context The unique context identifier for the message.
|
||||
* @param string $message The message to store for the given context.
|
||||
* @param string $type The message type to store for the given context.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function set_message($context, $message, $type = 'info') {
|
||||
self::$messages[$context] = array('message' => $message, 'type' => $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a message for a specific context.
|
||||
*
|
||||
* @param string $context The unique context identifier for the message.
|
||||
*
|
||||
* @return array|null The message for the given context, or null if not found.
|
||||
*/
|
||||
public static function get_message($context) {
|
||||
return isset(self::$messages[$context]) ? self::$messages[$context] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear messages (optional, for cleanup purposes).
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clear_messages() {
|
||||
self::$messages = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function checks if the current request is an UpdraftCentral request by looking for a specific constant.
|
||||
*
|
||||
* @return boolean - True if the request is from UpdraftCentral, false otherwise.
|
||||
*/
|
||||
public static function is_updraft_central_request() {
|
||||
return defined('UPDRAFTCENTRAL_COMMAND') && UPDRAFTCENTRAL_COMMAND;
|
||||
}
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
if (!defined('ABSPATH')) die('No direct access allowed.');
|
||||
|
||||
/**
|
||||
* Class to utilize the 'Have I Been Pwned?' API.
|
||||
*/
|
||||
if (class_exists('AIOS_HIBP')) return;
|
||||
|
||||
class AIOS_HIBP {
|
||||
|
||||
/**
|
||||
* Checks a password against the HIBP database.
|
||||
*
|
||||
* @param string $password
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function password_is_pwned($password) {
|
||||
global $aio_wp_security;
|
||||
|
||||
$password_hash = sha1($password);
|
||||
$password_hash_prefix = substr($password_hash, 0, 5);
|
||||
$password_hash_suffix = substr($password_hash, 5);
|
||||
|
||||
$response_body = AIOWPSecurity_Utility_API::get('https://api.pwnedpasswords.com/range/' . $password_hash_prefix);
|
||||
|
||||
if (is_wp_error($response_body)) {
|
||||
$aio_wp_security->debug_logger->log_debug('Failed to query the HIBP api: ' . $response_body->get_error_message(), 4);
|
||||
return false;
|
||||
}
|
||||
|
||||
$response_body_array = explode("\n", $response_body);
|
||||
|
||||
foreach ($response_body_array as $suffix_and_count) {
|
||||
$suffix = explode(':', $suffix_and_count)[0];
|
||||
|
||||
if (strtolower($suffix) == $password_hash_suffix) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a password has been pwned when updating a user profile.
|
||||
*
|
||||
* @param WP_Error $errors
|
||||
* @param bool $update
|
||||
* @param stdClass $user
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function user_profile_update_check(&$errors, $update, &$user) {
|
||||
// Use get_error_code() instead of has_errors() for backward compatibility with WP 5.0.
|
||||
if ($errors->get_error_code() || empty($user->user_pass)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self::password_is_pwned($user->user_pass)) {
|
||||
$errors->add('pass', __('<strong>Error:</strong> This password has been exposed in a data breach, according to Have I Been Pwned (HIBP).'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a password has been pwned when resetting a password.
|
||||
*
|
||||
* @param WP_Error $errors
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function password_reset_check($errors) {
|
||||
// Use get_error_code() instead of has_errors() for backward compatibility with WP 5.0.
|
||||
if ($errors->get_error_code() || !isset($_POST['pass1']) || empty($_POST['pass1'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self::password_is_pwned($_POST['pass1'])) {
|
||||
$errors->add('pass', __('<strong>Error:</strong> This password has been exposed in a data breach, according to Have I Been Pwned (HIBP).'));
|
||||
}
|
||||
}
|
||||
}
|
||||
+498
@@ -0,0 +1,498 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;//Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Installer {
|
||||
|
||||
private static $db_tasks = array(
|
||||
'2.0.2' => array(
|
||||
'clean_audit_log_stacktraces',
|
||||
),
|
||||
'2.0.9' => array(
|
||||
'update_table_column_to_timestamp',
|
||||
),
|
||||
'2.0.10' => array(
|
||||
'delete_aiowps_temp_configs_option',
|
||||
),
|
||||
'2.1.4' => array(
|
||||
'update_tables_to_innodb',
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Run installer function.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function run_installer() {
|
||||
global $wpdb;
|
||||
if (function_exists('is_multisite') && is_multisite() && is_main_site()) {
|
||||
// check if it is a network activation - if so, run the activation function for each blog id
|
||||
$blogids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- There doesn't seem to be a documented alternative. Possible alternative get_sites(array('fields' => 'ids', 'number' => 0) but 'number' => 0 is not documented.
|
||||
foreach ($blogids as $blog_id) {
|
||||
switch_to_blog($blog_id);
|
||||
AIOWPSecurity_Installer::create_db_tables();
|
||||
AIOWPSecurity_Installer::migrate_db_tables();
|
||||
AIOWPSecurity_Installer::check_tasks();
|
||||
AIOWPSecurity_Configure_Settings::add_option_values();
|
||||
AIOWPSecurity_Configure_Settings::update_aiowpsec_db_version();
|
||||
restore_current_blog();
|
||||
}
|
||||
} else {
|
||||
AIOWPSecurity_Installer::create_db_tables();
|
||||
AIOWPSecurity_Installer::migrate_db_tables();
|
||||
AIOWPSecurity_Installer::check_tasks();
|
||||
AIOWPSecurity_Configure_Settings::add_option_values();
|
||||
AIOWPSecurity_Configure_Settings::update_aiowpsec_db_version();
|
||||
}
|
||||
|
||||
AIOWPSecurity_Installer::create_db_backup_dir(); // Create a backup dir in the WP uploads directory.
|
||||
}
|
||||
|
||||
/**
|
||||
* See if any database tasks need to be run, and perform them if so.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function check_tasks() {
|
||||
$our_version = AIO_WP_SECURITY_DB_VERSION;
|
||||
$db_version = get_option('aiowpsec_db_version');
|
||||
// database tasks not need to be run if first time install - false check added
|
||||
if (false != $db_version && version_compare($our_version, $db_version, '>')) {
|
||||
foreach (self::$db_tasks as $version => $updates) {
|
||||
if (version_compare($version, $db_version, '>')) {
|
||||
foreach ($updates as $update) {
|
||||
call_user_func(array(__CLASS__, $update));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function create_db_tables() {
|
||||
global $wpdb;
|
||||
|
||||
if (!function_exists('dbDelta')) {
|
||||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||
}
|
||||
|
||||
if (function_exists('is_multisite') && is_multisite()) {
|
||||
/*
|
||||
* FIX for multisite table creation case:
|
||||
* Although each table name is defined in a constant inside the wp-security-core.php,
|
||||
* we need to do this step for multisite case because we need to refresh the $wpdb->prefix value
|
||||
* otherwise it will contain the original blog id and not the current id we need.
|
||||
*
|
||||
*/
|
||||
$lockout_tbl_name = $wpdb->prefix.'aiowps_login_lockdown';
|
||||
$aiowps_global_meta_tbl_name = $wpdb->prefix.'aiowps_global_meta';
|
||||
$aiowps_event_tbl_name = $wpdb->prefix.'aiowps_events';
|
||||
$perm_block_tbl_name = $wpdb->prefix.'aiowps_permanent_block';
|
||||
} else {
|
||||
$lockout_tbl_name = AIOWPSEC_TBL_LOGIN_LOCKOUT;
|
||||
$aiowps_global_meta_tbl_name = AIOWPSEC_TBL_GLOBAL_META_DATA;
|
||||
$aiowps_event_tbl_name = AIOWPSEC_TBL_EVENTS;
|
||||
$perm_block_tbl_name = AIOWPSEC_TBL_PERM_BLOCK;
|
||||
}
|
||||
|
||||
$message_store_log_tbl_name = AIOWPSEC_TBL_MESSAGE_STORE;
|
||||
$audit_log_tbl_name = AIOWPSEC_TBL_AUDIT_LOG;
|
||||
$debug_log_tbl_name = AIOWPSEC_TBL_DEBUG_LOG;
|
||||
$logged_in_users_tbl_name = AIOWPSEC_TBL_LOGGED_IN_USERS;
|
||||
|
||||
$charset_collate = '';
|
||||
if (!empty($wpdb->charset)) {
|
||||
$charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
|
||||
} else {
|
||||
$charset_collate = "DEFAULT CHARSET=utf8";
|
||||
}
|
||||
if (!empty($wpdb->collate)) {
|
||||
$charset_collate .= " COLLATE $wpdb->collate";
|
||||
}
|
||||
|
||||
$ld_tbl_sql = "CREATE TABLE " . $lockout_tbl_name . " (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
user_id bigint(20) NOT NULL,
|
||||
user_login VARCHAR(150) NOT NULL,
|
||||
lockdown_date datetime NOT NULL DEFAULT '1000-10-10 10:00:00',
|
||||
created INTEGER UNSIGNED,
|
||||
release_date datetime NOT NULL DEFAULT '1000-10-10 10:00:00',
|
||||
released INTEGER UNSIGNED,
|
||||
failed_login_ip varchar(100) NOT NULL DEFAULT '',
|
||||
lock_reason varchar(128) NOT NULL DEFAULT '',
|
||||
unlock_key varchar(128) NOT NULL DEFAULT '',
|
||||
is_lockout_email_sent tinyint(1) NOT NULL DEFAULT '1',
|
||||
backtrace_log text NOT NULL,
|
||||
ip_lookup_result LONGTEXT DEFAULT NULL,
|
||||
PRIMARY KEY (id),
|
||||
KEY failed_login_ip (failed_login_ip),
|
||||
KEY is_lockout_email_sent (is_lockout_email_sent),
|
||||
KEY unlock_key (unlock_key)
|
||||
) ENGINE=InnoDB " . $charset_collate . ";";
|
||||
dbDelta($ld_tbl_sql);
|
||||
|
||||
$gm_tbl_sql = "CREATE TABLE " . $aiowps_global_meta_tbl_name . " (
|
||||
meta_id bigint(20) NOT NULL auto_increment,
|
||||
date_time datetime NOT NULL default '1000-10-10 10:00:00',
|
||||
created INTEGER UNSIGNED,
|
||||
meta_key1 varchar(255) NOT NULL,
|
||||
meta_key2 varchar(255) NOT NULL,
|
||||
meta_key3 varchar(255) NOT NULL,
|
||||
meta_key4 varchar(255) NOT NULL,
|
||||
meta_key5 varchar(255) NOT NULL,
|
||||
meta_value1 varchar(255) NOT NULL,
|
||||
meta_value2 text NOT NULL,
|
||||
meta_value3 text NOT NULL,
|
||||
meta_value4 longtext NOT NULL,
|
||||
meta_value5 longtext NOT NULL,
|
||||
PRIMARY KEY (meta_id)
|
||||
) ENGINE=InnoDB " . $charset_collate . ";";
|
||||
dbDelta($gm_tbl_sql);
|
||||
|
||||
$evt_tbl_sql = "CREATE TABLE " . $aiowps_event_tbl_name . " (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
event_type VARCHAR(150) NOT NULL DEFAULT '',
|
||||
username VARCHAR(150),
|
||||
user_id bigint(20),
|
||||
event_date datetime NOT NULL DEFAULT '1000-10-10 10:00:00',
|
||||
created INTEGER UNSIGNED,
|
||||
ip_or_host varchar(100),
|
||||
referer_info varchar(255),
|
||||
url varchar(255),
|
||||
country_code varchar(50),
|
||||
event_data longtext,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB " . $charset_collate . ";";
|
||||
dbDelta($evt_tbl_sql);
|
||||
|
||||
$pb_tbl_sql = "CREATE TABLE " . $perm_block_tbl_name . " (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
blocked_ip varchar(100) NOT NULL DEFAULT '',
|
||||
block_reason varchar(128) NOT NULL DEFAULT '',
|
||||
country_origin varchar(50) NOT NULL DEFAULT '',
|
||||
blocked_date datetime NOT NULL DEFAULT '1000-10-10 10:00:00',
|
||||
created INTEGER UNSIGNED,
|
||||
unblock tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (id),
|
||||
KEY blocked_ip (blocked_ip)
|
||||
) ENGINE=InnoDB " . $charset_collate . ";";
|
||||
dbDelta($pb_tbl_sql);
|
||||
|
||||
$audit_log_tbl_sql = "CREATE TABLE " . $audit_log_tbl_name . " (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
network_id bigint(20) NOT NULL DEFAULT '0',
|
||||
site_id bigint(20) NOT NULL DEFAULT '0',
|
||||
username varchar(60) NOT NULL DEFAULT '',
|
||||
ip VARCHAR(45) NOT NULL DEFAULT '',
|
||||
level varchar(25) NOT NULL DEFAULT '',
|
||||
event_type varchar(25) NOT NULL DEFAULT '',
|
||||
details text NOT NULL,
|
||||
stacktrace text NOT NULL,
|
||||
created INTEGER UNSIGNED,
|
||||
country_code varchar(50),
|
||||
PRIMARY KEY (id),
|
||||
INDEX username (username),
|
||||
INDEX ip (ip),
|
||||
INDEX level (level),
|
||||
INDEX event_type (event_type)
|
||||
) ENGINE=InnoDB " . $charset_collate . ";";
|
||||
dbDelta($audit_log_tbl_sql);
|
||||
|
||||
$debug_log_tbl_sql = "CREATE TABLE " . $debug_log_tbl_name . " (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
created datetime NOT NULL DEFAULT '1000-10-10 10:00:00',
|
||||
logtime INTEGER UNSIGNED,
|
||||
level varchar(25) NOT NULL DEFAULT '',
|
||||
network_id bigint(20) NOT NULL DEFAULT '0',
|
||||
site_id bigint(20) NOT NULL DEFAULT '0',
|
||||
message text NOT NULL,
|
||||
type varchar(25) NOT NULL DEFAULT '',
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB " . $charset_collate . ";";
|
||||
dbDelta($debug_log_tbl_sql);
|
||||
|
||||
$liu_tbl_sql = "CREATE TABLE " . $logged_in_users_tbl_name . " (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
user_id bigint(20) NOT NULL,
|
||||
username varchar(60) NOT NULL DEFAULT '',
|
||||
ip_address varchar(45) NOT NULL DEFAULT '',
|
||||
site_id bigint(20) NOT NULL,
|
||||
created integer UNSIGNED,
|
||||
expires integer UNSIGNED,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY unique_user_id (user_id),
|
||||
INDEX created (created),
|
||||
INDEX expires (expires),
|
||||
INDEX user_id (user_id),
|
||||
INDEX site_id (site_id)
|
||||
) ENGINE=InnoDB " . $charset_collate . ";";
|
||||
dbDelta($liu_tbl_sql);
|
||||
|
||||
$message_store_log_tbl_sql = "CREATE TABLE " . $message_store_log_tbl_name . " (
|
||||
id bigint(20) NOT NULL AUTO_INCREMENT,
|
||||
message_key text NOT NULL,
|
||||
message_value text NOT NULL,
|
||||
created INTEGER UNSIGNED,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB " . $charset_collate . ";";
|
||||
dbDelta($message_store_log_tbl_sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will handle any database table migrations
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function migrate_db_tables() {
|
||||
global $wpdb;
|
||||
|
||||
if (function_exists('is_multisite') && is_multisite()) {
|
||||
/*
|
||||
* FIX for multisite table creation case:
|
||||
* Although each table name is defined in a constant inside the wp-security-core.php,
|
||||
* we need to do this step for multisite case because we need to refresh the $wpdb->prefix value
|
||||
* otherwise it will contain the original blog id and not the current id we need.
|
||||
*
|
||||
*/
|
||||
$failed_login_tbl_name = $wpdb->prefix.'aiowps_failed_logins';
|
||||
$login_activity_tbl_name = $wpdb->prefix.'aiowps_login_activity';
|
||||
|
||||
} else {
|
||||
$failed_login_tbl_name = AIOWPSEC_TBL_FAILED_LOGINS;
|
||||
$login_activity_tbl_name = AIOWPSEC_TBL_USER_LOGIN_ACTIVITY;
|
||||
}
|
||||
|
||||
$audit_log_tbl_name = AIOWPSEC_TBL_AUDIT_LOG;
|
||||
$network_id = get_current_network_id();
|
||||
$site_id = get_current_blog_id();
|
||||
|
||||
$table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $wpdb->esc_like($failed_login_tbl_name))); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- There doesn't seem to be an alternative.
|
||||
if ($table_exists) {
|
||||
$import_details = array(
|
||||
'failed_login' => array(
|
||||
'imported' => true,
|
||||
)
|
||||
);
|
||||
$import_details = wp_json_encode($import_details, true);
|
||||
$table_migration_details = array(
|
||||
'table_migration' => array(
|
||||
'success' => true,
|
||||
'from_table' => $failed_login_tbl_name,
|
||||
'to_table' => $audit_log_tbl_name
|
||||
)
|
||||
);
|
||||
|
||||
if (false === $wpdb->query($wpdb->prepare("INSERT INTO $audit_log_tbl_name (network_id, site_id, username, ip, level, event_type, details, stacktrace, created) SELECT %d AS network_id, %d AS site_id, fl.user_login AS username, fl.login_attempt_ip AS ip, 'warning' AS level, 'Failed login' AS event_type, %s AS details, '' AS stacktrace, UNIX_TIMESTAMP(fl.failed_login_date) AS created FROM $failed_login_tbl_name fl", $network_id, $site_id, $import_details))) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- We can't use %i because our plugin supports wordpress < 6.2.
|
||||
$table_migration_details['table_migration']['success'] = false;
|
||||
do_action('aiowps_record_event', 'table_migration', $table_migration_details, 'error');
|
||||
} else {
|
||||
do_action('aiowps_record_event', 'table_migration', $table_migration_details, 'info');
|
||||
$wpdb->query("DROP TABLE IF EXISTS `$failed_login_tbl_name`"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- We can't use %i because our plugin supports wordpress < 6.2.
|
||||
}
|
||||
}
|
||||
|
||||
$table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $wpdb->esc_like($login_activity_tbl_name))); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- There doesn't seem to be an alternative.
|
||||
if ($table_exists) {
|
||||
$wpdb->query("DROP TABLE IF EXISTS `$login_activity_tbl_name`"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- We can't use %i because our plugin supports wordpress < 6.2.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will run SQL to clean sensitive information from the audit log table stacktrace
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clean_audit_log_stacktraces() {
|
||||
global $wpdb;
|
||||
$wpdb->query("UPDATE ".AIOWPSEC_TBL_AUDIT_LOG." SET stacktrace = '' WHERE event_type = 'failed_login' OR event_type = 'successful_login' OR event_type = 'user_registration'"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.NotPrepared -- We can't use %i because our plugin supports wordpress < 6.2.
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will update the table datetime column to timestamp with backward compability
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function update_table_column_to_timestamp() {
|
||||
$db_version = get_option('aiowpsec_db_version', '1.0');
|
||||
if (version_compare('2.0.8', $db_version, '>')) {
|
||||
self::update_column_to_timestamp(AIOWPSEC_TBL_EVENTS, 'event_date', 'created');
|
||||
self::update_column_to_timestamp(AIOWPSEC_TBL_LOGIN_LOCKOUT, 'lockdown_date', 'created');
|
||||
self::update_column_to_timestamp(AIOWPSEC_TBL_LOGIN_LOCKOUT, 'release_date', 'released');
|
||||
}
|
||||
|
||||
if (version_compare('2.0.9', $db_version, '>')) {
|
||||
self::update_column_to_timestamp(AIOWPSEC_TBL_PERM_BLOCK, 'blocked_date', 'created');
|
||||
self::update_column_to_timestamp(AIOWPSEC_TBL_GLOBAL_META_DATA, 'date_time', 'created');
|
||||
self::update_column_to_timestamp(AIOWPSEC_TBL_DEBUG_LOG, 'created', 'logtime');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the table column to UTC timestamp not depending on the timezone of the user or server settings
|
||||
*
|
||||
* @global wpdb $wpdb
|
||||
*
|
||||
* @param string $table_name
|
||||
* @param string $field_datetime
|
||||
* @param string $field_timestamp
|
||||
*
|
||||
* @return boolean - returns the rows updated or not
|
||||
*/
|
||||
public static function update_column_to_timestamp($table_name, $field_datetime, $field_timestamp) {
|
||||
global $wpdb;
|
||||
//MySQL UNIX_TIMESTAMP will convert datetime based on local timezone not UTC
|
||||
$offset = $wpdb->get_var("SELECT TIMESTAMPDIFF(SECOND, NOW(), UTC_TIMESTAMP())"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- There doesn't seem to be an alternative.
|
||||
if (AIOWPSEC_TBL_PERM_BLOCK == $table_name || AIOWPSEC_TBL_GLOBAL_META_DATA == $table_name || AIOWPSEC_TBL_DEBUG_LOG == $table_name) {
|
||||
//User local settings date time saved offset timezone needs to removed for UTC correct value
|
||||
$offset += AIOWPSecurity_Utility::get_wp_timezone()->getOffset(new DateTime('now', new DateTimeZone('UTC')));
|
||||
}
|
||||
if (function_exists('is_multisite') && is_multisite() && AIOWPSEC_TBL_EVENTS == $table_name) {
|
||||
$table_name = $wpdb->prefix.'aiowps_events';
|
||||
} elseif (function_exists('is_multisite') && is_multisite() && AIOWPSEC_TBL_LOGIN_LOCKOUT == $table_name) {
|
||||
$table_name = $wpdb->prefix.'aiowps_login_lockdown';
|
||||
} elseif (function_exists('is_multisite') && is_multisite() && AIOWPSEC_TBL_PERM_BLOCK == $table_name) {
|
||||
$table_name = $wpdb->prefix.'aiowps_permanent_block';
|
||||
} elseif (function_exists('is_multisite') && is_multisite() && AIOWPSEC_TBL_GLOBAL_META_DATA == $table_name) {
|
||||
$table_name = $wpdb->prefix.'aiowps_global_meta';
|
||||
} elseif (function_exists('is_multisite') && is_multisite() && AIOWPSEC_TBL_DEBUG_LOG == $table_name) {
|
||||
$table_name = $wpdb->prefix.'aiowps_debug_log';
|
||||
}
|
||||
//offset to make sure UTC timestamp updated
|
||||
$wpdb->query($wpdb->prepare("UPDATE $table_name SET $field_timestamp = (UNIX_TIMESTAMP($field_datetime) - %d)", $offset)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- We can't use %i because our plugin supports wordpress < 6.2.
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the aiowps_temp_configs option if present.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function delete_aiowps_temp_configs_option() {
|
||||
delete_option('aiowps_temp_configs');
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters all of the AIOS tables to use InnoDB.
|
||||
*
|
||||
* @global wpdb $wpdb
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function update_tables_to_innodb() {
|
||||
global $wpdb;
|
||||
|
||||
if (function_exists('is_multisite') && is_multisite()) {
|
||||
/*
|
||||
* FIX for multisite table creation case:
|
||||
* Although each table name is defined in a constant inside the wp-security-core.php,
|
||||
* we need to do this step for multisite case because we need to refresh the $wpdb->prefix value
|
||||
* otherwise it will contain the original blog id and not the current id we need.
|
||||
*
|
||||
*/
|
||||
$lockout_tbl_name = $wpdb->prefix.'aiowps_login_lockdown';
|
||||
$aiowps_global_meta_tbl_name = $wpdb->prefix.'aiowps_global_meta';
|
||||
$aiowps_event_tbl_name = $wpdb->prefix.'aiowps_events';
|
||||
$perm_block_tbl_name = $wpdb->prefix.'aiowps_permanent_block';
|
||||
} else {
|
||||
$lockout_tbl_name = AIOWPSEC_TBL_LOGIN_LOCKOUT;
|
||||
$aiowps_global_meta_tbl_name = AIOWPSEC_TBL_GLOBAL_META_DATA;
|
||||
$aiowps_event_tbl_name = AIOWPSEC_TBL_EVENTS;
|
||||
$perm_block_tbl_name = AIOWPSEC_TBL_PERM_BLOCK;
|
||||
}
|
||||
|
||||
$message_store_log_tbl_name = AIOWPSEC_TBL_MESSAGE_STORE;
|
||||
$audit_log_tbl_name = AIOWPSEC_TBL_AUDIT_LOG;
|
||||
$debug_log_tbl_name = AIOWPSEC_TBL_DEBUG_LOG;
|
||||
$logged_in_users_tbl_name = AIOWPSEC_TBL_LOGGED_IN_USERS;
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery -- No alternative.
|
||||
$wpdb->query('ALTER TABLE ' . $lockout_tbl_name . ' ENGINE=InnoDB');
|
||||
$wpdb->query('ALTER TABLE ' . $aiowps_global_meta_tbl_name . ' ENGINE=InnoDB');
|
||||
$wpdb->query('ALTER TABLE ' . $aiowps_event_tbl_name . ' ENGINE=InnoDB');
|
||||
$wpdb->query('ALTER TABLE ' . $perm_block_tbl_name . ' ENGINE=InnoDB');
|
||||
$wpdb->query('ALTER TABLE ' . $audit_log_tbl_name . ' ENGINE=InnoDB');
|
||||
$wpdb->query('ALTER TABLE ' . $debug_log_tbl_name . ' ENGINE=InnoDB');
|
||||
$wpdb->query('ALTER TABLE ' . $logged_in_users_tbl_name . ' ENGINE=InnoDB');
|
||||
$wpdb->query('ALTER TABLE ' . $message_store_log_tbl_name . ' ENGINE=InnoDB');
|
||||
// phpcs:enable WordPress.DB.DirectDatabaseQuery
|
||||
}
|
||||
|
||||
public static function create_db_backup_dir() {
|
||||
global $aio_wp_security;
|
||||
// phpcs:disable WordPress.WP.AlternativeFunctions -- WP_Filesystem is not appropriate here.
|
||||
//Create our folder in the "wp-content" directory
|
||||
$aiowps_dir = WP_CONTENT_DIR . '/' . AIO_WP_SECURITY_BACKUPS_DIR_NAME;
|
||||
if (!is_dir($aiowps_dir) && is_writable(WP_CONTENT_DIR)) {
|
||||
mkdir($aiowps_dir, 0755, true);
|
||||
//Let's also create an empty index.html file in this folder
|
||||
$index_file = $aiowps_dir . '/index.html';
|
||||
$handle = fopen($index_file, 'w'); //or die('Cannot open file: '.$index_file);
|
||||
fclose($handle);
|
||||
}
|
||||
$server_type = AIOWPSecurity_Utility::get_server_type();
|
||||
//Only create .htaccess if server is the right type
|
||||
if ('apache' == $server_type || 'litespeed' == $server_type) {
|
||||
$file = $aiowps_dir . '/.htaccess';
|
||||
if (!file_exists($file)) {
|
||||
//Create an .htacces file
|
||||
//Write some rules which will only allow people originating from wp admin page to download the DB backup
|
||||
$rules = '';
|
||||
$rules .= 'order deny,allow' . PHP_EOL;
|
||||
$rules .= 'deny from all' . PHP_EOL;
|
||||
$write_result = file_put_contents($file, $rules);
|
||||
if (false === $write_result) {
|
||||
$aio_wp_security->debug_logger->log_debug("Creation of .htaccess file in " . AIO_WP_SECURITY_BACKUPS_DIR_NAME . " directory failed!", 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
// phpcs:enable WordPress.WP.AlternativeFunctions -- WP_Filesystem is not appropriate here.
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup AIOS cron tasks.
|
||||
* Handles both single and multi-site (NW activation) cases.
|
||||
*
|
||||
* @global type $wpdb
|
||||
*
|
||||
* @return Void
|
||||
*/
|
||||
public static function set_cron_tasks_upon_activation() {
|
||||
require_once(__DIR__.'/wp-security-cronjob-handler.php');
|
||||
// It is required because we are going to schedule a 15-minute cron event upon activation.
|
||||
add_filter('cron_schedules', array('AIOWPSecurity_Cronjob_Handler', 'cron_schedules'));
|
||||
if (is_multisite() && is_main_site()) {
|
||||
global $wpdb;
|
||||
// check if it is a network activation
|
||||
$blogids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery -- There doesn't seem to be a documented alternative. Possible alternative get_sites(array('fields' => 'ids', 'number' => 0) but 'number' => 0 is not documented.
|
||||
foreach ($blogids as $blog_id) {
|
||||
switch_to_blog($blog_id);
|
||||
AIOWPSecurity_Installer::schedule_cron_events();
|
||||
do_action('aiowps_activation_complete');
|
||||
restore_current_blog();
|
||||
}
|
||||
} else {
|
||||
AIOWPSecurity_Installer::schedule_cron_events();
|
||||
do_action('aiowps_activation_complete');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for scheduling AIOS cron events.
|
||||
*
|
||||
* @return Void
|
||||
*/
|
||||
public static function schedule_cron_events() {
|
||||
if (!wp_next_scheduled('aios_15_minutes_cron_event')) {
|
||||
wp_schedule_event(time(), 'aios-every-15-minutes', 'aios_15_minutes_cron_event'); //schedule a 15 minutes cron event
|
||||
}
|
||||
if (!wp_next_scheduled('aiowps_hourly_cron_event')) {
|
||||
wp_schedule_event(time(), 'hourly', 'aiowps_hourly_cron_event'); //schedule an hourly cron event
|
||||
}
|
||||
if (!wp_next_scheduled('aiowps_daily_cron_event')) {
|
||||
wp_schedule_event(time(), 'daily', 'aiowps_daily_cron_event'); //schedule an daily cron event
|
||||
}
|
||||
if (!wp_next_scheduled('aiowps_weekly_cron_event')) {
|
||||
wp_schedule_event(time(), 'weekly', 'aiowps_weekly_cron_event'); //schedule an daily cron event
|
||||
}
|
||||
}
|
||||
}
|
||||
+739
@@ -0,0 +1,739 @@
|
||||
<?php
|
||||
|
||||
if (!defined('AIO_WP_SECURITY_PATH')) die('No direct access allowed');
|
||||
|
||||
if (!class_exists('Updraft_Notices_1_2')) require_once(AIO_WP_SECURITY_PATH.'/vendor/team-updraft/common-libs/src/updraft-notices/updraft-notices.php');
|
||||
|
||||
class AIOWPSecurity_Notices extends Updraft_Notices_1_2 {
|
||||
|
||||
private $initialized = false;
|
||||
|
||||
protected $notices_content = array();
|
||||
|
||||
// protected $self_affiliate_id = null;
|
||||
|
||||
/**
|
||||
* Safely formats translatable strings to prevent errors due to mis-translations.
|
||||
*
|
||||
* @param string $text Translatable text with placeholders.
|
||||
* @param mixed ...$args Arguments to fill placeholders.
|
||||
*
|
||||
* @return string The formatted text or an error fallback.
|
||||
*/
|
||||
private function safe_sprintf($text, ...$args) {
|
||||
global $aio_wp_security;
|
||||
try {
|
||||
return sprintf($text, ...$args);
|
||||
} catch (Throwable $e) {
|
||||
// Log the error for debugging purposes
|
||||
$aio_wp_security->debug_logger->log_debug('Notice rendering error: ' . $e->getMessage(), 4);
|
||||
return esc_html__('An error occurred while rendering this notice, please enable and check your debug log.', 'all-in-one-wp-security-and-firewall');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns array_merge of notices from parent and notices in $child_notice_content.
|
||||
*
|
||||
* @return Array
|
||||
*/
|
||||
protected function populate_notices_content() {
|
||||
global $aio_wp_security;
|
||||
$parent_notice_content = parent::populate_notices_content();
|
||||
|
||||
/* translators: 1. HTML text. 2. HTML text, 3. HTML text. */
|
||||
$sale_description = $this->safe_sprintf(__('Get %1$s with %2$s. %3$s, downtime, and response time issues.', 'all-in-one-wp-security-and-firewall'), '<strong>' . __('added protection', 'all-in-one-wp-security-and-firewall') . '</strong>', '<strong>' . __('Premium', 'all-in-one-wp-security-and-firewall') . '</strong>', '<strong>' . __('Scan your site for malware', 'all-in-one-wp-security-and-firewall') . '</strong>');
|
||||
|
||||
/* translators: %s: HTML text. */
|
||||
$sale_description .= ' ' . $this->safe_sprintf(__('Block traffic by country of origin, get advanced two-factor authentication, %s, and more.', 'all-in-one-wp-security-and-firewall'), '<strong>' . __('added protection', 'all-in-one-wp-security-and-firewall') . '</strong>', '<strong>' . __('Premium', 'all-in-one-wp-security-and-firewall') . '</strong>', '<strong>' . __('Scan your site for malware', 'all-in-one-wp-security-and-firewall') . '</strong>');
|
||||
|
||||
// Build text for firewall rules that have been upgraded
|
||||
$firewall_upgrade_text = '<p>' .
|
||||
esc_html__('The All in One Security plugin has deactivated some of the firewall settings that you had activated.', 'all-in-one-wp-security-and-firewall') .
|
||||
'</p>';
|
||||
$firewall_upgrade_text .= '<p>' .
|
||||
esc_html__('We have upgraded the following settings so that they are now part of the PHP firewall instead of .htaccess directives:', 'all-in-one-wp-security-and-firewall') .
|
||||
'</p>';
|
||||
$firewall_upgrade_text .= '<ul style="list-style: inside;">';
|
||||
|
||||
$active_settings = $aio_wp_security->configs->get_value('aiowps_firewall_active_upgrade');
|
||||
|
||||
if (!empty($active_settings)) {
|
||||
$active_settings = json_decode($active_settings);
|
||||
if (!empty($active_settings)) {
|
||||
|
||||
foreach ($active_settings as $setting) {
|
||||
switch ($setting) {
|
||||
case 'aiowps_enable_pingback_firewall':
|
||||
$firewall_upgrade_text .= '<li>' . esc_html__('Completely block xmlrpc.php', 'all-in-one-wp-security-and-firewall').'</li>';
|
||||
break;
|
||||
case 'aiowps_forbid_proxy_comments':
|
||||
$firewall_upgrade_text .= '<li>' . esc_html__('Forbid proxy comment posting', 'all-in-one-wp-security-and-firewall').'</li>';
|
||||
break;
|
||||
case 'aiowps_deny_bad_query_strings':
|
||||
$firewall_upgrade_text .= '<li>' . esc_html__('Deny bad query strings', 'all-in-one-wp-security-and-firewall').'</li>';
|
||||
break;
|
||||
case 'aiowps_advanced_char_string_filter':
|
||||
$firewall_upgrade_text .= '<li>' . esc_html__('Advanced character filter', 'all-in-one-wp-security-and-firewall').'</li>';
|
||||
break;
|
||||
default:
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$firewall_upgrade_text .= '<p><strong>' . esc_html__('None of the settings that have been upgraded were active.', 'all-in-one-wp-security-and-firewall').'</strong></p>';
|
||||
}
|
||||
|
||||
$firewall_upgrade_text .= '</ul>';
|
||||
$firewall_upgrade_text .= '<p>' . esc_html__('What would you like to do?', 'all-in-one-wp-security-and-firewall') .'</p>';
|
||||
|
||||
$login_whitelist_notice_text = '<p>' .
|
||||
esc_html__('The All in One Security plugin has disabled the login whitelist setting that you have enabled in the past.', 'all-in-one-wp-security-and-firewall') .
|
||||
'</p>' .
|
||||
'<p>';
|
||||
if (AIOWPSecurity_Utility::is_apache_server()) {
|
||||
$login_whitelist_notice_text .= esc_html__('Your website is running on an Apache webserver, the login whitelisting might not be functional until the recent update of AIOS (because it relied upon Apache-specific module features).', 'all-in-one-wp-security-and-firewall');
|
||||
} else {
|
||||
$login_whitelist_notice_text .= esc_html__('Your website is running on a non-Apache webserver, so the login whitelisting was not functional until the recent update of AIOS (because it relied upon Apache-specific features).', 'all-in-one-wp-security-and-firewall');
|
||||
}
|
||||
$login_whitelist_notice_text .= ' ' . esc_html__('It began working with AIOS version 5.0.8.', 'all-in-one-wp-security-and-firewall') . ' ' . esc_html__('We have disabled it so that your login page will not be blocked unexpectedly.', 'all-in-one-wp-security-and-firewall') .
|
||||
'</p>';
|
||||
|
||||
$allowed_ip_addresses = explode("\n", $aio_wp_security->configs->get_value('aiowps_allowed_ip_addresses'));
|
||||
$allowed_ip_addresses = array_map('trim', $allowed_ip_addresses);
|
||||
$login_whitelist_notice_text .= '<p>' .
|
||||
esc_html__('Whitelisted login IP address(es):', 'all-in-one-wp-security-and-firewall') . ' ' . htmlspecialchars(implode(', ', $allowed_ip_addresses)) .
|
||||
'</p>' .
|
||||
'<p>' .
|
||||
esc_html__('Would you like to re-enable login whitelisting?', 'all-in-one-wp-security-and-firewall') .
|
||||
'</p>';
|
||||
|
||||
$child_notice_content = array(
|
||||
// Upgrade AIOS backup to UDP backup in the 5.0.0 version
|
||||
'automated-database-backup' => array(
|
||||
'title' => esc_html__('Removed database backup feature from the All-In-One Security plugin', 'all-in-one-wp-security-and-firewall'),
|
||||
'text' => '<p>' .
|
||||
esc_html__('Beginning with version 5.0.0, AIOS has replaced the AIOS backup method with the superior UpdraftPlus method.', 'all-in-one-wp-security-and-firewall') . ' '.
|
||||
esc_html__('It remains free and is fully supported by the UpdraftPlus team.', 'all-in-one-wp-security-and-firewall') .
|
||||
'</p>' .
|
||||
'<p>' .
|
||||
esc_html__('You are seeing this notice because you have previously set up automated database backups in AIOS.', 'all-in-one-wp-security-and-firewall') . ' ' .
|
||||
esc_html__('Would you like to set up scheduled backups with UpdraftPlus?', 'all-in-one-wp-security-and-firewall') .
|
||||
'</p>',
|
||||
'button_link' => add_query_arg(array(
|
||||
'page' => 'aiowpsec_database',
|
||||
'tab' => 'database-backup',
|
||||
), admin_url('admin.php')) . '#automated-scheduled-backups-heading',
|
||||
'button_meta' => esc_html__('Setup UpdraftPlus backup plugin', 'all-in-one-wp-security-and-firewall'),
|
||||
'dismiss_time' => 'dismiss_automated_database_backup_notice',
|
||||
'supported_positions' => array('automated-database-backup'),
|
||||
'validity_function' => 'should_show_automated_database_backup_notice',
|
||||
),
|
||||
'ip-retrieval-settings' => array(
|
||||
'title' => esc_html__('Important: set up your IP address detection settings', 'all-in-one-wp-security-and-firewall'),
|
||||
'text' => '<p>' .
|
||||
esc_html__("The All in One Security plugin couldn't be certain about the correct method to detect the IP address for your site visitors with your currently-configured IP address detection settings.", 'all-in-one-wp-security-and-firewall') . ' '.
|
||||
esc_html__('It is important for your security to set the IP address detection settings properly.', 'all-in-one-wp-security-and-firewall') .
|
||||
'</p>' .
|
||||
'<p>' .
|
||||
esc_html__('Please go to the settings and set them now.', 'all-in-one-wp-security-and-firewall') .
|
||||
'</p>',
|
||||
'button_link' => add_query_arg(array(
|
||||
'page' => 'aiowpsec_settings',
|
||||
'tab' => 'advanced-settings',
|
||||
), admin_url('admin.php')) . '#automated-scheduled-backups-heading',
|
||||
'button_meta' => esc_html__('Setup IP address detection settings', 'all-in-one-wp-security-and-firewall'),
|
||||
'dismiss_time' => 'dismiss_ip_retrieval_settings_notice',
|
||||
'supported_positions' => array('ip-retrieval-settings'),
|
||||
'validity_function' => 'should_show_ip_retrieval_settings_notice',
|
||||
),
|
||||
'load-firewall-resources-failed' => array(
|
||||
'title' => '',
|
||||
'text' => '<p>' .
|
||||
esc_html__('Failed to load the firewall resources.', 'all-in-one-wp-security-and-firewall') . ' ' .
|
||||
esc_html__('The firewall won\'t operate correctly.', 'all-in-one-wp-security-and-firewall') .
|
||||
'</p>',
|
||||
'dismiss_time' => '',
|
||||
'supported_positions' => array('load-firewall-resources-failed'),
|
||||
'validity_function' => 'should_show_load_firewall_resources_failed_notice',
|
||||
),
|
||||
'end-of-support-php-56' => array(
|
||||
'title' => esc_html__('AIOS PHP 5.6 support will end soon', 'all-in-one-wp-security-and-firewall'),
|
||||
'text' => $this->get_end_of_support_php_56_text(),
|
||||
'dismiss_time' => 'php_56_eol_dismiss_forever',
|
||||
'supported_positions' => array('end-of-support-php-56'),
|
||||
'validity_function' => 'should_show_end_of_support_php_56',
|
||||
),
|
||||
'upgrade-firewall-tab-rules' => array(
|
||||
'title' => esc_html__('Important: Disabled firewall settings', 'all-in-one-wp-security-and-firewall'),
|
||||
'text' => $firewall_upgrade_text,
|
||||
'button_link' => add_query_arg(array(
|
||||
'page' => esc_html(AIOWPSEC_FIREWALL_MENU_SLUG),
|
||||
'tab' => 'basic-firewall',
|
||||
), admin_url('admin.php')),
|
||||
'action_button_text' => esc_html__('Reactivate', 'all-in-one-wp-security-and-firewall'),
|
||||
'button_meta' => esc_html__('Configure manually', 'all-in-one-wp-security-and-firewall'),
|
||||
'dismiss_time' => 'dismiss_firewall_settings_disabled_on_upgrade_notice',
|
||||
'supported_positions' => array('upgrade-firewall-tab-rules'),
|
||||
'dismiss_text' => esc_html__('Keep deactivated', 'all-in-one-wp-security-and-firewall'),
|
||||
'validity_function' => 'should_show_upgrade_firewall_settings_notice',
|
||||
),
|
||||
'ip-blacklist-settings-on-upgrade' => array(
|
||||
'title' => esc_html__('Important: Blacklist manager disabled', 'all-in-one-wp-security-and-firewall'),
|
||||
'text' => '<p>' .
|
||||
esc_html__("The blacklist manager feature has been disabled to prevent any unexpected site lockouts.", 'all-in-one-wp-security-and-firewall') .
|
||||
'</p>' .
|
||||
'<p>' .
|
||||
esc_html__("This feature will block any IP address or range listed in its settings, please double check your own details are not included before turning it back on.", 'all-in-one-wp-security-and-firewall') .
|
||||
'</p>' ,
|
||||
'button_link' => add_query_arg(array(
|
||||
'page' => esc_html(AIOWPSEC_FIREWALL_MENU_SLUG),
|
||||
'tab' => 'blacklist'
|
||||
), admin_url('admin.php')) . '#poststuff',
|
||||
'action_button_text' => 'Turn it on',
|
||||
'button_meta' => esc_html__('Edit the settings', 'all-in-one-wp-security-and-firewall'),
|
||||
'dismiss_time' => 'dismiss_ip_blacklist_notice',
|
||||
'dismiss_text' => 'Keep it off',
|
||||
'supported_positions' => array('ip-blacklist-settings-on-upgrade'),
|
||||
'validity_function' => 'should_show_ip_blacklist_settings_on_upgrade',
|
||||
),
|
||||
'login-whitelist-disabled-on-upgrade' => array(
|
||||
'title' => esc_html__('Important: Disabled login whitelist setting', 'all-in-one-wp-security-and-firewall'),
|
||||
'text' => $login_whitelist_notice_text,
|
||||
'button_link' => add_query_arg(array(
|
||||
'page' => esc_html(AIOWPSEC_BRUTE_FORCE_MENU_SLUG),
|
||||
'tab' => 'login-whitelist',
|
||||
), admin_url('admin.php')) . '#poststuff',
|
||||
'action_button_text' => esc_html__('Turn it back on', 'all-in-one-wp-security-and-firewall'),
|
||||
'button_meta' => esc_html__('Edit the settings', 'all-in-one-wp-security-and-firewall'),
|
||||
'dismiss_time' => 'dismiss_login_whitelist_disabled_on_upgrade_notice',
|
||||
'supported_positions' => array('login-whitelist-disabled-on-upgrade'),
|
||||
'dismiss_text' => esc_html__('Keep it off', 'all-in-one-wp-security-and-firewall'),
|
||||
'validity_function' => 'should_show_login_whitelist_disabled_on_upgrade_notice',
|
||||
),
|
||||
'rate_plugin' => array(
|
||||
'text' => $this->safe_sprintf(esc_html__('We noticed AIOS has kept your site safe for a while.', 'all-in-one-wp-security-and-firewall') . ' ' . esc_html__('If you like us, please consider leaving a positive review.', 'all-in-one-wp-security-and-firewall'). ' ' . esc_html__('If you have any issues or questions, please contact %s.', 'all-in-one-wp-security-and-firewall'), '<a href="https://wordpress.org/support/plugin/all-in-one-wp-security-and-firewall/" target="_blank">' . esc_html__('support', 'all-in-one-wp-security-and-firewall').'</a>') . '<br>' . esc_html__('Thank you so much!', 'all-in-one-wp-security-and-firewall') . '<br><br>- <b>' . esc_html__('All-In-One Security (AIOS)', 'all-in-one-wp-security-and-firewall').'</b>',
|
||||
'image' => 'plugin-logos/aios-icon.png',
|
||||
'button_link' => 'https://wordpress.org/support/plugin/all-in-one-wp-security-and-firewall/reviews/?rate=5#new-post',
|
||||
'button_meta' => 'review',
|
||||
'dismiss_time' => 'dismiss_review_notice',
|
||||
'supported_positions' => $this->dashboard_top,
|
||||
'validity_function' => 'show_rate_notice'
|
||||
),
|
||||
'updraftplus' => array(
|
||||
'prefix' => '',
|
||||
'title' => esc_html__('Enhance your security even more by backing up your site', 'all-in-one-wp-security-and-firewall'),
|
||||
'text' => esc_html__('UpdraftPlus is the world\'s most trusted backup plugin.', 'all-in-one-wp-security-and-firewall') . ' ' . esc_html__('From the owners of All-In-One Security (AIOS).', 'all-in-one-wp-security-and-firewall'),
|
||||
'image' => 'plugin-logos/updraftplus-icon.png',
|
||||
'button_link' => 'https://wordpress.org/plugins/updraftplus/',
|
||||
'button_meta' => 'updraftplus',
|
||||
'dismiss_time' => 'dismiss_page_notice_until',
|
||||
'supported_positions' => $this->dashboard_top_or_report,
|
||||
'validity_function' => 'updraftplus_not_installed',
|
||||
),
|
||||
'wp-optimize' => array(
|
||||
'prefix' => '',
|
||||
'title' => esc_html__('Speed up your site', 'all-in-one-wp-security-and-firewall'),
|
||||
'text' => esc_html__("After you've secured your site, we recommend you install our WP-Optimize plugin to streamline it for better website performance.", 'all-in-one-wp-security-and-firewall'),
|
||||
'image' => 'plugin-logos/wp-optimize-icon.png',
|
||||
'button_link' => 'https://wordpress.org/plugins/wp-optimize/',
|
||||
'button_meta' => 'wp-optimize',
|
||||
'dismiss_time' => 'dismiss_notice',
|
||||
'supported_positions' => $this->anywhere,
|
||||
'validity_function' => 'wp_optimize_not_installed',
|
||||
),
|
||||
|
||||
// The sale adverts content starts here
|
||||
'blackfriday' => array(
|
||||
'prefix' => '',
|
||||
'title' => esc_html__('20% off - Black Friday Sale', 'all-in-one-wp-security-and-firewall'),
|
||||
'text' => $sale_description,
|
||||
'text2' => esc_html__('at checkout.', 'all-in-one-wp-security-and-firewall') . ' <strong>' . esc_html__('Hurry, offer ends 2 December.', 'all-in-one-wp-security-and-firewall') . '</strong>',
|
||||
'image' => 'notices/sale_20.png',
|
||||
'button_text' => sprintf(__('Save 20%% with code %s', 'all-in-one-wp-security-and-firewall'), 'blackfridaysale2025'),
|
||||
'button_link' => add_query_arg(
|
||||
array(
|
||||
'utm_source' => 'aios-plugin',
|
||||
'utm_medium' => 'referral',
|
||||
'utm_campaign' => 'bf25-aios-plugin-banner',
|
||||
'utm_content' => 'bf-sale',
|
||||
'utm_creative_format' => 'advert',
|
||||
),
|
||||
'https://teamupdraft.com/plugin-black-friday/?utm_source=aios-plugin&utm_medium=referral&utm_campaign=bf25-aios-plugin-banner&utm_content=bf-sale&utm_creative_format=advert'),
|
||||
'campaign' => 'blackfriday',
|
||||
'button_meta' => 'inline',
|
||||
'dismiss_time' => 'dismiss_season',
|
||||
// 'discount_code' => '‘bf22aiosupgrade’',
|
||||
'valid_from' => '2025-11-14 00:00:00',
|
||||
'valid_to' => '2025-12-02 23:59:59',
|
||||
'supported_positions' => $this->dashboard_top_or_report,
|
||||
)
|
||||
);
|
||||
|
||||
return array_merge($parent_notice_content, $child_notice_content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether to show the automated database backup notice.
|
||||
*
|
||||
* @return Boolean True if the automated database notice should be shown, otherwise false.
|
||||
*/
|
||||
protected function should_show_automated_database_backup_notice() {
|
||||
if ($this->is_database_backup_admin_page_tab()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (defined('AIOS_FORCE_AUTOMATED_DATABASE_BACKUP_NOTICE') && AIOS_FORCE_AUTOMATED_DATABASE_BACKUP_NOTICE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->is_updraftplus_plugin_active() && $this->is_schedule_database_backup_set_in_updraftplus()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
global $aio_wp_security;
|
||||
if ('1' == $aio_wp_security->configs->get_value('aiowps_enable_automated_backups')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to show the PHP 5.6 end of support notice
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function should_show_end_of_support_php_56() {
|
||||
return version_compare(PHP_VERSION, '7.0.0', '<');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the text to display with the PHP 5.6 end of support notice
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function get_end_of_support_php_56_text() {
|
||||
$text = '<p>' . esc_html__('AIOS will end support for PHP 5.6 on the 1st September 2025.', 'all-in-one-wp-security-and-firewall') . '</p>';
|
||||
|
||||
$text .= '<p>' . esc_html__('PHP 5.6 is outdated and no longer receives security updates.', 'all-in-one-wp-security-and-firewall') . ' ' . esc_html__('To keep things secure and compatible with modern WordPress standards, AIOS will move to a minimum requirement of PHP 7.0.', 'all-in-one-wp-security-and-firewall') . '</p>';
|
||||
|
||||
$text .= '<p>' . esc_html__('After the 1st September 2025, AIOS may not operate correctly on PHP versions below 7.0.', 'all-in-one-wp-security-and-firewall') . '</p>';
|
||||
|
||||
$text .= '<p>' . esc_html__('If you require help upgrading your PHP version, please contact your hosting provider.', 'all-in-one-wp-security-and-firewall') . '</p>';
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether to show the load firewall resources failed notice.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function should_show_load_firewall_resources_failed_notice() {
|
||||
return !AIOS_Firewall_Resource::all_loaded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to show the notice which handles the firewall settings notice
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function should_show_upgrade_firewall_settings_notice() {
|
||||
if (!is_main_site()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No nonce to check.
|
||||
$is_firewall_page = ('admin.php' == $GLOBALS['pagenow'] && isset($_GET['page']) && AIOWPSEC_FIREWALL_MENU_SLUG == $_GET['page']);
|
||||
if ($is_firewall_page) return false;
|
||||
|
||||
global $aio_wp_security;
|
||||
|
||||
$active_settings = $aio_wp_security->configs->get_value('aiowps_firewall_active_upgrade');
|
||||
|
||||
if (empty($active_settings)) return false;
|
||||
|
||||
$active_settings = json_decode($active_settings);
|
||||
|
||||
if (empty($active_settings)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current page is the AIOS database backup admin page
|
||||
*
|
||||
* @return Boolean True if the current page is the AIOS database backup admin page, otherwise false.
|
||||
*/
|
||||
private function is_database_backup_admin_page_tab() {
|
||||
return $this->is_database_security_admin_page() && $this->is_database_backup_tab();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current page is the database security admin page.
|
||||
*
|
||||
* @return Boolean True if the current page is the database security admin page, otherwise false.
|
||||
*/
|
||||
private function is_database_security_admin_page() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No nonce to check.
|
||||
return ('admin.php' == $GLOBALS['pagenow'] && isset($_GET['page']) && 'aiowpsec_database' == $_GET['page']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current tab is the database backup tab.
|
||||
*
|
||||
* @return Boolean True if the current tab is the database backup tab, otherwise false.
|
||||
*/
|
||||
private function is_database_backup_tab() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No nonce to check.
|
||||
return (isset($_GET['tab']) && 'database-backup' == $_GET['tab']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether to show the IP address detection settings notice.
|
||||
*
|
||||
* @return Boolean True if the IP address detection settings notice should be shown, otherwise false.
|
||||
*/
|
||||
protected function should_show_ip_retrieval_settings_notice() {
|
||||
if (!is_main_site()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->is_ip_settings_admin_page_tab()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (defined('AIOS_FORCE_IP_RETRIEVAL_SETTINGS_NOTICE') && AIOS_FORCE_IP_RETRIEVAL_SETTINGS_NOTICE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
global $aio_wp_security;
|
||||
$aiowps_firewall_config = AIOS_Firewall_Resource::request(AIOS_Firewall_Resource::CONFIG);
|
||||
|
||||
// Is notice dismissed.
|
||||
if ('1' == $aio_wp_security->configs->get_value('dismiss_ip_retrieval_settings_notice')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$configured_ip_method_id = $aio_wp_security->configs->get_value('aiowps_ip_retrieve_method');
|
||||
|
||||
if (AIOWPSecurity_Utility_IP::is_server_suitable_ip_methods_give_same_ip_address()) {
|
||||
if ('' === $configured_ip_method_id) {
|
||||
$server_suitable_ip_methods = AIOWPSecurity_Utility_IP::get_server_suitable_ip_methods();
|
||||
$most_suitable_ip_method = reset($server_suitable_ip_methods);
|
||||
if (!empty($most_suitable_ip_method)) {
|
||||
$most_suitable_ip_method_id = array_search($most_suitable_ip_method, AIOS_Abstracted_Ids::get_ip_retrieve_methods());
|
||||
$aio_wp_security->configs->set_value('aiowps_ip_retrieve_method', $most_suitable_ip_method_id);
|
||||
$aiowps_firewall_config->set_value('aios_ip_retrieve_method', $most_suitable_ip_method_id, true);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the IP retrieval method is not set.
|
||||
$configured_ip_method_id = $aio_wp_security->configs->get_value('aiowps_ip_retrieve_method');
|
||||
if ('' === $configured_ip_method_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$server_user_ip_address = AIOS_Helper::get_server_detected_user_ip_address();
|
||||
return empty($server_user_ip_address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current page is the AIOS IP retrieval admin page
|
||||
*
|
||||
* @return Boolean True if the current page is the AIOS database backup admin page, otherwise false.
|
||||
*/
|
||||
private function is_ip_settings_admin_page_tab() {
|
||||
return $this->is_settings_admin_page() && $this->is_advanced_settings_tab();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current page is the AIOS settings admin page
|
||||
*
|
||||
* @return Boolean True if the current page is the AIOS settings admin page, otherwise false.
|
||||
*/
|
||||
private function is_settings_admin_page() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No nonce to check.
|
||||
return ('admin.php' == $GLOBALS['pagenow'] && isset($_GET['page']) && 'aiowpsec_settings' == $_GET['page']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current tab is the advanced settings tab.
|
||||
*
|
||||
* @return Boolean True if the current tab is the advanced settings tab, otherwise false.
|
||||
*/
|
||||
private function is_advanced_settings_tab() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No nonce to check.
|
||||
return (isset($_GET['tab']) && 'advanced-settings' == $_GET['tab']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the UpdraftPlus plugin is active or not.
|
||||
*
|
||||
* @return bool True if the UpdraftPlus plugin is active, otherwise false.
|
||||
*/
|
||||
private function is_updraftplus_plugin_active() {
|
||||
return class_exists('UpdraftPlus');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the database backup scheduled in the UpdraftPlus plugin.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function is_schedule_database_backup_set_in_updraftplus() {
|
||||
$updraft_interval_database_option_val = get_option('updraft_interval_database', '');
|
||||
if (empty($updraft_interval_database_option_val) || 'manual' == $updraft_interval_database_option_val) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether to show the IP Blacklist settings notice.
|
||||
*
|
||||
* @return Boolean True if the IP Blacklist settings notice should be shown, otherwise false.
|
||||
*/
|
||||
protected function should_show_ip_blacklist_settings_on_upgrade() {
|
||||
if (!is_main_site()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->is_blacklist_admin_page()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
global $aio_wp_security;
|
||||
|
||||
if ('1' == $aio_wp_security->configs->get_value('aiowps_is_ip_blacklist_settings_notice_on_upgrade')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current page is the AIOS blacklist admin page
|
||||
*
|
||||
* @return Boolean True if the current page is the AIOS blacklist admin page, otherwise false.
|
||||
*/
|
||||
private function is_blacklist_admin_page() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No nonce to check.
|
||||
return ('admin.php' == $GLOBALS['pagenow'] && isset($_GET['page']) && AIOWPSEC_FIREWALL_MENU_SLUG == $_GET['page'] && isset($_GET['tab']) && 'blacklist' == $_GET['tab']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether to show the IP address detection settings notice.
|
||||
*
|
||||
* @return Boolean True if the IP address detection settings notice should be shown, otherwise false.
|
||||
*/
|
||||
protected function should_show_login_whitelist_disabled_on_upgrade_notice() {
|
||||
if (!is_main_site()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->is_login_whitelist_admin_page_tab()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (defined('AIOS_FORCE_LOGIN_WHITELIST_DISABLED_ON_UPGRADE_NOTICE') && AIOS_FORCE_LOGIN_WHITELIST_DISABLED_ON_UPGRADE_NOTICE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
global $aio_wp_security;
|
||||
|
||||
if ('1' == $aio_wp_security->configs->get_value('aiowps_is_login_whitelist_disabled_on_upgrade') && '1' != $aio_wp_security->configs->get_value('aiowps_enable_whitelisting')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current page is the AIOS IP retrieval admin page
|
||||
*
|
||||
* @return Boolean True if the current page is the AIOS database backup admin page, otherwise false.
|
||||
*/
|
||||
private function is_login_whitelist_admin_page_tab() {
|
||||
return $this->is_brute_force_admin_page() && $this->is_login_whitelist_tab();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current page is the AIOS settings admin page
|
||||
*
|
||||
* @return Boolean True if the current page is the AIOS settings admin page, otherwise false.
|
||||
*/
|
||||
private function is_brute_force_admin_page() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No nonce to check.
|
||||
return ('admin.php' == $GLOBALS['pagenow'] && isset($_GET['page']) && AIOWPSEC_BRUTE_FORCE_MENU_SLUG == $_GET['page']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the current tab is the advanced settings tab.
|
||||
*
|
||||
* @return Boolean True if the current tab is the advanced settings tab, otherwise false.
|
||||
*/
|
||||
private function is_login_whitelist_tab() {
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No nonce to check.
|
||||
return (isset($_GET['tab']) && 'login-whitelist' == $_GET['tab']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method to setup the notices
|
||||
*/
|
||||
public function notices_init() {
|
||||
if ($this->initialized) return;
|
||||
$this->initialized = true;
|
||||
$this->notices_content = $this->populate_notices_content();
|
||||
|
||||
$enqueue_version = (defined('WP_DEBUG') && WP_DEBUG) ? AIO_WP_SECURITY_VERSION.'.'.time() : AIO_WP_SECURITY_VERSION;
|
||||
wp_enqueue_style('aiowpsec-admin-notices-css', AIO_WP_SECURITY_URL.'/css/wp-security-notices.css', array(), $enqueue_version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get AIOS Plugin installation timestamp.
|
||||
*
|
||||
* @return integer AIOS Plugin installation timestamp.
|
||||
*/
|
||||
public function get_aiowps_plugin_installed_timestamp() {
|
||||
$installed_at = @filemtime(AIO_WP_SECURITY_PATH.'/index.html'); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore warning as we handle it below
|
||||
if (false === $installed_at) {
|
||||
global $aio_wp_security;
|
||||
$installed_at = (int) $aio_wp_security->configs->get_value('installed-at');
|
||||
}
|
||||
$installed_at = apply_filters('aiowps_plugin_installed_timestamp', $installed_at);
|
||||
return $installed_at;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will check if we should display the rate notice or not
|
||||
*
|
||||
* @return boolean - to indicate if we should show the notice or not
|
||||
*/
|
||||
protected function show_rate_notice() {
|
||||
$installed_at = $this->get_aiowps_plugin_installed_timestamp();
|
||||
$time_now = $this->get_time_now();
|
||||
$installed_for = $time_now - $installed_at;
|
||||
|
||||
if ($installed_at && $installed_for > 28*86400) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if UpdraftPlus is installed(returns false) or not(returns true).
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
protected function updraftplus_not_installed() {
|
||||
if (!function_exists('get_plugins')) include_once(ABSPATH.'wp-admin/includes/plugin.php');
|
||||
$plugins = get_plugins();
|
||||
|
||||
foreach ($plugins as $value) {
|
||||
if ('updraftplus' == $value['TextDomain']) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if WP-Optimize is installed(returns false) or not(returns true).
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
protected function wp_optimize_not_installed() {
|
||||
if (!function_exists('get_plugins')) include_once(ABSPATH.'wp-admin/includes/plugin.php');
|
||||
$plugins = get_plugins();
|
||||
|
||||
foreach ($plugins as $value) {
|
||||
if ('wp-optimize' == $value['TextDomain']) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to prepare a seasonal notice(returns true) or not(returns false).
|
||||
*
|
||||
* @param Array $notice_data - all data for the notice
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
protected function skip_seasonal_notices($notice_data) {
|
||||
$time_now = $this->get_time_now();
|
||||
$valid_from = strtotime($notice_data['valid_from']);
|
||||
$valid_to = strtotime($notice_data['valid_to']);
|
||||
$dismiss = $this->check_notice_dismissed($notice_data['dismiss_time']);
|
||||
if (($time_now >= $valid_from && $time_now <= $valid_to) && !$dismiss) {
|
||||
// return true so that we return this notice to be displayed
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get timestamp that is considered as current timestamp for notice.
|
||||
*
|
||||
* @return integer timestamp that should be consider as a current time.
|
||||
*/
|
||||
public function get_time_now() {
|
||||
$time_now = defined('AIOWPSECURITY_NOTICES_FORCE_TIME') ? AIOWPSECURITY_NOTICES_FORCE_TIME : time();
|
||||
return $time_now;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a notice is dismissed(returns true) or not(returns false).
|
||||
*
|
||||
* @param String $dismiss_time - dismiss time id for the notice
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
protected function check_notice_dismissed($dismiss_time) {
|
||||
$time_now = $this->get_time_now();
|
||||
|
||||
global $aio_wp_security;
|
||||
$dismiss = ($time_now < (int) $aio_wp_security->configs->get_value($dismiss_time));
|
||||
|
||||
return $dismiss;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders or returns a notice.
|
||||
*
|
||||
* @param Boolean|String $advert_information - all data for the notice
|
||||
* @param Boolean $return_instead_of_echo - whether to return the notice(true) or render it to the page(false)
|
||||
* @param String $position - notice position
|
||||
*
|
||||
* @return Void|String
|
||||
*/
|
||||
protected function render_specified_notice($advert_information, $return_instead_of_echo = false, $position = 'top') {
|
||||
|
||||
if ('bottom' == $position) {
|
||||
$template_file = 'bottom-notice.php';
|
||||
} elseif ('report' == $position) {
|
||||
$template_file = 'report.php';
|
||||
} elseif ('report-plain' == $position) {
|
||||
$template_file = 'report-plain.php';
|
||||
} elseif (in_array($position, AIOS_Abstracted_Ids::custom_admin_notice_ids())) {
|
||||
$template_file = 'custom-notice.php';
|
||||
} elseif (in_array($position, AIOS_Abstracted_Ids::htaccess_to_php_feature_notice_ids())) {
|
||||
$template_file = 'htaccess-to-php-feature-notice.php';
|
||||
} else {
|
||||
$template_file = 'horizontal-notice.php';
|
||||
}
|
||||
|
||||
global $aio_wp_security;
|
||||
return $aio_wp_security->include_template('notices/'.$template_file, $return_instead_of_echo, $advert_information);
|
||||
}
|
||||
}
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;//Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Process_Renamed_Login_Page {
|
||||
|
||||
public function __construct() {
|
||||
add_action('login_init', array($this, 'aiowps_login_init'));
|
||||
add_filter('site_url', array($this, 'aiowps_site_url'), 10, 2);
|
||||
add_filter('network_site_url', array($this, 'aiowps_site_url'), 10, 2);
|
||||
add_filter('wp_redirect', array($this, 'aiowps_wp_redirect'), 10, 2);
|
||||
add_filter('register', array($this, 'register_link'));
|
||||
add_filter('user_request_action_email_content', array($this, 'aiowps_user_request_email_content'), 10, 2);
|
||||
remove_action('template_redirect', 'wp_redirect_admin_locations', 1000); //To prevent redirect to login page when people type "login" at end of home URL
|
||||
|
||||
}
|
||||
|
||||
public function aiowps_login_init() {
|
||||
$parsed_request = isset($_SERVER['REQUEST_URI']) ? wp_parse_url(sanitize_url(wp_unslash($_SERVER['REQUEST_URI']))) : '';
|
||||
if ($parsed_request && preg_match('/wp-login\.php$/', $parsed_request['path'])) {
|
||||
AIOWPSecurity_Process_Renamed_Login_Page::aiowps_set_404();
|
||||
}
|
||||
}
|
||||
|
||||
public function aiowps_site_url($url) {
|
||||
return $this->aiowps_filter_wp_login_file($url);
|
||||
}
|
||||
|
||||
public function aiowps_wp_redirect($location) {
|
||||
return $this->aiowps_filter_wp_login_file($location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter register link on the login page
|
||||
*
|
||||
* @param string $registration_url
|
||||
* @return string
|
||||
*/
|
||||
public function register_link($registration_url) {
|
||||
return $this->aiowps_filter_wp_login_file($registration_url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter confirm link so we hide the secret login slug in the export_personal_data email
|
||||
*
|
||||
* @param string $email_text
|
||||
* @param string $email_data
|
||||
* @return string
|
||||
*/
|
||||
public function aiowps_user_request_email_content($email_text, $email_data) {
|
||||
global $aio_wp_security;
|
||||
if (isset($email_data['request']) && isset($email_data['request']->action_name)) {
|
||||
if ('export_personal_data' == $email_data['request']->action_name) {
|
||||
$confirm_url = $email_data['confirm_url'];
|
||||
$login_slug = $aio_wp_security->configs->get_value('aiowps_login_page_slug');
|
||||
if (get_option('permalink_structure')) {
|
||||
$new_confirm_url = str_replace($login_slug, 'wp-login.php', $confirm_url);
|
||||
} else {
|
||||
$search_pattern = '?'.$login_slug.'&action';
|
||||
$new_confirm_url = str_replace($search_pattern, '/wp-login.php/?action', $confirm_url);
|
||||
}
|
||||
|
||||
$email_text_modified = str_replace('###CONFIRM_URL###', esc_url_raw($new_confirm_url), $email_text);
|
||||
return $email_text_modified;
|
||||
}
|
||||
}
|
||||
return $email_text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter all login url strings on the login page
|
||||
*
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
public function aiowps_filter_wp_login_file($url) {
|
||||
if (strpos($url, 'wp-login.php') !== false) {
|
||||
$args = explode('?', $url);
|
||||
if (isset($args[1])) {
|
||||
if (strpos($args[1], 'action=postpass') !== false) {
|
||||
return $url; //Don't reveal the secret URL in the post password action url
|
||||
}
|
||||
parse_str($args[1], $args);
|
||||
$url = esc_url(add_query_arg($args, AIOWPSecurity_Process_Renamed_Login_Page::new_login_url()));
|
||||
$url = html_entity_decode($url);
|
||||
} elseif (isset($_SERVER['REQUEST_URI']) && stripos(urldecode(sanitize_url(wp_unslash($_SERVER['REQUEST_URI']))), 'wp-admin/install.php')) {
|
||||
return $url;
|
||||
} else {
|
||||
$url = AIOWPSecurity_Process_Renamed_Login_Page::new_login_url();
|
||||
}
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login page renamed related tasks, do not allow access if not logged with rename login page.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function renamed_login_init_tasks() {
|
||||
// Bail if the host cron job is running by running the command "php wp-cron.php"
|
||||
// The $_SERVER['REQUEST_URI'] is undefined when running a PHP file from the command line.
|
||||
// for `wp plugin list` it will be empty so showing Not available instead plugin list.
|
||||
if (empty($_SERVER['REQUEST_URI']) || defined('WP_CLI') || 'cli' == PHP_SAPI || wp_doing_cron() || wp_doing_ajax()) {
|
||||
return;
|
||||
}
|
||||
|
||||
global $aio_wp_security;
|
||||
|
||||
//The following will process the native wordpress post password protection form
|
||||
//Normally this is done by wp-login.php file but we cannot use that since the login page has been renamed
|
||||
|
||||
// Bots with URLs like: /index.php?action[]=aaaa will return an array here. It needs to be a string.
|
||||
$action = (isset($_GET['action']) && is_string($_GET['action'])) ? sanitize_text_field(wp_unslash($_GET['action'])) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No nonce available.
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing -- No nonce available.
|
||||
if (isset($_POST['post_password']) && 'postpass' == $action) {
|
||||
|
||||
// Check if the captcha is enabled for the password protected pages and process validation if the login page was renamed
|
||||
if ('1' == $aio_wp_security->configs->get_value('aiowps_enable_password_protected_captcha')) {
|
||||
$aio_wp_security->captcha_obj->validate_password_protected_password_form_with_captcha();
|
||||
}
|
||||
|
||||
require_once ABSPATH . 'wp-includes/class-phpass.php';
|
||||
$hasher = new PasswordHash(8, true);
|
||||
|
||||
/**
|
||||
* Filter the life span of the post password cookie.
|
||||
*
|
||||
* By default, the cookie expires 10 days from creation. To turn this
|
||||
* into a session cookie, return 0.
|
||||
*
|
||||
* @since 3.7.0
|
||||
*
|
||||
* @param int $expires The expiry time, as passed to setcookie().
|
||||
*/
|
||||
$expire = apply_filters('post_password_expires', time() + 10 * DAY_IN_SECONDS);
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- No nonce available, not recommended to sanitize passwords.
|
||||
setcookie('wp-postpass_' . COOKIEHASH, $hasher->HashPassword(wp_unslash($_POST['post_password'])), $expire, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true);
|
||||
|
||||
wp_safe_redirect(wp_get_referer());
|
||||
exit();
|
||||
}
|
||||
|
||||
//case where someone attempting to reach wp-admin
|
||||
if (is_admin() && !is_user_logged_in() && (isset($_SERVER["SCRIPT_FILENAME"]) ? basename(sanitize_text_field(wp_unslash($_SERVER["SCRIPT_FILENAME"]))) : '') !== 'admin-post.php') {
|
||||
//Fix to prevent fatal error caused by some themes and Yoast SEO
|
||||
do_action('aiowps_before_wp_die_renamed_login');
|
||||
wp_die(esc_html__('You do not have permission to access this page.', 'all-in-one-wp-security-and-firewall') . ' ' . esc_html__('Please log in and try again.', 'all-in-one-wp-security-and-firewall'), 403);
|
||||
}
|
||||
|
||||
//case where someone attempting to reach wp-login
|
||||
if (isset($_SERVER['REQUEST_URI']) && stripos(urldecode(sanitize_url(wp_unslash($_SERVER['REQUEST_URI']))), 'wp-login.php') && !is_user_logged_in()) {
|
||||
|
||||
// Handle export personal data request for rename login case
|
||||
if (isset($_GET['request_id'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- PCP warning. No nonce available.
|
||||
$request_id = (int) sanitize_text_field(wp_unslash($_GET['request_id'])); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- PCP warning. No nonce available.
|
||||
$result = '';
|
||||
if (isset($_GET['confirm_key'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- PCP warning. No nonce available.
|
||||
$key = sanitize_text_field(wp_unslash($_GET['confirm_key'])); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- PCP warning. No nonce available.
|
||||
$result = wp_validate_user_request_key($request_id, $key);
|
||||
} else {
|
||||
$result = new WP_Error('invalid_key', esc_html__('Invalid key', 'all-in-one-wp-security-and-firewall'));
|
||||
}
|
||||
|
||||
if (is_wp_error($result)) {
|
||||
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- PCP error. $result is WPError object with sanitized strings.
|
||||
wp_die($result);
|
||||
} elseif (!empty($result)) {
|
||||
_wp_privacy_account_request_confirmed($request_id);
|
||||
$message = _wp_privacy_account_request_confirmed_message($request_id);
|
||||
login_header(__('User action confirmed.', 'all-in-one-wp-security-and-firewall'), $message);
|
||||
login_footer();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
//Check if the maintenance (lockout) mode is active - if so prevent access to site by not displaying 404 page!
|
||||
if ($aio_wp_security->configs->get_value('aiowps_site_lockout') == '1') {
|
||||
AIOWPSecurity_WP_Loaded_Tasks::site_lockout_tasks();
|
||||
} else {
|
||||
AIOWPSecurity_Process_Renamed_Login_Page::aiowps_set_404();
|
||||
}
|
||||
}
|
||||
|
||||
//case where someone attempting to reach the standard register or signup pages
|
||||
$request_uri = urldecode(sanitize_url(wp_unslash($_SERVER['REQUEST_URI'])));
|
||||
if ('' !== $request_uri && stripos($request_uri, 'wp-register.php') || '' !== $request_uri && stripos($request_uri, 'wp-signup.php')) {
|
||||
//Check if the maintenance (lockout) mode is active - if so prevent access to site by not displaying 404 page!
|
||||
if ('1' == $aio_wp_security->configs->get_value('aiowps_site_lockout')) {
|
||||
AIOWPSecurity_WP_Loaded_Tasks::site_lockout_tasks();
|
||||
} else {
|
||||
AIOWPSecurity_Process_Renamed_Login_Page::aiowps_set_404();
|
||||
}
|
||||
}
|
||||
|
||||
$login_slug = $aio_wp_security->configs->get_value('aiowps_login_page_slug');
|
||||
|
||||
if (self::is_renamed_login_page_requested($login_slug)) {
|
||||
if (empty($action) && is_user_logged_in()) {
|
||||
//if user is already logged in but tries to access the renamed login page, send them to the dashboard
|
||||
// or to requested redirect-page, filtered in 'login_redirect'.
|
||||
if (isset($_REQUEST['redirect_to'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- PCP warning. No nonce available.
|
||||
$redirect_to = wp_sanitize_redirect(wp_unslash($_REQUEST['redirect_to'])); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- PCP warning. No nonce available.
|
||||
$redirect_to = wp_validate_redirect($redirect_to, apply_filters('wp_safe_redirect_fallback', admin_url(), 302));
|
||||
$requested_redirect_to = $redirect_to;
|
||||
} else {
|
||||
$redirect_to = admin_url();
|
||||
$requested_redirect_to = '';
|
||||
}
|
||||
$redirect_to = apply_filters('login_redirect', $redirect_to, $requested_redirect_to, wp_get_current_user());
|
||||
AIOWPSecurity_Utility::redirect_to_url($redirect_to);
|
||||
} else {
|
||||
global $wp_version;
|
||||
do_action('aiowps_rename_login_load');
|
||||
// logout action called by WooCommerce does not apply the login whitelist which shows a 403 error for the customer
|
||||
if (!(isset($_GET['action']) && 'logout' == $_GET['action'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- PCP warning. No nonce available.
|
||||
AIOWPSecurity_Utility_IP::check_login_whitelist_and_forbid();
|
||||
}
|
||||
|
||||
status_header(200);
|
||||
if (version_compare($wp_version, '6.6', '>=')) {
|
||||
require_once(AIO_WP_SECURITY_PATH . '/other-includes/wp-security-rename-login-feature.php');
|
||||
} elseif (version_compare($wp_version, '5.7', '>=')) {
|
||||
require_once(AIO_WP_SECURITY_PATH . '/other-includes/wp-security-rename-login-feature-pre-6-6.php');
|
||||
} elseif (version_compare($wp_version, '5.2', '>=')) {
|
||||
require_once(AIO_WP_SECURITY_PATH . '/other-includes/wp-security-rename-login-feature-pre-5-7.php');
|
||||
} else {
|
||||
require_once(AIO_WP_SECURITY_PATH . '/other-includes/wp-security-rename-login-feature-pre-5-2.php');
|
||||
}
|
||||
|
||||
die;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function new_login_url() {
|
||||
global $aio_wp_security;
|
||||
$login_slug = $aio_wp_security->configs->get_value('aiowps_login_page_slug');
|
||||
if (get_option('permalink_structure')) {
|
||||
return trailingslashit(trailingslashit(home_url()) . $login_slug);
|
||||
} else {
|
||||
return trailingslashit(site_url()) . '?' . $login_slug;
|
||||
}
|
||||
}
|
||||
|
||||
public static function aiowps_set_404() {
|
||||
global $wp_query;
|
||||
do_action('aiowps_before_set_404'); // This hook is for themes which produce a fatal error when the rename login feature is enabled and someone visits "wp-admin" slug directly
|
||||
|
||||
status_header(404);
|
||||
$wp_query->set_404();
|
||||
do_action('template_redirect'); // Trigger 'template_redirect' to allow third-party plugins to intercept and inject custom logic before rendering the fallback template.
|
||||
|
||||
$template = get_404_template();
|
||||
if (empty($template)) $template = get_index_template();
|
||||
$template = apply_filters('template_include', $template);
|
||||
if ($template) include($template);
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check renamed login page is requested
|
||||
*
|
||||
* @param string $login_slug Renamed loginpage slug
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_renamed_login_page_requested($login_slug) {
|
||||
|
||||
if (empty($_SERVER['REQUEST_URI'])) return false;
|
||||
|
||||
$parsed_url_path = isset($_SERVER['REQUEST_URI']) ? wp_parse_url(sanitize_url(wp_unslash($_SERVER['REQUEST_URI'])), PHP_URL_PATH) : '';
|
||||
$home_url_with_slug = home_url($login_slug, 'relative');
|
||||
|
||||
/*
|
||||
* Compatibility fix for WPML, TranslatePress plugin
|
||||
*/
|
||||
if (function_exists('wpml_object_id') || function_exists('trp_enable_translatepress')) {
|
||||
$home_url_with_slug = home_url($login_slug);
|
||||
$parsed_home_url_with_slug = wp_parse_url($home_url_with_slug);
|
||||
$home_url_with_slug = $parsed_home_url_with_slug['path']; //this will return just the path minus the protocol and host
|
||||
}
|
||||
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- PCP warning. No nonce available.
|
||||
if (untrailingslashit($parsed_url_path) === $home_url_with_slug || (!get_option('permalink_structure') && isset($_GET[$login_slug]))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; //Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Reporting {
|
||||
|
||||
/**
|
||||
* Generate a report
|
||||
*
|
||||
* @param string $output_format Output format
|
||||
* @param array $section_content The content of the sections
|
||||
* @param string $section_title The title of the section
|
||||
*
|
||||
* @return string Report content
|
||||
*/
|
||||
public static function generate_report_sections($output_format, $section_content = array(), $section_title = '') {
|
||||
$data = '';
|
||||
|
||||
$sanitized_section_content = array();
|
||||
|
||||
foreach ($section_content as $key => $value) {
|
||||
$sanitized_key = esc_html($key);
|
||||
$sanitized_value = esc_html($value);
|
||||
$sanitized_section_content[$sanitized_key] = $sanitized_value;
|
||||
}
|
||||
|
||||
if ('text' === $output_format) {
|
||||
$data .= "\n --- $section_title --- \n\n";
|
||||
$data .= self::output_section_data($sanitized_section_content);
|
||||
$data .= "\n===================================\n";
|
||||
} elseif ('table' === $output_format) {
|
||||
$data .= '<div class="postbox">';
|
||||
$data .= '<h3 class="hndle"><label for="title">' . $section_title . '</label></h3>';
|
||||
$data .= '<div class="inside" id="' . strtolower(str_replace(' ', '-', $section_title)) . '-info">';
|
||||
$data .= apply_filters('aiowp_security_report_section_content', AIOWPSecurity_Utility_UI::format_data_as_table($sanitized_section_content));
|
||||
$data .= '</div>';
|
||||
$data .= '</div>';
|
||||
}
|
||||
|
||||
$data = apply_filters('aiowp_security_generate_report_section_below', $data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the section data
|
||||
*
|
||||
* @param array $section_data Section data to output
|
||||
*
|
||||
* @return string Section data
|
||||
*/
|
||||
private static function output_section_data($section_data = array()) {
|
||||
$output = '';
|
||||
foreach ($section_data as $key => $value) {
|
||||
$output .= "$key - $value\n";
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; // Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Sender_Service {
|
||||
|
||||
/**
|
||||
* Send email with the provided data
|
||||
*
|
||||
* @param string $recipient Email recipient
|
||||
* @param string $subject Email subject
|
||||
* @param string $message Email message
|
||||
*
|
||||
* @return bool True on success, false on failure
|
||||
*/
|
||||
public static function send_email($recipient, $subject, $message) {
|
||||
$headers = array(
|
||||
'Content-Type: text/html; charset=UTF-8',
|
||||
);
|
||||
|
||||
$result = wp_mail($recipient, $subject, $message, $headers);
|
||||
|
||||
// Trigger an action after sending the email
|
||||
do_action('aiowp_security_after_send_email', $recipient, $subject, $message, $headers, $result);
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Executable
+126
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;//Exit if accessed directly
|
||||
}
|
||||
|
||||
/**
|
||||
* All settings operation performed from here to use as general for wp cli.
|
||||
*/
|
||||
class AIOWPSecurity_Settings_Tasks {
|
||||
|
||||
/**
|
||||
* Enable basic firewall rule.
|
||||
*
|
||||
* @return array messages
|
||||
*/
|
||||
public static function enable_basic_firewall() {
|
||||
global $aio_wp_security;
|
||||
$msg = array();
|
||||
$aio_wp_security->configs->set_value('aiowps_enable_basic_firewall', '1', true);
|
||||
|
||||
//Now let's write the applicable rules to the .htaccess file
|
||||
if (AIOWPSecurity_Utility::allow_to_write_to_htaccess()) {
|
||||
$res = AIOWPSecurity_Utility_Htaccess::write_to_htaccess();
|
||||
} else {
|
||||
$res = true;
|
||||
}
|
||||
|
||||
if ($res) {
|
||||
$msg['updated'] = __('Settings were successfully saved.', 'all-in-one-wp-security-and-firewall');
|
||||
} else {
|
||||
/* translators: %s: .htaccess path */
|
||||
$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');
|
||||
}
|
||||
return $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable all security features.
|
||||
*
|
||||
* @return array messages
|
||||
*/
|
||||
public static function disable_all_security_features() {
|
||||
$msg = array();
|
||||
AIOWPSecurity_Configure_Settings::turn_off_all_security_features();
|
||||
|
||||
//Now let's clear the applicable rules from the .htaccess file
|
||||
if (AIOWPSecurity_Utility::allow_to_write_to_htaccess()) {
|
||||
$res = AIOWPSecurity_Utility_Htaccess::write_to_htaccess();
|
||||
} else {
|
||||
$res = true;
|
||||
}
|
||||
|
||||
//Now let's revert the disable editing setting in the wp-config.php file if necessary
|
||||
$res2 = AIOWPSecurity_Utility::enable_file_edits();
|
||||
|
||||
if ($res) {
|
||||
$msg['updated'] = __('All the security features have been disabled successfully.', 'all-in-one-wp-security-and-firewall');
|
||||
} else {
|
||||
/* translators: %s: .htaccess path */
|
||||
$msg['error'][] = sprintf(__('Could not write to the %s file.', 'all-in-one-wp-security-and-firewall'), AIOWPSecurity_Utility_File::get_home_path().'.htaccess') . ' ' . sprintf(__('Please restore it manually using the restore functionality in the "%s" tab.', 'all-in-one-wp-security-and-firewall'), '.htaccess ' . __('file', 'all-in-one-wp-security-and-firewall'));
|
||||
}
|
||||
|
||||
if (!$res2) {
|
||||
/* translators: %s: wp-config.php path */
|
||||
$msg['error'][] = sprintf(__('Could not write to the %s file.', 'all-in-one-wp-security-and-firewall'), AIOWPSecurity_Utility_File::get_home_path().'wp-config.php') . ' ' . sprintf(__('Please restore it manually using the restore functionality in the "%s" tab.', 'all-in-one-wp-security-and-firewall'), 'wp-config.php ' . __('file', 'all-in-one-wp-security-and-firewall'));
|
||||
}
|
||||
return $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable all firewall rules.
|
||||
*
|
||||
* @return array messages
|
||||
*/
|
||||
public static function disable_all_firewall_rules() {
|
||||
$msg = array();
|
||||
AIOWPSecurity_Configure_Settings::turn_off_firewall_configs();
|
||||
|
||||
//Now let's clear the applicable rules from the .htaccess file
|
||||
if (AIOWPSecurity_Utility::allow_to_write_to_htaccess()) {
|
||||
$res = AIOWPSecurity_Utility_Htaccess::write_to_htaccess();
|
||||
} else {
|
||||
$res = true;
|
||||
}
|
||||
|
||||
if ($res) {
|
||||
$msg['updated'] = __('All firewall rules have been disabled successfully.', 'all-in-one-wp-security-and-firewall');
|
||||
} else {
|
||||
/* translators: %s: .htaccess path, %s file tab name. */
|
||||
$msg['error'] = sprintf(__('Could not write to the %s file.', 'all-in-one-wp-security-and-firewall'), AIOWPSecurity_Utility_File::get_home_path().'.htaccess') . ' ' . sprintf(__('Please restore it manually using the restore functionality in the "%s" tab.', 'all-in-one-wp-security-and-firewall'), '.htaccess ' . __('file', 'all-in-one-wp-security-and-firewall'));
|
||||
}
|
||||
return $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all settings.
|
||||
*
|
||||
* @return array messages
|
||||
*/
|
||||
public static function reset_all_settings() {
|
||||
$msg = array();
|
||||
if (!class_exists('AIOWPSecurity_Reset_Settings')) {
|
||||
include(AIO_WP_SECURITY_PATH . '/admin/wp-security-reset-settings.php');
|
||||
}
|
||||
$reset_option_res = AIOWPSecurity_Reset_Settings::reset_options();
|
||||
if (AIOWPSecurity_Utility::allow_to_write_to_htaccess()) {
|
||||
$delete_htaccess = AIOWPSecurity_Reset_Settings::delete_htaccess();
|
||||
} else {
|
||||
$delete_htaccess = true;
|
||||
}
|
||||
AIOWPSecurity_Reset_Settings::reset_db_tables();
|
||||
// AIOS premium and other plugin related config settings are reset by adding below action.
|
||||
do_action('aios_reset_all_settings');
|
||||
|
||||
if (false === $reset_option_res && false === $delete_htaccess) {
|
||||
$msg['error'] = __('Deletion of aio_wp_security_configs option and .htaccess directives failed.', 'all-in-one-wp-security-and-firewall');
|
||||
} elseif (false === $reset_option_res) {
|
||||
$msg['error'] = __('Reset of aio_wp_security_configs option failed.', 'all-in-one-wp-security-and-firewall');
|
||||
} elseif (false === $delete_htaccess) {
|
||||
$msg['error'] = __('Deletion of .htaccess directives failed.', 'all-in-one-wp-security-and-firewall');
|
||||
} else {
|
||||
$msg['updated'] = __('All settings have been successfully reset.', 'all-in-one-wp-security-and-firewall');
|
||||
}
|
||||
return $msg;
|
||||
}
|
||||
}
|
||||
Executable
+201
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) die('No direct access allowed');
|
||||
|
||||
if (!class_exists('Simba_Two_Factor_Authentication_1')) require AIO_WP_SECURITY_PATH.'/includes/simba-tfa/simba-tfa.php';
|
||||
|
||||
/**
|
||||
* This parent-child relationship enables the two to be split without affecting backwards compatibility for developers making direct calls
|
||||
*
|
||||
* This class is for the plugin encapsulation.
|
||||
*/
|
||||
class AIO_WP_Security_Simba_Two_Factor_Authentication_Plugin extends Simba_Two_Factor_Authentication_1 {
|
||||
|
||||
/**
|
||||
* Whether the TFA plugin is being integrated into the AIOS plugin.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $is_tfa_integrated;
|
||||
|
||||
/**
|
||||
* Stores the current plugin version.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $version;
|
||||
|
||||
/**
|
||||
* Class constructor
|
||||
*
|
||||
* @uses __FILE__
|
||||
*
|
||||
* @return Void
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
add_filter('aiowpsecurity_setting_tabs', array($this, 'add_two_factor_setting_tab'));
|
||||
add_filter('tfa_user_ip_address', array($this, 'aios_set_user_ip_address'));
|
||||
|
||||
if (false !== $this->is_incompatible_plugin_active()) return;
|
||||
|
||||
if (!function_exists('mcrypt_get_iv_size') && !function_exists('openssl_cipher_iv_length')) {
|
||||
add_action('all_admin_notices', array($this, 'admin_notice_missing_mcrypt_and_openssl'));
|
||||
return;
|
||||
}
|
||||
|
||||
$this->is_tfa_integrated = true;
|
||||
|
||||
// Run at a priority ensuring that this will be after AIOS has registered its translation domain
|
||||
add_action('init', array($this, 'plugin_text_domain_loaded'), 11);
|
||||
|
||||
add_action('admin_menu', array($this, 'menu_entry_for_user'), 30);
|
||||
$this->version = AIO_WP_SECURITY_VERSION;
|
||||
$this->set_user_settings_page_slug(AIOWPSEC_TWO_FACTOR_AUTH_MENU_SLUG);
|
||||
|
||||
$this->set_plugin_translate_url('https://translate.wordpress.org/projects/wp-plugins/all-in-one-wp-security-and-firewall/');
|
||||
$this->set_site_wide_administration_url(admin_url('admin.php?page=aiowpsec_settings&tab=two-factor-authentication'));
|
||||
$this->set_premium_version_url('https://teamupdraft.com/all-in-one-security/pricing/?utm_source=aios-plugin&utm_medium=referral&utm_campaign=paac&utm_content=emergency-codes-feature&utm_creative_format=text');
|
||||
$this->set_faq_url('https://wordpress.org/plugins/all-in-one-wp-security-and-firewall/#faq');
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs upon the WP action init (once the text domain has been loaded)
|
||||
*/
|
||||
public function plugin_text_domain_loaded() {
|
||||
$this->set_settings_page_heading(__('Two factor authentication - Admin settings', 'all-in-one-wp-security-and-firewall'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect plugins that cause us to self-deactivate
|
||||
*
|
||||
* @return Boolean|String
|
||||
*/
|
||||
private function is_incompatible_plugin_active() {
|
||||
|
||||
if (defined('WORDFENCE_LS_VERSION')) return 'Wordfence Login Security';
|
||||
|
||||
$active_plugins = $this->get_active_plugins();
|
||||
foreach ($active_plugins as $plugin_file_rel_to_plugins_dir) {
|
||||
$temp_plugin_file_name = substr($plugin_file_rel_to_plugins_dir, strpos($plugin_file_rel_to_plugins_dir, '/') + 1);
|
||||
if ('wordfence-login-security.php' == $temp_plugin_file_name) {
|
||||
return 'Wordfence Login Security';
|
||||
}
|
||||
if ('wordfence.php' == $temp_plugin_file_name) {
|
||||
return 'Wordfence';
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of plugins active on either the current site, or site-wide
|
||||
*
|
||||
* @return Array - a list of plugin paths (relative to the plugin directory)
|
||||
*/
|
||||
private function get_active_plugins() {
|
||||
|
||||
// Gets all active plugins on the current site
|
||||
$active_plugins = get_option('active_plugins');
|
||||
|
||||
if (is_multisite()) {
|
||||
$network_active_plugins = get_site_option('active_sitewide_plugins');
|
||||
if (!empty($network_active_plugins)) {
|
||||
$network_active_plugins = array_keys($network_active_plugins);
|
||||
$active_plugins = array_merge($active_plugins, $network_active_plugins);
|
||||
}
|
||||
}
|
||||
|
||||
return $active_plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs upon the WP actions admin_menu and network_admin_menu
|
||||
*/
|
||||
public function menu_entry_for_user() {
|
||||
|
||||
global $current_user;
|
||||
if ($this->is_activated_for_user($current_user->ID)) {
|
||||
if (!AIOWPSecurity_Utility_Permissions::has_manage_cap()) {
|
||||
$menu_icon_url = AIO_WP_SECURITY_URL . '/images/aios-plugin-icon.svg';
|
||||
add_menu_page(__('AIOS', 'all-in-one-wp-security-and-firewall'), __('AIOS', 'all-in-one-wp-security-and-firewall'), apply_filters('aios_management_permission', 'manage_options'), AIOWPSEC_MAIN_MENU_SLUG, '', $menu_icon_url);
|
||||
}
|
||||
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Two Factor Auth', 'all-in-one-wp-security-and-firewall'), __('Two Factor Auth', 'all-in-one-wp-security-and-firewall'), 'read', AIOWPSEC_TWO_FACTOR_AUTH_MENU_SLUG, array($this, 'show_dashboard_user_settings_page'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AIOS settings based user IP address
|
||||
*
|
||||
* @return string IP address
|
||||
*/
|
||||
public function aios_set_user_ip_address() {
|
||||
return AIOS_Helper::get_user_ip_address();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds Two Factor Authentication tab
|
||||
*
|
||||
* @param array $tabs array that contain tab name and call back function
|
||||
* @return array Returns all tabs with callback function name
|
||||
*/
|
||||
public function add_two_factor_setting_tab($tabs = array()) {
|
||||
if (!AIOWPSecurity_Utility_Permissions::has_manage_cap()) return;
|
||||
|
||||
$tabs['two-factor-authentication'] = array(
|
||||
'title' => __('Two factor authentication', 'all-in-one-wp-security-and-firewall'),
|
||||
'render_callback' => array($this, 'render_two_factor_authentication'),
|
||||
'display_condition_callback' => 'is_main_site',
|
||||
);
|
||||
return $tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the Two Factor Authentication tab & handle the operations
|
||||
*/
|
||||
public function render_two_factor_authentication() {
|
||||
$plugin = $this->is_incompatible_plugin_active();
|
||||
if (false !== $plugin) {
|
||||
global $aio_wp_security;
|
||||
$aio_wp_security->include_template('admin/incompatible-plugin.php', false, array(
|
||||
'incompatible_plugin' => $plugin,
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
$this->show_admin_settings_page();
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the admin settings page code.
|
||||
*/
|
||||
public function show_admin_settings_page() {
|
||||
|
||||
if (!is_admin() || !AIOWPSecurity_Utility_Permissions::has_manage_cap()) return;
|
||||
|
||||
// Check if there are any settings errors and display them (this is needed because the forms from this template submit to the TFA options page not AIOS, so we need to grab them and output them manually).
|
||||
$settings_errors = get_settings_errors();
|
||||
foreach ($settings_errors as $error) {
|
||||
$type = 'success' == $error['type'] ? 'updated' : 'error';
|
||||
$this->show_admin_warning($error['message'], $type);
|
||||
}
|
||||
|
||||
// The value for totp_controller is already set by versions of the TFA plugin after 3 Oct 2022
|
||||
$this->include_template('admin-settings.php', array(
|
||||
'totp_controller' => $this->get_controller('totp'),
|
||||
'settings_page_heading' => $this->get_settings_page_heading(),
|
||||
'admin_settings_links' => array(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs conditionally on the WP action all_admin_notices.
|
||||
*/
|
||||
public function admin_notice_missing_mcrypt_and_openssl() {
|
||||
$this->show_admin_warning('<strong>'.__('PHP OpenSSL or mcrypt module required', 'all-in-one-wp-security-and-firewall').'</strong><br> '.__('The All-In-One Security plugin\'s Two Factor Authentication module requires either the PHP openssl (preferred) or mcrypt module to be installed.', 'all-in-one-wp-security-and-firewall') . ' ' . __('Please ask your web hosting company to install one of them.', 'all-in-one-wp-security-and-firewall'), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
if (false === AIOWPSecurity_Utility::is_incompatible_tfa_premium_version_active() && false === AIOWPSecurity_Utility::is_tfa_or_self_plugin_activating()) {
|
||||
$GLOBALS['simba_two_factor_authentication'] = new AIO_WP_Security_Simba_Two_Factor_Authentication_Plugin();
|
||||
}
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; // Exit if accessed directly.
|
||||
}
|
||||
|
||||
require_once(AIO_WP_SECURITY_PATH.'/classes/wp-security-base-tasks.php');
|
||||
|
||||
class AIOWPSecurity_Uninstallation_Tasks extends AIOWPSecurity_Base_Tasks {
|
||||
/**
|
||||
* Runs various uninstallation tasks
|
||||
* Handles single and multi-site (NW activation) cases
|
||||
*
|
||||
* @global type $wpdb
|
||||
* @global type $aio_wp_security
|
||||
*/
|
||||
public static function run() {
|
||||
parent::run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run uninstallation task for a single site.
|
||||
*
|
||||
* This method overrides {@see AIOWPSecurity_Base_Tasks::run_for_a_site()}.
|
||||
* It drops database tables, deletes options/configs, and removes firewall files when the plugin is uninstalled.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function run_for_a_site() {
|
||||
// Unset the plugin deletion hooks so that we don't try to log to the audit table after it has been removed
|
||||
AIOWPSecurity_Audit_Events::remove_event_actions();
|
||||
|
||||
// Drop db tables and configs
|
||||
self::drop_database_tables_and_configs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to drop database tables and remove configuration settings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function drop_database_tables_and_configs() {
|
||||
global $wpdb, $aio_wp_security;
|
||||
|
||||
$database_tables = array(
|
||||
$wpdb->prefix.'aiowps_login_lockdown',
|
||||
$wpdb->prefix.'aiowps_failed_logins',
|
||||
$wpdb->prefix.'aiowps_login_activity',
|
||||
$wpdb->prefix.'aiowps_global_meta',
|
||||
$wpdb->prefix.'aiowps_events',
|
||||
$wpdb->prefix.'aiowps_permanent_block',
|
||||
$wpdb->prefix.'aiowps_debug_log',
|
||||
$wpdb->prefix.'aiowps_audit_log',
|
||||
$wpdb->prefix.'aiowps_logged_in_users',
|
||||
$wpdb->prefix.'aiowps_message_store',
|
||||
);
|
||||
|
||||
$aio_wp_security->configs->load_config();
|
||||
|
||||
// check and drop database tables
|
||||
if ('1' == $aio_wp_security->configs->get_value('aiowps_on_uninstall_delete_db_tables')) {
|
||||
foreach ($database_tables as $table_name) {
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery -- PCP warning. Ignore.
|
||||
$wpdb->query("DROP TABLE IF EXISTS `$table_name`");
|
||||
}
|
||||
}
|
||||
|
||||
// check and delete configurations
|
||||
if ('1' == $aio_wp_security->configs->get_value('aiowps_on_uninstall_delete_configs')) {
|
||||
if (is_main_site()) {
|
||||
$firewall_rules_path = AIOWPSecurity_Utility_Firewall::get_firewall_rules_path();
|
||||
AIOWPSecurity_Utility_File::remove_local_directory($firewall_rules_path);
|
||||
|
||||
delete_metadata('user', '0', 'aiowps_account_status', '', true);
|
||||
delete_metadata('user', '0', 'aiowps_registrant_ip', '', true);
|
||||
}
|
||||
|
||||
delete_option('aio_wp_security_configs');
|
||||
delete_option('aiowpsec_db_version');
|
||||
delete_option('aiowpsec_firewall_version');
|
||||
delete_option('aios_antibot_key_map_info');
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+1022
File diff suppressed because it is too large
Load Diff
Executable
+100
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;//Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_User_Registration {
|
||||
|
||||
public function __construct() {
|
||||
global $aio_wp_security;
|
||||
add_action('user_register', array($this, 'aiowps_user_registration_action_handler'));
|
||||
|
||||
if ($aio_wp_security->configs->get_value('aiowps_enable_manual_registration_approval') == '1') {
|
||||
add_filter("woocommerce_registration_auth_new_customer", array($this, 'aios_registration_auth_new_customer'));
|
||||
}
|
||||
|
||||
if ($aio_wp_security->configs->get_value('aiowps_enable_registration_page_captcha') == '1') {
|
||||
add_filter('registration_errors', array($this, 'aiowps_validate_registration_with_captcha'), 10, 3);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This public function will add a special meta string in the users table
|
||||
* Meta field name: 'aiowps_account_status'
|
||||
* Meta field value: 'pending'
|
||||
*
|
||||
* @param int $user_id
|
||||
* @return void
|
||||
*/
|
||||
public function aiowps_user_registration_action_handler($user_id) {
|
||||
global $aio_wp_security;
|
||||
//Check if auto pending new account status feature is enabled
|
||||
if ($aio_wp_security->configs->get_value('aiowps_enable_manual_registration_approval') == '1') {
|
||||
if (AIOWPSecurity_Utility_Permissions::has_manage_cap() || (defined('WP_CLI') && WP_CLI)) {
|
||||
AIOWPSecurity_Audit_Events::event_user_registration($user_id, 'admin');
|
||||
return; //if the user has been added from admin side don't put in pending state
|
||||
}
|
||||
$res = add_user_meta($user_id, 'aiowps_account_status', 'pending');
|
||||
if (!$res) {
|
||||
$aio_wp_security->debug_logger->log_debug("aiowps_user_registration_action_handler: Error adding user meta data: aiowps_account_status", 4);
|
||||
}
|
||||
$user_ip_address = AIOWPSecurity_Utility_IP::get_user_ip_address();
|
||||
$res = add_user_meta($user_id, 'aiowps_registrant_ip', $user_ip_address);
|
||||
if (!$res) {
|
||||
$aio_wp_security->debug_logger->log_debug("aiowps_user_registration_action_handler: Error adding user meta data: aiowps_registrant_ip", 4);
|
||||
}
|
||||
AIOWPSecurity_Audit_Events::event_user_registration($user_id, 'pending');
|
||||
} else {
|
||||
if (AIOWPSecurity_Utility_Permissions::has_manage_cap()) {
|
||||
AIOWPSecurity_Audit_Events::event_user_registration($user_id, 'admin');
|
||||
} else {
|
||||
AIOWPSecurity_Audit_Events::event_user_registration($user_id, 'registered');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This public function will set the special meta string in the usermeta table so that the account becomes active
|
||||
* Meta field name: 'aiowps_account_status'
|
||||
* Meta field values: 'active', 'pending', etc
|
||||
*
|
||||
* @param int $user_id
|
||||
* @param string $status
|
||||
* @return void
|
||||
*/
|
||||
public function aiowps_set_user_account_status($user_id, $status) {
|
||||
global $aio_wp_security;
|
||||
$res = update_user_meta($user_id, 'aiowps_account_status', $status);
|
||||
if (!$res) {
|
||||
$aio_wp_security->debug_logger->log_debug("aiowps_set_user_account_status: Error updating user meta data: aiowps_account_status", 4);
|
||||
}
|
||||
}
|
||||
|
||||
public function aiowps_validate_registration_with_captcha($errors) {
|
||||
global $aio_wp_security;
|
||||
|
||||
$locked = $aio_wp_security->user_login_obj->check_locked_user();
|
||||
if (null == $locked) {
|
||||
//user is not locked continue
|
||||
} else {
|
||||
$errors->add('authentication_failed', __('<strong>ERROR</strong>: You are not allowed to register because your IP address is currently locked!', 'all-in-one-wp-security-and-firewall'));
|
||||
return $errors;
|
||||
}
|
||||
$verify_captcha = $aio_wp_security->captcha_obj->verify_captcha_submit();
|
||||
if (false === $verify_captcha) {
|
||||
// wrong answer was entered
|
||||
$errors->add('authentication_failed', __('<strong>ERROR</strong>: Your answer was incorrect - please try again.', 'all-in-one-wp-security-and-firewall'));
|
||||
return $errors;
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function serves the purpose of preventing login in certain plugins that enable user registration, such as WooCommerce and others.
|
||||
*
|
||||
* @return bool Returns false means do not authenticate on registration
|
||||
*/
|
||||
public function aios_registration_auth_new_customer() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Executable
+121
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;//Exit if accessed directly
|
||||
}
|
||||
|
||||
if (class_exists('AIOWPSecurity_Utility_API')) return;
|
||||
|
||||
class AIOWPSecurity_Utility_API {
|
||||
|
||||
/**
|
||||
* Performs a GET request.
|
||||
*
|
||||
* @param string $url The URL to send the request to.
|
||||
* @param array $args The request arguments.
|
||||
*
|
||||
* @return array|string|WP_Error The response or WP_Error on failure.
|
||||
*/
|
||||
public static function get($url, $args = array()) {
|
||||
return self::make_api_request($url, $args, 'GET');
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a POST request.
|
||||
*
|
||||
* @param string $url The URL to send the request to.
|
||||
* @param array $args The request arguments.
|
||||
*
|
||||
* @return array|string|WP_Error The response or WP_Error on failure.
|
||||
*/
|
||||
public static function post($url, $args = array()) {
|
||||
return self::make_api_request($url, $args, 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method to make the API request.
|
||||
*
|
||||
* @param string $url The URL to send the request to.
|
||||
* @param array $args The request arguments.
|
||||
* @param string $method The request method: 'GET' or 'POST'.
|
||||
*
|
||||
* @return array|string|WP_Error The response or WP_Error on failure.
|
||||
*/
|
||||
private static function make_api_request($url, $args = array(), $method = 'GET') {
|
||||
global $aio_wp_security;
|
||||
|
||||
// Validate the method
|
||||
$method = strtoupper($method);
|
||||
if (!in_array($method, array('GET', 'POST'))) {
|
||||
$aio_wp_security->debug_logger->log_debug("AIOWPSecurity_Utility_API::make_api_request() - Invalid request method. Only GET and POST are supported.", 4);
|
||||
return new WP_Error('aios_api_invalid_method', 'Invalid request method. Only GET and POST are supported.');
|
||||
}
|
||||
|
||||
// Validate the URL
|
||||
if ('' !== $url && !filter_var($url, FILTER_VALIDATE_URL)) {
|
||||
$aio_wp_security->debug_logger->log_debug("AIOWPSecurity_Utility_API::make_api_request() - Invalid or missing request URL.", 4);
|
||||
return new WP_Error('aios_api_invalid_url', 'Invalid or missing request URL.');
|
||||
}
|
||||
|
||||
// Set up default arguments
|
||||
$default_args = array(
|
||||
'headers' => array(),
|
||||
'body' => array(),
|
||||
'timeout' => 10,
|
||||
);
|
||||
|
||||
// Merge default arguments with provided arguments
|
||||
$request_args = wp_parse_args($args, $default_args);
|
||||
|
||||
// Make the request
|
||||
if ('POST' === $method) {
|
||||
$response = wp_remote_post($url, $request_args);
|
||||
} else {
|
||||
$response = wp_remote_get($url, $request_args);
|
||||
}
|
||||
|
||||
// Check for errors
|
||||
if (is_wp_error($response)) {
|
||||
// Get error code and message
|
||||
$error_code = $response->get_error_code();
|
||||
$error_message = $response->get_error_message();
|
||||
$error_data = $response->get_error_data(); // Optional additional error data
|
||||
|
||||
// Log the error details
|
||||
$aio_wp_security->debug_logger->log_debug("AIOWPSecurity_Utility_API::make_api_request() | Response error - Code: {$error_code}, Message: {$error_message}", 4);
|
||||
|
||||
// Log any additional error data (could be useful for debugging)
|
||||
if ($error_data) {
|
||||
if (is_array($error_data) || is_object($error_data)) {
|
||||
$aio_wp_security->debug_logger->log_debug("AIOWPSecurity_Utility_API::make_api_request() | Response error - Additional Error Data: " . json_encode($error_data), 4);
|
||||
} else {
|
||||
$aio_wp_security->debug_logger->log_debug("AIOWPSecurity_Utility_API::make_api_request() | Response error - Additional Error Data: {$error_data}", 4);
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Get the response body
|
||||
$response_body = wp_remote_retrieve_body($response);
|
||||
|
||||
// Check for JSON response
|
||||
$content_type = wp_remote_retrieve_header($response, 'content-type');
|
||||
if (false !== strpos($content_type, 'application/json')) {
|
||||
|
||||
// Decode the response body if it's JSON
|
||||
$decoded_body = json_decode($response_body, true);
|
||||
|
||||
// Log JSON decoding error if any
|
||||
if (JSON_ERROR_NONE !== json_last_error()) {
|
||||
$aio_wp_security->debug_logger->log_debug("AIOWPSecurity_Utility_API::make_api_request() - JSON decode error: " . json_last_error_msg(), 4);
|
||||
return new WP_Error('aios_api_json_decode_error', json_last_error_msg());
|
||||
}
|
||||
|
||||
// Return the decoded JSON response body
|
||||
return $decoded_body;
|
||||
}
|
||||
|
||||
// Return raw response body if it's not JSON
|
||||
return $response_body;
|
||||
}
|
||||
}
|
||||
Executable
+522
@@ -0,0 +1,522 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;//Exit if accessed directly
|
||||
}
|
||||
|
||||
if (class_exists('AIOWPSecurity_Utility_File')) return;
|
||||
|
||||
class AIOWPSecurity_Utility_File {
|
||||
|
||||
/**
|
||||
* This function returns an array of all the files and/or directories we wish to check permissions for
|
||||
*
|
||||
* @return array - array containing files and/or directories
|
||||
*/
|
||||
public static function get_files_and_dirs_to_check() {
|
||||
// NOTE: we can add to this list in future if we wish
|
||||
|
||||
//Get wp-config.php file path
|
||||
$wp_config_path = self::get_wp_config_file_path();
|
||||
$home_path = self::get_home_path();
|
||||
|
||||
$file_list = array(
|
||||
array('name' => 'root directory', 'path' => ABSPATH, 'permissions' => '0755'),
|
||||
array('name' => 'wp-includes/', 'path' => ABSPATH."wp-includes", 'permissions' => '0755'),
|
||||
array('name' => '.htaccess', 'path' => $home_path.".htaccess", 'permissions' => '0644'),
|
||||
array('name' => 'wp-admin/index.php', 'path' => ABSPATH."wp-admin/index.php", 'permissions' => '0644'),
|
||||
array('name' => 'wp-admin/js/', 'path' => ABSPATH."wp-admin/js/", 'permissions' => '0755'),
|
||||
array('name' => 'wp-content/themes/', 'path' => WP_CONTENT_DIR."/themes", 'permissions' => '0755'),
|
||||
array('name' => 'wp-content/plugins/', 'path' => WP_PLUGIN_DIR, 'permissions' => '0755'),
|
||||
array('name' => 'wp-admin/', 'path' => ABSPATH."wp-admin", 'permissions' => '0755'),
|
||||
array('name' => 'wp-content/', 'path' => WP_CONTENT_DIR, 'permissions' => '0755'),
|
||||
array('name' => 'wp-config.php', 'path' => $wp_config_path, 'permissions' => '0640'),
|
||||
//Add as many files or dirs as needed by following the convention above
|
||||
);
|
||||
|
||||
return apply_filters('aiowpsecurity_file_permission_list', $file_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns full path to mu-plugin directory
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_mu_plugin_dir() {
|
||||
return WPMU_PLUGIN_DIR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns path to wp-config
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_wp_config_file_path() {
|
||||
$wp_config_file = ABSPATH . 'wp-config.php';
|
||||
if (file_exists($wp_config_file)) {
|
||||
return $wp_config_file;
|
||||
} elseif (file_exists(dirname(ABSPATH) . '/wp-config.php')) {
|
||||
return dirname(ABSPATH) . '/wp-config.php';
|
||||
}
|
||||
return $wp_config_file;
|
||||
}
|
||||
|
||||
public static function write_content_to_file($file_path, $new_contents) {
|
||||
// Get the file permissions so we can revert back to it when we are done
|
||||
$permissions = octdec(substr(sprintf('%o', fileperms($file_path)), -4));
|
||||
@chmod($file_path, 0777);
|
||||
if (is_writeable($file_path)) {
|
||||
$handle = fopen($file_path, 'w');
|
||||
foreach ($new_contents as $line) {
|
||||
fwrite($handle, $line);
|
||||
}
|
||||
fclose($handle);
|
||||
@chmod($file_path, $permissions); //Let's change the file back to it's original permission setting
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static function backup_and_rename_wp_config($src_file_path, $prefix = 'backup') {
|
||||
global $aio_wp_security;
|
||||
|
||||
//Check to see if the main "backups" directory exists - create it otherwise
|
||||
$aiowps_backup_dir = WP_CONTENT_DIR.'/'.AIO_WP_SECURITY_BACKUPS_DIR_NAME;
|
||||
if (!AIOWPSecurity_Utility_File::create_dir($aiowps_backup_dir)) {
|
||||
$aio_wp_security->debug_logger->log_debug("backup_and_rename_wp_config - Creation of backup directory failed!", 4);
|
||||
return false;
|
||||
}
|
||||
|
||||
$src_parts = pathinfo($src_file_path);
|
||||
$backup_file_name = $prefix . '.' . $src_parts['basename'];
|
||||
|
||||
$backup_file_path = $aiowps_backup_dir . '/' . $backup_file_name;
|
||||
if (!copy($src_file_path, $backup_file_path)) {
|
||||
//Failed to make a backup copy
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backs up and renames the .htaccess file.
|
||||
*
|
||||
* This function creates a backup of the specified .htaccess file by copying it
|
||||
* to a designated backup directory with a randomly generated filename.
|
||||
*
|
||||
* @param string $src_file_path The path to the source .htaccess file to be backed up.
|
||||
*
|
||||
* @return string|false The name of the backup file on success, or false on failure.
|
||||
*/
|
||||
public static function backup_and_rename_htaccess($src_file_path) {
|
||||
global $aio_wp_security;
|
||||
|
||||
// Define the backup directory path
|
||||
$aiowps_backup_dir = WP_CONTENT_DIR . '/' . AIO_WP_SECURITY_BACKUPS_DIR_NAME;
|
||||
|
||||
// Ensure the backup directory exists or create it
|
||||
if (!AIOWPSecurity_Utility_File::create_dir($aiowps_backup_dir)) {
|
||||
$aio_wp_security->debug_logger->log_debug("backup_and_rename_htaccess - Creation of backup directory failed!", 4);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate a random prefix for the backup file name
|
||||
$random_prefix = AIOWPSecurity_Utility::generate_alpha_numeric_random_string(10);
|
||||
$backup_file_name = $random_prefix . '_htaccess_backup';
|
||||
|
||||
// Define the backup file path
|
||||
$backup_file_path = $aiowps_backup_dir . '/' . $backup_file_name .'.txt';
|
||||
|
||||
// Copy the source file to the backup location
|
||||
if (!copy($src_file_path, $backup_file_path)) {
|
||||
// Failed to make a backup copy
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return the backup file name on success
|
||||
return $backup_file_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will perform a recursive search for files in the path that match the passed in pattern
|
||||
*
|
||||
* @param string $pattern - the file pattern to search for
|
||||
* @param integer $flags - flags to apply on the search
|
||||
* @param string $path - the path we want to search
|
||||
*
|
||||
* @return boolean|array - an array of files matching the pattern or false if there was an error (directory traversal in path) or none found
|
||||
*/
|
||||
public static function recursive_file_search($pattern = '*', $flags = 0, $path = '') {
|
||||
$paths = glob($path.'*', GLOB_MARK|GLOB_ONLYDIR|GLOB_NOSORT);
|
||||
if (false === $paths) {
|
||||
return false;
|
||||
}
|
||||
$files = glob($path.$pattern, $flags);
|
||||
if (false === $files) {
|
||||
return false;
|
||||
}
|
||||
foreach ($paths as $path) {
|
||||
$files = array_merge($files, AIOWPSecurity_Utility_File::recursive_file_search($pattern, $flags, $path));
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful when wanting to echo file contents to screen with <br /> tags
|
||||
*
|
||||
* @param string $src_file
|
||||
* @return string
|
||||
*/
|
||||
public static function get_file_contents_with_br($src_file) {
|
||||
$file_contents = file_get_contents($src_file);
|
||||
return nl2br($file_contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful when wanting to echo file contents inside textarea
|
||||
*
|
||||
* @param string $src_file
|
||||
* @return string
|
||||
*/
|
||||
public static function get_file_contents($src_file) {
|
||||
$file_contents = file_get_contents($src_file);
|
||||
return $file_contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file's permission value eg, "0755"
|
||||
*
|
||||
* @param string $filepath
|
||||
* @return string
|
||||
*/
|
||||
public static function get_file_permission($filepath) {
|
||||
if (!function_exists('fileperms')) {
|
||||
$perms = '-1';
|
||||
} else {
|
||||
clearstatcache();
|
||||
$perms = substr(sprintf("%o", @fileperms($filepath)), -4);
|
||||
}
|
||||
return $perms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a write operation is possible for the file in question
|
||||
*
|
||||
* @param string $filepath
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_file_writable($filepath) {
|
||||
$test_string = ""; //We will attempt to append an empty string at the end of the file for the test
|
||||
$write_result = @file_put_contents($filepath, $test_string, FILE_APPEND | LOCK_EX);
|
||||
if (false === $write_result) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will compare the current permission value for a file or dir with the recommended value.
|
||||
* It will compare the individual "execute", "write" and "read" bits for the "public", "group" and "owner" permissions.
|
||||
* If the permissions for an actual bit value are greater than the recommended value it returns '0' (=less secure)
|
||||
* Otherwise it returns '1' which means it is secure
|
||||
* Accepts permission value parameters in octal, ie, "0777" or "777"
|
||||
*
|
||||
* @param string $recommended
|
||||
* @param string $actual
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_file_permission_secure($recommended, $actual) {
|
||||
$result = 1; //initialize return result
|
||||
|
||||
//Check "public" permissions
|
||||
$public_value_actual = substr($actual, -1, 1); //get dec value for actual public permission
|
||||
$public_value_rec = substr($recommended, -1, 1); //get dec value for recommended public permission
|
||||
|
||||
$pva_bin = sprintf('%04b', $public_value_actual); //Convert value to binary
|
||||
$pvr_bin = sprintf('%04b', $public_value_rec); //Convert value to binary
|
||||
//Compare the "executable" bit values for the public actual versus the recommended
|
||||
if (substr($pva_bin, -1, 1)<=substr($pvr_bin, -1, 1)) {
|
||||
//The "execute" bit is the same or less as the recommended value
|
||||
$result = 1*$result;
|
||||
} else {
|
||||
//The "execute" bit is switched on for the actual value - meaning it is less secure
|
||||
$result = 0*$result;
|
||||
}
|
||||
|
||||
//Compare the "write" bit values for the public actual versus the recommended
|
||||
if (substr($pva_bin, -2, 1)<=substr($pvr_bin, -2, 1)) {
|
||||
//The "write" bit is the same or less as the recommended value
|
||||
$result = 1*$result;
|
||||
} else {
|
||||
//The "write" bit is switched on for the actual value - meaning it is less secure
|
||||
$result = 0*$result;
|
||||
}
|
||||
|
||||
//Compare the "read" bit values for the public actual versus the recommended
|
||||
if (substr($pva_bin, -3, 1)<=substr($pvr_bin, -3, 1)) {
|
||||
//The "read" bit is the same or less as the recommended value
|
||||
$result = 1*$result;
|
||||
} else {
|
||||
//The "read" bit is switched on for the actual value - meaning it is less secure
|
||||
$result = 0*$result;
|
||||
}
|
||||
|
||||
//Check "group" permissions
|
||||
$group_value_actual = substr($actual, -2, 1);
|
||||
$group_value_rec = substr($recommended, -2, 1);
|
||||
$gva_bin = sprintf('%04b', $group_value_actual); //Convert value to binary
|
||||
$gvr_bin = sprintf('%04b', $group_value_rec); //Convert value to binary
|
||||
|
||||
//Compare the "executable" bit values for the group actual versus the recommended
|
||||
if (substr($gva_bin, -1, 1)<=substr($gvr_bin, -1, 1)) {
|
||||
//The "execute" bit is the same or less as the recommended value
|
||||
$result = 1*$result;
|
||||
} else {
|
||||
//The "execute" bit is switched on for the actual value - meaning it is less secure
|
||||
$result = 0*$result;
|
||||
}
|
||||
|
||||
//Compare the "write" bit values for the public actual versus the recommended
|
||||
if (substr($gva_bin, -2, 1)<=substr($gvr_bin, -2, 1)) {
|
||||
//The "write" bit is the same or less as the recommended value
|
||||
$result = 1*$result;
|
||||
} else {
|
||||
//The "write" bit is switched on for the actual value - meaning it is less secure
|
||||
$result = 0*$result;
|
||||
}
|
||||
|
||||
//Compare the "read" bit values for the public actual versus the recommended
|
||||
if (substr($gva_bin, -3, 1)<=substr($gvr_bin, -3, 1)) {
|
||||
//The "read" bit is the same or less as the recommended value
|
||||
$result = 1*$result;
|
||||
} else {
|
||||
//The "read" bit is switched on for the actual value - meaning it is less secure
|
||||
$result = 0*$result;
|
||||
}
|
||||
|
||||
//Check "owner" permissions
|
||||
$owner_value_actual = substr($actual, -3, 1);
|
||||
$owner_value_rec = substr($recommended, -3, 1);
|
||||
$ova_bin = sprintf('%04b', $owner_value_actual); //Convert value to binary
|
||||
$ovr_bin = sprintf('%04b', $owner_value_rec); //Convert value to binary
|
||||
|
||||
//Compare the "executable" bit values for the group actual versus the recommended
|
||||
if (substr($ova_bin, -1, 1)<=substr($ovr_bin, -1, 1)) {
|
||||
//The "execute" bit is the same or less as the recommended value
|
||||
$result = 1*$result;
|
||||
} else {
|
||||
//The "execute" bit is switched on for the actual value - meaning it is less secure
|
||||
$result = 0*$result;
|
||||
}
|
||||
|
||||
//Compare the "write" bit values for the public actual versus the recommended
|
||||
if (substr($ova_bin, -2, 1)<=substr($ovr_bin, -2, 1)) {
|
||||
//The "write" bit is the same or less as the recommended value
|
||||
$result = 1*$result;
|
||||
} else {
|
||||
//The "write" bit is switched on for the actual value - meaning it is less secure
|
||||
$result = 0*$result;
|
||||
}
|
||||
|
||||
//Compare the "read" bit values for the public actual versus the recommended
|
||||
if (substr($ova_bin, -3, 1)<=substr($ovr_bin, -3, 1)) {
|
||||
//The "read" bit is the same or less as the recommended value
|
||||
$result = 1*$result;
|
||||
} else {
|
||||
//The "read" bit is switched on for the actual value - meaning it is less secure
|
||||
$result = 0*$result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a directory exists and creates one if it does not
|
||||
*
|
||||
* @param string $dirpath
|
||||
* @return boolean
|
||||
*/
|
||||
public static function create_dir($dirpath = '') {
|
||||
$res = true;
|
||||
if ('' != $dirpath) {
|
||||
//TODO - maybe add some checks to make sure someone is not passing a path with a filename, ie, something which has ".<extension>" at the end
|
||||
//$path_parts = pathinfo($dirpath);
|
||||
//$dirpath = $path_parts['dirname'] . '/' . $path_parts['basename'];
|
||||
if (!file_exists($dirpath)) {
|
||||
$res = mkdir($dirpath, 0755);
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a directory from the local filesystem
|
||||
*
|
||||
* @param string $dir - the directory
|
||||
* @param boolean $contents_only - if set to true, then do not remove the directory, but only empty it of contents
|
||||
*
|
||||
* @return boolean - success/failure
|
||||
*/
|
||||
public static function remove_local_directory($dir, $contents_only = false) {
|
||||
|
||||
$handle = opendir($dir);
|
||||
if ($handle) {
|
||||
while (false !== ($entry = readdir($handle))) {
|
||||
if ('.' !== $entry && '..' !== $entry) {
|
||||
if (is_dir($dir.'/'.$entry)) {
|
||||
self::remove_local_directory($dir.'/'.$entry, false);
|
||||
} else {
|
||||
@unlink($dir.'/'.$entry);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore warning and proceed to try and remove the rest of the files
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_resource($handle)) closedir($handle);
|
||||
}
|
||||
|
||||
return $contents_only ? true : rmdir($dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get home path.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_home_path() {
|
||||
// Make the scope of $wp_file_descriptions global, so that when wp-admin/includes/file.php assigns to it, it is adjusting the global variable as intended
|
||||
global $wp_file_descriptions; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- We need to make this global see above comment
|
||||
if (!function_exists('get_home_path')) require_once(ABSPATH. '/wp-admin/includes/file.php');
|
||||
return wp_normalize_path(get_home_path());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if wp config file.
|
||||
*
|
||||
* @param string $file_contents File contents
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function check_if_wp_config_contents($file_contents) {
|
||||
return !empty($file_contents) && preg_match("/define\(\s*['\"]DB_NAME['\"]/i", $file_contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if valid aios settings text
|
||||
*
|
||||
* @param string $text - Settings text
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function check_is_aiowps_settings($text) {
|
||||
return (false !== strpos($text, 'aiowps_enable_login_lockdown'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if valid AIOS settings file contents and returns contents as string
|
||||
*
|
||||
* @param string $file_contents File contents
|
||||
*
|
||||
* @return int|string
|
||||
*/
|
||||
public static function check_if_valid_aiowps_settings_content($file_contents) {
|
||||
// Check a known AIOS config strings to see if it is contained within this file
|
||||
return !empty($file_contents) && self::check_is_aiowps_settings($file_contents);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans WP key core files and directory permissions and populates a wp wide_fat table
|
||||
* Displays a red background entry with a "Fix" button for permissions which are "777"
|
||||
* Displays a yellow background entry with a "Fix" button for permissions which are less secure than the recommended
|
||||
* Displays a green entry for permissions which are as secure or better than the recommended
|
||||
*
|
||||
* @param string $name - file name
|
||||
* @param string $path - file path
|
||||
* @param string $recommended - file permission
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function show_wp_filesystem_permission_status($name, $path, $recommended) {
|
||||
$fix = false;
|
||||
$configmod = self::get_file_permission($path);
|
||||
if (self::is_file_world_writable($configmod)) {
|
||||
$trclass = "aio_table_row_red"; // Display a red background if permissions are set as least secure ("777")
|
||||
$fix = true;
|
||||
} elseif ($configmod != $recommended) {
|
||||
// $res = $this->is_file_permission_secure($recommended, $configmod);
|
||||
$res = self::is_file_permission_secure($recommended, $configmod);
|
||||
if ($res) {
|
||||
$trclass = "aio_table_row_green"; //If the current permissions are even tighter than recommended then display a green row
|
||||
} else {
|
||||
$trclass = "aio_table_row_yellow"; // Display a yellow background if permissions are set to something different than recommended
|
||||
$fix = true;
|
||||
}
|
||||
} else {
|
||||
$trclass = "aio_table_row_green";
|
||||
}
|
||||
echo "<tr class=".$trclass.">";
|
||||
echo '<td class="column-primary" data-colname="' . esc_attr(__('Name', 'all-in-one-wp-security-and-firewall')) . '">' . $name . '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __('Show more details', 'all-in-one-wp-security-and-firewall') . '</span></button>' . "</td>";
|
||||
echo '<td data-colname="' . esc_attr(__('File/Folder', 'all-in-one-wp-security-and-firewall')) . '">' . $path . "</td>";
|
||||
echo '<td data-colname="' . esc_attr(__('Current permissions', 'all-in-one-wp-security-and-firewall')) . '">' . $configmod . '</td>';
|
||||
echo '<td data-colname="' . esc_attr(__('Recommended permissions', 'all-in-one-wp-security-and-firewall')) . '">' . $recommended . '</td>';
|
||||
if ($fix) {
|
||||
echo '<td data-colname="' . esc_attr(__('Recommended action', 'all-in-one-wp-security-and-firewall')) . '">
|
||||
<input type="submit" onclick="return set_file_permission_tochange(\'' . esc_js($path) . '\', \'' . esc_js($recommended) . '\')" name="aiowps_fix_permissions" value="' . esc_attr(__('Set recommended permissions', 'all-in-one-wp-security-and-firewall')) . '" class="button-secondary">
|
||||
</td>';
|
||||
} else {
|
||||
echo '<td>'.__('No action required', 'all-in-one-wp-security-and-firewall').'</td>';
|
||||
}
|
||||
echo "</tr>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given file permissions indicate that the file is world-writable.
|
||||
*
|
||||
* This function accepts a string representation of file permissions (in octal format)
|
||||
* and checks if the write bit is set for 'others' (world). It ensures the string
|
||||
* is 4 characters long by prepending a '0' if necessary and then extracts and
|
||||
* evaluates the last digit to determine if the file is world-writable.
|
||||
*
|
||||
* @param string $permissions The file permissions in octal format (e.g., "0777", "0755").
|
||||
* @return bool Returns true if the file is world-writable, otherwise false.
|
||||
*/
|
||||
public static function is_file_world_writable($permissions) {
|
||||
if (strlen($permissions) == 3) {
|
||||
$permissions = '0' . $permissions;
|
||||
}
|
||||
|
||||
// Get the 'others' permissions (last digit)
|
||||
$others_permissions = (int) substr($permissions, -1);
|
||||
|
||||
// Check if the write bit (2) is set for 'others'
|
||||
return (bool) ($others_permissions & 0x2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the larger file and get last 100 lines etc in an efficient way
|
||||
*
|
||||
* @param string $filepath - file path
|
||||
* @param integer $offset - offest to start reading the file from that line
|
||||
* @param integer $num - number of lines to read
|
||||
* @param boolean $reverse - return in reverse order
|
||||
*
|
||||
* @return array|boolean - file lines
|
||||
*/
|
||||
public static function read_file_lines($filepath, $offset = 0, $num = 10, $reverse = false) {
|
||||
global $aio_wp_security;
|
||||
try {
|
||||
$file = new \SplFileObject($filepath, 'r');
|
||||
if (-1 == $offset) {
|
||||
$file->seek(PHP_INT_MAX); // PHP_INT_MAX to seeek to last line
|
||||
$last_line = $file->key();
|
||||
$offset = $last_line > $num ? $last_line - $num : 0;
|
||||
}
|
||||
$lines = new \LimitIterator($file, $offset, $offset + $num);
|
||||
$lines_arr = iterator_to_array($lines);
|
||||
if ($reverse) $lines_arr = array_reverse($lines_arr);
|
||||
return $lines_arr;
|
||||
} catch (\Exception $e) {
|
||||
$aio_wp_security->debug_logger->log_debug("AIOS - Unable to read file: ". $filepath . " - " .$e->getMessage(), 4);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Executable
+238
@@ -0,0 +1,238 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) {
|
||||
exit; //Exit if accessed directly
|
||||
}
|
||||
|
||||
class AIOWPSecurity_Utility_Firewall {
|
||||
|
||||
/**
|
||||
* Returned if the user is required to setup auto_prepend_file manually
|
||||
*
|
||||
* @var null
|
||||
*/
|
||||
const MANUAL_SETUP = null;
|
||||
|
||||
/**
|
||||
* Returns the current path to our firewall file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_firewall_path() {
|
||||
return wp_normalize_path(AIO_WP_SECURITY_PATH.'/classes/firewall/wp-security-firewall.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the firewall rules path.
|
||||
*
|
||||
* @param boolean $mkdir - whether or not to create the directory if it doesn't exist
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_firewall_rules_path($mkdir = false) {
|
||||
$upload_dir_info = wp_get_upload_dir();
|
||||
$base = $upload_dir_info['basedir'];
|
||||
|
||||
// We want the base to always point to the main site's upload directory and not the subsite's.
|
||||
if (!is_main_site()) {
|
||||
$base = preg_replace('#/sites/'.get_current_blog_id().'/?$#', '', $base);
|
||||
}
|
||||
|
||||
$firewall_rules_path = trailingslashit("{$base}/aios/firewall-rules");
|
||||
|
||||
if ($mkdir) {
|
||||
wp_mkdir_p($firewall_rules_path);
|
||||
}
|
||||
|
||||
return wp_normalize_path($firewall_rules_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether we're on the firewall page
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_firewall_page() {
|
||||
global $pagenow;
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- PCP warning. No nonce.
|
||||
return ('admin.php' == $pagenow && isset($_GET['page']) && false !== strpos(sanitize_title(wp_unslash($_GET['page'])), AIOWPSEC_MENU_SLUG_PREFIX.'_firewall'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current path to our bootstrap file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_bootstrap_path() {
|
||||
return path_join(AIOWPSecurity_Utility_File::get_home_path(), 'aios-bootstrap.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full path to our mu-plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_muplugin_path() {
|
||||
return path_join(AIOWPSecurity_Utility_File::get_mu_plugin_dir(), 'aios-firewall-loader.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns our managed mu-plugin file
|
||||
*
|
||||
* @return AIOWPSecurity_Block_Muplugin
|
||||
*/
|
||||
public static function get_muplugin_file() {
|
||||
return new AIOWPSecurity_Block_Muplugin(AIOWPSecurity_Utility_Firewall::get_muplugin_path());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns our managed wp-config file
|
||||
*
|
||||
* @return AIOWPSecurity_Block_WpConfig
|
||||
*/
|
||||
public static function get_wpconfig_file() {
|
||||
return new AIOWPSecurity_Block_WpConfig(AIOWPSecurity_Utility_File::get_wp_config_file_path());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns our managed bootstrap file
|
||||
*
|
||||
* @return AIOWPSecurity_Block_Bootstrap
|
||||
*/
|
||||
public static function get_bootstrap_file() {
|
||||
return new AIOWPSecurity_Block_Bootstrap(AIOWPSecurity_Utility_Firewall::get_bootstrap_path());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the auto_prepend_file directive, if already set
|
||||
*
|
||||
* @param string $source - where to check for the directive
|
||||
* @return string - returns the directive if set, or empty string if not set
|
||||
*/
|
||||
public static function get_already_set_directive($source = '') {
|
||||
global $aio_wp_security;
|
||||
if (!empty($source)) {
|
||||
clearstatcache();
|
||||
if (file_exists($source) && is_readable($source)) {
|
||||
try {
|
||||
$vals = @parse_ini_file($source); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
||||
|
||||
if (false !== $vals && isset($vals['auto_prepend_file'])) {
|
||||
return $vals['auto_prepend_file'];
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
$aio_wp_security->debug_logger->log_debug($exception->getMessage(), 4);
|
||||
return '';
|
||||
} catch (Error $error) { // phpcs:ignore PHPCompatibility.Classes.NewClasses.errorFound -- this won't run on PHP 5.6 so we still want to catch it on other versions
|
||||
$aio_wp_security->debug_logger->log_debug($error->getMessage(), 4);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$directive = ini_get('auto_prepend_file');
|
||||
if (false !== $directive) {
|
||||
return $directive;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file that's necessary to load our firewall
|
||||
*
|
||||
* @return AIOWPSecurity_Block_File|null file needed to load the firewall
|
||||
*/
|
||||
public static function get_server_file() {
|
||||
$server_type = AIOWPSecurity_Utility::get_server_type();
|
||||
$is_cgi = false;
|
||||
$sapi = PHP_SAPI;
|
||||
|
||||
if (false !== stripos($sapi, 'cgi')) {
|
||||
$is_cgi = true;
|
||||
}
|
||||
|
||||
if (AIOWPSecurity_Utility::UNSUPPORTED_SERVER_TYPE === $server_type) {
|
||||
return self::MANUAL_SETUP;
|
||||
|
||||
} elseif (false === $is_cgi && 'apache' === $server_type) {
|
||||
$htpath = path_join(AIOWPSecurity_Utility_File::get_home_path(), '.htaccess');
|
||||
return new AIOWPSecurity_Block_Htaccess($htpath);
|
||||
|
||||
} elseif ('litespeed' === $server_type || 'litespeed' === $sapi) {
|
||||
$htpath = path_join(AIOWPSecurity_Utility_File::get_home_path(), '.htaccess');
|
||||
return new AIOWPSecurity_Block_Litespeed($htpath);
|
||||
|
||||
} else {
|
||||
$userini = path_join(AIOWPSecurity_Utility_File::get_home_path(), '.user.ini');
|
||||
return new AIOWPSecurity_Block_Userini($userini);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the firewall has been setup
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function is_firewall_setup() {
|
||||
$is_in_bootstrap = (true === self::get_bootstrap_file()->contains_contents());
|
||||
|
||||
$files = array(
|
||||
self::get_server_file(),
|
||||
self::get_wpconfig_file(),
|
||||
self::get_muplugin_file(),
|
||||
);
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (AIOWPSecurity_Utility_Firewall::MANUAL_SETUP === $file) continue;
|
||||
|
||||
if ($is_in_bootstrap && (true === $file->contains_contents())) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to remove our firewall.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function remove_firewall() {
|
||||
global $aio_wp_security;
|
||||
|
||||
$firewall_files = array(
|
||||
'server' => AIOWPSecurity_Utility_Firewall::get_server_file(),
|
||||
'bootstrap' => AIOWPSecurity_Utility_Firewall::get_bootstrap_file(),
|
||||
'wpconfig' => AIOWPSecurity_Utility_Firewall::get_wpconfig_file(),
|
||||
'muplugin' => AIOWPSecurity_Utility_Firewall::get_muplugin_file(),
|
||||
);
|
||||
|
||||
foreach ($firewall_files as $file) {
|
||||
if (AIOWPSecurity_Utility_Firewall::MANUAL_SETUP === $file) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (true === $file->contains_contents()) {
|
||||
|
||||
$removed = $file->remove_contents();
|
||||
|
||||
if (is_wp_error($removed)) {
|
||||
$error_message = $removed->get_error_message();
|
||||
$error_message .= ' - ';
|
||||
$error_message .= $removed->get_error_data();
|
||||
$aio_wp_security->debug_logger->log_debug($error_message, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Delete our mu-plugin, if it's created
|
||||
clearstatcache();
|
||||
$muplugin_path = $firewall_files['muplugin'];
|
||||
if (file_exists($muplugin_path)) {
|
||||
@wp_delete_file($muplugin_path); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
||||
}
|
||||
|
||||
$aio_wp_security->configs->set_value('aios_firewall_dismiss', false, true);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user