Current File : /home/n742ef5/.trash/wp-content.3/plugins/security-malware-firewall/security-malware-firewall.php |
<?php
/*
Plugin Name: Security by CleanTalk
Plugin URI: https://wordpress.org/plugins/security-malware-firewall/
Description: Security & Malware scan by CleanTalk to protect your website from online threats and viruses. IP/Country FireWall, Web application FireWall. Detailed stats and logs to have full control.
Author: CleanTalk Security
Version: 2.127
Author URI: https://cleantalk.org
Text Domain: security-malware-firewall
Domain Path: /i18n
*/
use CleantalkSP\SpbctWP\Activator;
use CleantalkSP\SpbctWP\FSWatcher\SpbctWpFSWController as FSWatcherController;
use CleantalkSP\SpbctWP\DB;
use CleantalkSP\SpbctWP\Firewall;
use CleantalkSP\SpbctWP\Firewall\BFP;
use CleantalkSP\SpbctWP\Firewall\FW;
use CleantalkSP\SpbctWP\Firewall\TC;
use CleantalkSP\SpbctWP\Firewall\WAF;
use CleantalkSP\SpbctWP\Cron as SpbcCron;
use CleantalkSP\SpbctWP\Firewall\WafBlocker;
use CleantalkSP\SpbctWP\HTTP\CDNHeadersChecker;
use CleantalkSP\SpbctWP\RemoteCalls as SpbcRemoteCalls;
use CleantalkSP\SpbctWP\RenameLoginPage;
use CleantalkSP\SpbctWP\Sanitize;
use CleantalkSP\SpbctWP\Scanner\Stages\SignatureAnalysis\SignatureAnalysisFacade;
use CleantalkSP\SpbctWP\State;
use CleantalkSP\SpbctWP\Transaction;
use CleantalkSP\SpbctWP\Variables\Cookie;
use CleantalkSP\Updater\Updater;
use CleantalkSP\Variables\Get;
use CleantalkSP\Variables\Post;
use CleantalkSP\Variables\Server;
use CleantalkSP\SpbctWP\Helpers\IP;
use CleantalkSP\SpbctWP\Helpers\HTTP;
use CleantalkSP\SpbctWP\API as SpbcAPI;
// Prevent direct call
if ( ! defined('WPINC') ) {
die('Not allowed!');
}
// Getting version form main file (look above)
$plugin_info = get_file_data(__FILE__, array('Version' => 'Version', 'Name' => 'Plugin Name', 'Description' => 'Description'));
$plugin_version__agent = $plugin_info['Version'];
// Converts xxx.xxx.xx-dev to xxx.xxx.2xx
// And xxx.xxx.xx-fix to xxx.xxx.1xx
if ( preg_match('@^(\d+)\.(\d+)\.(\d{1,2})-(dev|fix)$@', $plugin_version__agent, $m) ) {
$plugin_version__agent = $m[1] . '.' . $m[2] . '.' . ($m[4] === 'dev' ? '2' : '1') . str_pad($m[3], 2, '0', STR_PAD_LEFT);
}
// Common params
define('SPBC_NAME', $plugin_info['Name']);
define('SPBC_VERSION', $plugin_info['Version']);
define('SPBC_AGENT', 'wordpress-security-' . $plugin_version__agent);
define('SPBC_USER_AGENT', 'Cleantalk-Security-Wordpress-Plugin/' . $plugin_info['Version']);
define('SPBC_API_URL', 'https://api.cleantalk.org'); //Api URL
define('SPBC_PLUGIN_DIR', dirname(__FILE__) . DIRECTORY_SEPARATOR); //System path. Plugin root folder with '/'.
define('SPBC_PLUGIN_BASE_NAME', plugin_basename(__FILE__)); //Plugin base name.
define(
'SPBC_PATH',
is_ssl()
? preg_replace('/^http(s)?/', 'https', plugins_url('', __FILE__))
: plugins_url('', __FILE__)
); //HTTP(S)? path. Plugin root folder without '/'.
// SSL Serttificate path
if ( ! defined('CLEANTALK_CASERT_PATH') ) {
define('CLEANTALK_CASERT_PATH', file_exists(ABSPATH . WPINC . '/certificates/ca-bundle.crt') ? ABSPATH . WPINC . '/certificates/ca-bundle.crt' : '');
}
// Options names
define('SPBC_DATA', 'spbc_data'); //Option name with different plugin data.
define('SPBC_SETTINGS', 'spbc_settings'); //Option name with plugin settings.
define('SPBC_NETWORK_SETTINGS', 'spbc_network_settings'); //Option name with plugin network settings.
define('SPBC_CRON', 'spbc_cron'); //Option name with scheduled tasks.
define('SPBC_ERRORS', 'spbc_errors'); //Option name with errors.
define('SPBC_DEBUG', 'spbc_debug'); //Option name with a debug data. Empty by default.
define('SPBC_PLUGINS', 'spbc_plugins'); //Option name with a debug data. Empty by default.
define('SPBC_THEMES', 'spbc_themes'); //Option name with a debug data. Empty by default.
// Different params
define('SPBC_REMOTE_CALL_SLEEP', 10); //Minimum time between remote call
define('SPBC_LAST_ACTIONS_TO_VIEW', 20); //Nubmer of last actions to show in plugin settings page.
// Auth params
define('SPBC_2FA_KEY_TTL', 600); // 2fa key lifetime in seconds
// DataBase params
global $wpdb;
define('SPBC_TBL_FIREWALL_DATA', $wpdb->base_prefix . 'spbc_firewall_data');
define('SPBC_TBL_FIREWALL_DATA_V4', SPBC_TBL_FIREWALL_DATA . '_v4');
define('SPBC_TBL_FIREWALL_DATA_V6', SPBC_TBL_FIREWALL_DATA . '_v6');
define('SPBC_TBL_FIREWALL_DATA__IPS', $wpdb->prefix . 'spbc_firewall__personal_ips');
define('SPBC_TBL_FIREWALL_DATA__IPS_V4', SPBC_TBL_FIREWALL_DATA__IPS . '_v4'); // Table with firewall IPS v4
define('SPBC_TBL_FIREWALL_DATA__IPS_V6', SPBC_TBL_FIREWALL_DATA__IPS . '_v6'); // Table with firewall IPS v6
define('SPBC_TBL_FIREWALL_DATA__COUNTRIES', $wpdb->prefix . 'spbc_firewall__personal_countries'); // Table with firewall countries.
define('SPBC_TBL_FIREWALL_LOG', $wpdb->prefix . 'spbc_firewall_logs'); // Table with firewall logs.
define('SPBC_TBL_SESSIONS', $wpdb->prefix . 'spbc_sessions'); // Alternative sessions table
define('SPBC_TBL_MONITORING_USERS', $wpdb->prefix . 'spbc_monitoring_users'); // Table with users monitoring data
define('SPBC_TBL_SECURITY_LOG', $wpdb->prefix . 'spbc_auth_logs'); // Table with security logs.
define('SPBC_TBL_TC_LOG', $wpdb->prefix . 'spbc_traffic_control_logs'); // Table with traffic control logs.
define('SPBC_TBL_BFP_BLOCKED', $wpdb->prefix . 'spbc_bfp_blocked'); // Table with traffic control logs.
define('SPBC_TBL_SCAN_FILES', $wpdb->base_prefix . 'spbc_scan_results'); // Table with scan results.
define('SPBC_TBL_SCAN_RESULTS_LOG', $wpdb->base_prefix . 'spbc_scan_results_log'); // Table with log of scan results.
define('SPBC_TBL_SCAN_LINKS', $wpdb->prefix . 'spbc_scan_links_logs'); // For links scanner. Results of scan.
define('SPBC_TBL_SCAN_FRONTEND', $wpdb->base_prefix . 'spbc_scan_frontend'); // For frontend scanner. Results of scan.
define('SPBC_TBL_SCAN_SIGNATURES', $wpdb->base_prefix . 'spbc_scan_signatures'); // For malware signatures.
define('SPBC_TBL_BACKUPED_FILES', $wpdb->prefix . 'spbc_backuped_files'); // Contains backuped files
define('SPBC_TBL_BACKUPS', $wpdb->prefix . 'spbc_backups'); // Contains backup info.
define('SPBC_TBL_IMPORTANT_FILES', $wpdb->prefix . 'spbc_important_files'); // Contains important files for monitoring.
define('SPBC_TBL_IMPORTANT_FILES_SNAPSHOTS', $wpdb->prefix . 'spbc_important_file_snapshots'); // Contains state of important files.
define('SPBC_TBL_CURE_LOG', $wpdb->base_prefix . 'spbc_cure_log'); // Table with scan results.
define('SPBC_SELECT_LIMIT', 1500); // Select limit for logs.
define('SPBC_WRITE_LIMIT', 5000); // Write limit for firewall data.
// Multisite
define('SPBC_WPMS', (is_multisite() ? true : false)); // WMPS is enabled
// Scanner params for background scanning
define('SPBC_SCAN_SURFACE_AMOUNT', 1000); // Surface scan amount for 1 iteration
define('SPBC_SCAN_SURFACE_PERIOD', 30); // Surface scan call period
define('SPBC_SCAN_MODIFIED_AMOUNT', 5); // Deep scan amount for 1 iteration
define('SPBC_SCAN_SIGNATURE_AMOUNT', 20); // Deep scan amount for 1 iteration
define('SPBC_SCAN_MODIFIED_PERIOD', 30); // Deep scan call period
define('SPBC_SCAN_LINKS_AMOUNT', 10); // Links scan amount for 1 iteration
define('SPBC_SCAN_FRONTEND_AMOUNT', 10); // Links scan amount for 1 iteration
define('SPBC_SCAN_LINKS_PERIOD', 30); // Links scan call period
define('SPBC_PSCAN_UPDATE_FILES_STATUS_PERIOD', 300); // Check cloud analysis files status period
// brief data limits
define('SPBC_BRIEF_DATA_DAYS_LIMIT', 7); // how many days will be logs looked for
define('SPBC_BRIEF_DATA_ACTIONS_LIMIT', 10); // how many actions will be logs looked for
require_once SPBC_PLUGIN_DIR . 'lib/spbc-php-patch.php'; // PHP functions patches
require_once SPBC_PLUGIN_DIR . 'lib/autoloader.php'; // Autoloader
require_once SPBC_PLUGIN_DIR . 'inc/spbc-backups.php';
require_once SPBC_PLUGIN_DIR . 'inc/fw-update.php';
// Misc libs
require_once SPBC_PLUGIN_DIR . 'inc/spbc-tools.php'; // Different helper functions
require_once SPBC_PLUGIN_DIR . 'inc/spbc-pluggable.php'; // WordPress functions
require_once SPBC_PLUGIN_DIR . 'inc/spbc-scanner.php';
// ArrayObject with settings and other global variables
global $spbc;
$spbc = new State(
'spbc',
array(
'settings',
'data',
'remote_calls',
'debug',
'installing',
'errors',
'fw_stats'
),
is_multisite(),
is_main_site()
);
require_once SPBC_PLUGIN_DIR . 'inc/spbc-auth.php';
// Update plugin's data to current version
spbc_update_actions();
// Remote calls
if ( SpbcRemoteCalls::check() ) {
try {
if ( Get::get('spbc_remote_call_action') === 'run_service_template_get' ) {
require_once(SPBC_PLUGIN_DIR . 'inc/spbc-settings.php');
}
$rc = new SpbcRemoteCalls($spbc);
$rc->process();
} catch ( Exception $e ) {
die(json_encode(array('ERROR:' => $e->getMessage())));
}
}
//First start
if ( $spbc->settings && $spbc->key_is_ok) {
// FireWall
if (
! $spbc->fw_stats['is_on_maintenance']
&& $spbc->moderate // Plugin is enabled
&& isset($spbc->fw_stats['last_updated'], $spbc->fw_stats['entries']) // Plugin's FW base is updated
&& ! CleantalkSP\SpbctWP\Firewall::isException()
&& (( ! is_admin() // Not admin area
&& ! defined('DOING_AJAX') // Pass AJAX
&& ! spbc_wp_doing_cron() // Pass WP cron tasks
&& ! \CleantalkSP\Variables\Server::inUri('/favicon.ico') // Exclude favicon.ico requests from the check
&& ! spbc_mailpoet_doing_cron())
|| ! empty($_FILES) // Or file downloads
)
) {
spbc_upload_checker__check();
spbc_firewall__check();
}
} elseif ( isset($spbc->errors) && ! isset($spbc->errors['apikey']) ) {
if ($spbc->settings['spbc_key'] === '') {
$text = __('Access key is empty.', 'security-malware-firewall');
} else {
$text = __('Unknown access key.', 'security-malware-firewall');
}
$spbc->error_add('apikey', $text);
}
// Disable XMLRPC if setting is enabled
if ( $spbc->settings['wp__disable_xmlrpc'] ) {
add_filter('xmlrpc_enabled', '__return_false');
}
// Disable WordPress REST API for non-authenticated
if ( $spbc->settings['wp__disable_rest_api_for_non_authenticated'] ) {
add_filter(
'rest_authentication_errors',
function ($result) {
if ( empty($result) && ! is_user_logged_in() ) {
return new WP_Error(
'rest_not_logged_in',
'You are not currently logged in.',
array('status' => 401)
);
}
return $result;
}
);
}
if ( ! is_admin() && $spbc->settings['misc__prevent_logins_collecting'] ) {
add_filter('redirect_canonical', 'spbc_redirect_to_honeypot_login', 1, 2);
}
function spbc_redirect_to_honeypot_login($redirect, $request)
{
if ( preg_match('/author=\d+/i', $request) ) {
add_filter('author_link', 'spbc_change_author_name', 10, 3);
}
return $redirect;
}
function spbc_change_author_name($link, $_author_id, $_author_nicename)
{
$link = preg_replace('@(.*?)([\w-]+\/)$@', '$1honeypot_login_' . microtime(true), $link);
wp_redirect($link);
die();
}
if ( $spbc->settings['monitoring__users'] ) {
add_action('admin_head', array( '\CleantalkSP\Monitoring\User', 'record' ));
add_action('wp_head', array( '\CleantalkSP\Monitoring\User', 'record' ));
}
//Password-protected pages also uses wp-login page, we should not break it
if ( $spbc->settings['login_page_rename__enabled'] ) {
if ( Get::get('action') === 'postpass' ) {
require ABSPATH . 'wp-includes/pluggable.php';
require ABSPATH . 'wp-login.php';
}
new RenameLoginPage(
$spbc->settings['login_page_rename__name'],
$spbc->settings['login_page_rename__redirect']
);
}
// Logged hooks
register_activation_hook(__FILE__, 'spbc_activation');
register_deactivation_hook(__FILE__, 'spbc_deactivation');
register_uninstall_hook(__FILE__, 'spbc_uninstall');
// Hook for newly added blog
Activator::addActionForNetworkBlogLegacy(get_bloginfo('version'));
add_action('plugins_loaded', 'spbc_plugin_loaded', 1); // Main hook
// Posts hooks
add_action('wp_insert_post', 'spbc_update_postmeta_links', 10, 3);
add_action('wp_insert_comment', 'spbc_update_postmeta_links__by_comment', 10, 2);
// Set headers
add_action('init', 'spbc_set_headers');
if ( $spbc->settings['spbc_trusted_and_affiliate__footer'] === '1' ) {
add_action('wp_enqueue_scripts', 'spbc_attach_public_css');
add_action('wp_footer', 'spbc_hook__wp_footer_trusted_text', 998);
}
// Cron
global $spbc_cron; // Letting know functions that they are running under spbc_cron
$spbc_cron = new SpbcCron();
! SpbcRemoteCalls::check() && $spbc_cron->execute();
unset($spbc_cron);
if ($spbc->settings['scanner__fs_watcher']) {
$fswatch_params = array(
'dir_to_watch' => ABSPATH,
'exclude_dirs' => array(),
'extensions_to_watch' => array('php'),
);
FSWatcherController::work($fswatch_params);
}
if ( is_admin() || is_network_admin() ) {
// Async loading for JavaScript
add_filter('script_loader_tag', 'spbc_admin_add_script_attribute', 10, 3);
include_once SPBC_PLUGIN_DIR . 'inc/spbc-admin.php';
include_once SPBC_PLUGIN_DIR . 'templates/spbc_settings_main.php'; // Templates for settings pgae
add_action('admin_init', array('CleantalkSP\SpbctWP\Activator', 'redirectAfterActivation'), 1); // Redirect after activation
add_action('admin_init', 'spbc_admin_init', 1, 1); // Main admin hook
add_action('admin_menu', 'spbc_admin_add_page'); // Admin pages
add_action('network_admin_menu', 'spbc_admin_add_page'); // Network admin pages
add_action('admin_enqueue_scripts', 'spbc_enqueue_scripts'); // Scripts
// Getting dashboard widget statistics by click
if ( (int) Post::get('spbc_brief_refresh') === 1 ) {
spbc_set_brief_data();
}
if ( $spbc->settings['wp__dashboard_widget__show'] ) {
add_action('wp_dashboard_setup', 'spbc_widget_scripts_init');
add_action('wp_dashboard_setup', 'spbc_dashboard_statistics_widget');
}
add_action('admin_init', function () {
global $spbc;
$admin_banners_handler = new \CleantalkSP\SpbctWP\AdminBannersModule\AdminBannersHandler($spbc);
$admin_banners_handler->handle();
});
// Customize row with the plugin on plugins list page.
if ( ( isset($pagenow) && $pagenow === 'plugins.php' ) || ( isset($_SERVER['REQUEST_URI']) && strpos($_SERVER['REQUEST_URI'], 'plugins.php') !== false ) ) {
add_filter('plugin_action_links_' . SPBC_PLUGIN_BASE_NAME, 'spbc_plugin_action_links', 10, 2);
add_filter('network_admin_plugin_action_links_' . SPBC_PLUGIN_BASE_NAME, 'spbc_plugin_action_links', 10, 2);
add_filter('all_plugins', 'spbc_admin__change_plugin_description');
add_filter('plugin_row_meta', 'spbc_plugin_links_meta', 10, 2);
}
// Public scripts
} else {
$spbc->public_scripts_attached = false;
// Alternative cookies JS script
add_action('wp_enqueue_scripts', 'spbc_enqueue_scripts__public');
add_action('login_enqueue_scripts', 'spbc_enqueue_scripts__public');
}
/**
* Enqueue JS scripts on public page
*/
function spbc_enqueue_scripts__public()
{
global $spbc;
if (spbc_is_amp_request()) {
return;
}
if ( ! $spbc->public_scripts_attached && $spbc->settings['data__set_cookies'] ) {
wp_enqueue_script('spbc_cookie', SPBC_PATH . '/js/spbc-cookie.min.js', array( 'jquery' ), SPBC_VERSION, false /*in header*/);
wp_localize_script(
'spbc_cookie',
'spbcPublic',
array(
'_ajax_nonce' => wp_create_nonce('ct_secret_stuff'),
'_rest_nonce' => wp_create_nonce('wp_rest'),
'_ajax_url' => admin_url('admin-ajax.php', 'relative'),
'_rest_url' => esc_url(get_rest_url()),
// '_apbct_ajax_url' => APBCT_URL_PATH . '/lib/Cleantalk/ApbctWP/Ajax.php',
'data__set_cookies' => $spbc->settings['data__set_cookies'],
'data__set_cookies__alt_sessions_type' => $spbc->settings['data__set_cookies__alt_sessions_type'],
)
);
$spbc->public_scripts_attached = true;
}
}
function spbc_set_headers()
{
global $spbc;
if ( ! headers_sent() ) {
// Additional headers
if ( $spbc->settings['data__additional_headers'] ) {
header('X-XSS-Protection: 1; mode=block');
header('X-Content-Type-Options: nosniff');
header('Strict-Transport-Security: max-age=31536000; includeSubDomains');
header('Referrer-Policy: strict-origin-when-cross-origin');
}
// Forbid to show in iframes
if ( $spbc->settings['misc__forbid_to_show_in_iframes'] ) {
header('X-Frame-Options: sameorigin', false);
}
// Set cookie to detect any logged in user
if ( spbc_is_user_logged_in() && ! empty($spbc->settings['data__set_cookies']) && ! Cookie::get('spbc_is_logged_in') ) {
Cookie::set('spbc_is_logged_in', md5($spbc->data['salt'] . parse_url(get_option('home'), PHP_URL_HOST)), time() + 86400 * 365, '/');
}
}
}
function spbc_update_actions()
{
global $spbc;
//Update logic
$current_version = $spbc->data['plugin_version'];
if ( $current_version != SPBC_VERSION ) {
// Perform a transaction and exit transaction ID isn't match
if ( ! Transaction::get('updater', 5)->perform() ) {
return;
}
Updater::runUpdateScripts($current_version, SPBC_VERSION);
$spbc->data['plugin_version'] = SPBC_VERSION;
$spbc->save('data');
Transaction::get('updater')->clearTransactionTimer();
}
}
/**
* Wrapper to call UploadChecker logic.
* @return void
*/
function spbc_upload_checker__check()
{
global $spbc;
if ( $spbc->settings['upload_checker__file_check'] && !empty($_FILES) ) {
$upload_checker = new Firewall\UploadChecker(array(
'upload_checker__do_check_wordpress_modules' => $spbc->settings['upload_checker__do_check_wordpress_modules'],
'api_key' => $spbc->api_key,
));
$firewall = new Firewall();
$firewall->loadFwModule($upload_checker);
$firewall->run();
}
}
function spbc_firewall__check()
{
global $spbc, $apbct;
// Skip the check
// Set skip test cookie
if ( ! empty($_GET['access']) ) {
$apbct_settings = get_option('cleantalk_settings');
$apbct_key = ! empty($apbct_settings['apikey']) ? $apbct_settings['apikey'] : false;
if ( ( $_GET['access'] === $spbc->settings['spbc_key'] || ( $apbct_key !== false && $_GET['access'] === $apbct_key ) ) ) {
Cookie::set('spbc_firewall_pass_key', md5($_SERVER['REMOTE_ADDR'] . $spbc->settings['spbc_key']), time() + 1200, '/');
Cookie::set('ct_sfw_pass_key', md5($_SERVER['REMOTE_ADDR'] . $apbct_key), time() + 1200, '/');
return;
}
}
// Turn off the SpamFireWall if Remote Call is in progress
if ( ( ! empty($apbct) && $apbct->rc_running ) || $spbc->rc_running ) {
return;
}
$firewall = new Firewall();
$secfw_enabled_on_main_site = false;
if (!is_main_site() && $spbc->network_settings['ms__work_mode'] == 2) {
$spbc_settings_main_site = get_blog_option(1, 'spbc_settings');
if ($spbc_settings_main_site['secfw__enabled']) {
$secfw_enabled_on_main_site = true;
}
}
if ( (int) $spbc->settings['secfw__enabled'] || $secfw_enabled_on_main_site ) {
$firewall->loadFwModule(
new FW(
array(
'data_table__personal_countries' => SPBC_TBL_FIREWALL_DATA__COUNTRIES,
'log_table' => SPBC_TBL_FIREWALL_LOG,
'state' => $spbc,
'api_key' => $spbc->api_key,
)
)
);
}
if ( $spbc->settings['traffic_control__enabled'] && ! is_admin() ) {
$firewall->loadFwModule(
new TC(
array(
'data_table' => SPBC_TBL_FIREWALL_DATA,
'log_table' => SPBC_TBL_TC_LOG,
'state' => $spbc,
'api_key' => $spbc->api_key,
'is_logged_in' => Cookie::get('spbc_is_logged_in') === md5($spbc->data['salt'] . parse_url(get_option('home'), PHP_URL_HOST)),
'user_is_admin' => spbc_user_is_admin(),
'store_interval' => $spbc->settings['traffic_control__autoblock_timeframe'],
'tc_limit' => $spbc->settings['traffic_control__autoblock_amount'],
'block_period' => $spbc->settings['traffic_control__autoblock_period'],
)
)
);
}
if ( $spbc->settings['waf__enabled'] ) {
$waf_params = [
'api_key' => $spbc->api_key,
'log_table' => SPBC_TBL_TC_LOG,
'state' => $spbc,
'waf__xss_check' => $spbc->settings['waf__xss_check'],
'waf__sql_check' => $spbc->settings['waf__sql_check'],
'waf__exploit_check' => $spbc->settings['waf__exploit_check']
];
if ( $spbc->settings['waf_blocker__enabled'] ) {
$waf_blocker_params = [
'is_logged_in' => Cookie::get('spbc_is_logged_in') === md5($spbc->data['salt'] . parse_url(get_option('home'), PHP_URL_HOST)),
'db' => DB::getInstance(),
'ip_array' => $firewall->ip_array
];
$waf_blocker = new WafBlocker($waf_blocker_params);
$waf_params['waf_blocker'] = $waf_blocker;
$firewall->loadFwModule($waf_blocker);
}
$firewall->loadFwModule(new WAF($waf_params));
}
//todo This rewrite could break permalinks, need to implement new logic
if ( class_exists('Poppyz_Core') ) { //fix poppyz plugin early start conflict
$GLOBALS['wp_rewrite'] = new WP_Rewrite(); // Fix for early load WP_Rewrite
}
$login_url = wp_login_url();
if ( $spbc->settings['login_page_rename__enabled'] ) {
//todo This rewrite could break permalinks, need to implement new logic
$GLOBALS['wp_rewrite'] = new WP_Rewrite(); // Fix for early load WP_Rewrite
$login_url = RenameLoginPage::getURL($spbc->settings['login_page_rename__name']);
}
$firewall->loadFwModule(
new BFP(
array(
'api_key' => $spbc->api_key,
'state' => $spbc,
'is_login_page' => strpos(trim(Server::getURL(), '/'), trim($login_url, '/')) === 0,
'is_logged_in' => Cookie::get('spbc_is_logged_in') === md5($spbc->data['salt'] . parse_url(get_option('home'), PHP_URL_HOST)),
'bf_limit' => $spbc->settings['bfp__allowed_wrong_auths'],
'block_period' => $spbc->settings['bfp__block_period__5_fails'],
'count_period' => $spbc->settings['bfp__count_interval'], // Counting login attempts in this interval
)
)
);
//Pass the check if cookie is set.
foreach ( $firewall->ip_array as $spbc_cur_ip ) {
if ( Cookie::get('spbc_firewall_pass_key') == md5($spbc_cur_ip . $spbc->settings['spbc_key']) ) {
return;
}
}
$firewall->run();
}
/**
* Plugin activation
*
* @param $network
* @param $redirect
*
* @return void
* @throws Exception
*/
function spbc_activation($network, $redirect = true)
{
Activator::activation($network, $redirect);
}
/**
* A code during plugin deactivation.
*
* @param $network
*
* @return void
*/
function spbc_deactivation($network)
{
\CleantalkSP\SpbctWP\Deactivator::deactivation($network);
}
/**
* Run deactivation process (complete deactivation forced) for hook register_uninstall_hook.
* @param bool $network Is network wide command.
* @return void
*/
function spbc_uninstall($network)
{
global $spbc;
$spbc->settings['misc__complete_deactivation'] = 1;
$spbc->save('settings');
\CleantalkSP\SpbctWP\Deactivator::deactivation($network);
}
/**
* @deprecated 2.125 use Deactivator::deleteBlogTables()
* @return void
*/
function spbc_deactivation__delete_blog_tables() //deprecated
{
\CleantalkSP\SpbctWP\Deactivator::deleteBlogTables();
}
/**
* @deprecated 2.125 use Deactivator::deleteCommonTables()
* @return void
*/
function spbc_deactivation__delete_common_tables() //deprecated
{
\CleantalkSP\SpbctWP\Deactivator::deleteCommonTables();
}
// Misc functions to test the plugin.
function spbc_plugin_loaded()
{
global $spbc;
if ( is_admin() || is_network_admin() ) {
$dir = plugin_basename(dirname(__FILE__)) . '/i18n';
load_plugin_textdomain('security-malware-firewall', false, $dir);
}
if ( $spbc->settings['spbc_trusted_and_affiliate__shortcode'] === '1' ) {
add_action('wp_enqueue_scripts', 'spbc_attach_public_css');
add_shortcode('cleantalk_security_affiliate_link', 'spbc_trusted_text_shortcode_handler');
}
}
/**
* Check brute force attack
*
* @return void
*/
function spbc_authenticate__check_brute_force()
{
global $spbc;
$login_url = wp_login_url();
if ($spbc->settings['login_page_rename__enabled']) {
$GLOBALS['wp_rewrite'] = new WP_Rewrite();
$login_url = RenameLoginPage::getURL($spbc->settings['login_page_rename__name']);
}
$bfp = new BFP(
array(
'api_key' => $spbc->api_key,
'state' => $spbc,
'is_login_page' => strpos(trim(Server::getURL(), '/'), trim($login_url, '/')) === 0,
'is_logged_in' => Cookie::get('spbc_is_logged_in') === md5($spbc->data['salt'] . parse_url(get_option('home'), PHP_URL_HOST)),
'bf_limit' => $spbc->settings['bfp__allowed_wrong_auths'],
'block_period' => $spbc->settings['bfp__block_period__5_fails'],
'count_period' => $spbc->settings['bfp__count_interval'],
)
);
$bfp->setDb(new DB());
$bfp->setIpArray([IP::get()]);
$bfp_result = $bfp->check();
$bfp->middleAction();
if (!empty($bfp_result)) {
$bfp->_die($bfp_result[0]);
}
}
//
// Sorts some data.
//
function spbc_usort_desc($a, $b)
{
return $b->datetime_ts - $a->datetime_ts;
}
/**
* Function to get the countries by IPs list.
*
* @param $ips_data
*
* @return array
*/
function spbc_get_countries_by_ips($ips_data = '')
{
$ips_c = array();
if ( $ips_data === '' ) {
return $ips_c;
}
$result = SpbcAPI::method__ip_info($ips_data);
if ( empty($result['error']) ) {
foreach ( $result as $ip_dec => $v2 ) {
if ( isset($v2['country_code']) ) {
$ips_c[ $ip_dec ]['country_code'] = $v2['country_code'];
}
if ( isset($v2['country_name']) ) {
$ips_c[ $ip_dec ]['country_name'] = $v2['country_name'];
}
}
}
return $ips_c;
}
/**
* Gets and write new signatures in local database
*
* @return bool|array
* @global State $spbc
* @global WPDB $wpdb
*/
function spbc_scanner__signatures_update()
{
global $spbc;
$latest_signature_submitted_time = SignatureAnalysisFacade::getLatestSignatureSubmittedTime();
$signatures_from_cloud = SignatureAnalysisFacade::getSignaturesFromCloud($latest_signature_submitted_time);
// Signatures updated
if (isset($signatures_from_cloud['error']) && $signatures_from_cloud['error'] === 'UP_TO_DATE') {
return array('success' => 'UP_TO_DATE');
}
// There is errors
if (isset($signatures_from_cloud['error'])) {
return $signatures_from_cloud;
}
$signatures = $signatures_from_cloud['values'];
$map = $signatures_from_cloud['map'];
SignatureAnalysisFacade::clearSignaturesTable();
$signatures_added = SignatureAnalysisFacade::addSignaturesToDb($map, $signatures);
if (!$signatures_added) {
// Attempt to record one at a time
$signatures_added = SignatureAnalysisFacade::addSignaturesToDbOneByOne($map, $signatures);
if (isset($signatures_added['bad_signatures'])) {
$spbc->error_add('scanner_update_signatures_bad_signatures', $signatures_added['bad_signatures']);
} else {
/**
* @psalm-suppress InvalidScalarArgument
*/
$spbc->error_delete('scanner_update_signatures_bad_signatures', 'save');
}
}
$spbc->data['scanner']['last_signature_update'] = current_time('timestamp');
$spbc->data['scanner']['signature_count'] = count($signatures);
$spbc->save('data');
return true;
}
/**
* Sending Security FireWall logs
*
* @param $api_key
*
* @return array|int
*/
function spbc_send_firewall_logs($api_key = false)
{
global $spbc;
$api_key = ! empty($api_key) ? $api_key : $spbc->api_key;
if ( ! empty($api_key) ) {
$result = FW::sendLog(
DB::getInstance(),
SPBC_TBL_FIREWALL_LOG,
$api_key
);
if ( empty($result['error']) ) {
$spbc->fw_stats['last_send'] = current_time('timestamp');
$spbc->fw_stats['last_send_count'] = $result;
$spbc->save('fw_stats', true, false);
return $result;
}
return $result;
}
return array(
'error' => 'KEY_EMPTY'
);
}
/**
* Drop Security FireWall data
*
* @return bool|string[]
*/
function spbc_security_firewall_drop()
{
global $wpdb;
$result = $wpdb->query('DELETE FROM `' . SPBC_TBL_FIREWALL_DATA . '`;');
if ( $result !== false ) {
return true;
}
return array( 'error' => 'DELETE_ERROR' );
}
/**
* Handle firewall private_records remote call.
* @param $action string 'add','delete'
* @param $test_data string JSON string used in test cases
* @return string JSON string of results
* @throws Exception
*/
function spbct_sfw_private_records_handler($action, $test_data = null)
{
$error = 'secfw_private_records_handler: ';
if ( !empty($action) && (in_array($action, array('add', 'delete'))) ) {
$metadata = !empty($test_data) ? $test_data : Post::get('metadata');
/**
* Validate JSON
*/
if ( !empty($metadata) ) {
$metadata = json_decode(stripslashes($metadata), true);
if ( $metadata === 'NULL' || $metadata === null ) {
throw new InvalidArgumentException($error . 'metadata JSON decoding failed');
}
} else {
throw new InvalidArgumentException($error . 'metadata is empty');
}
foreach ( $metadata as $_key => &$row ) {
$row = explode(',', $row);
/**
* Validation of JSON decoded array data
*/
$ip_validated = false;
$validation_error = '';
//validate IP
if ( IP::validate($row[0]) === 'v6' ) {
$ip_validated = $row[0];
} elseif (
IP::validate(long2ip((int)$row[0])) === 'v4'
&& (int)($row[0]) === ip2long(long2ip((int)$row[0]))
) {
$ip_validated = (int)$row[0];
} else {
$validation_error = 'network value does not look like IP address ';
}
//do this to get info more obvious
$metadata_assoc_array = array(
'network' => $ip_validated ?: null,
'mask' => (int)$row[1],
'status' => isset($row[2]) && $row[2] !== '' ? (int)$row[2] : null,
);
//validate mask and status
if ( $metadata_assoc_array['mask'] === 0
|| $metadata_assoc_array['mask'] > 4294967295
) {
$validation_error = 'metadata validate failed on "mask" value';
}
//only for adding
if ( $action === 'add' ) {
if ( !in_array($metadata_assoc_array['status'], array(-4, -3, -2, -1, 0, 1, 2, 99)) ) {
$validation_error = 'metadata validate failed on "status" value';
}
}
if ( !empty($validation_error) ) {
throw new InvalidArgumentException($error . $validation_error);
}
/**
* Ip version logic
*/
if ( is_string($metadata_assoc_array['network']) ) {
$metadata_assoc_array['network'] = IP::convertIPv6ToFourIPv4(IP::extendIPv6(IP::normalizeIPv6($metadata_assoc_array['network'])));
if ($metadata_assoc_array['mask'] > 128) {
$validation_error = 'metadata validate failed on "mask" value';
break;
}
/**
* Versatility for mask for v6 and v4
* @psalm-suppress LoopInvalidation
*/
for ( $masks = array(), $mask = $metadata_assoc_array['mask'], $k = 4; $k >= 1; $k-- ) {
$masks[$k] = (2 ** 32) - (2 ** (32 - ($mask > 32 ? 32 : $mask)));
$mask -= 32;
$mask = $mask > 0 ? $mask : 0;
}
$metadata_assoc_array['mask'] = $masks;
}
//all checks done, change on link
$row = $metadata_assoc_array;
}
unset($row);
if ( !empty($validation_error) ) {
throw new InvalidArgumentException($error . $validation_error);
}
//method selection
if ( $action === 'add' ) {
$handler_output = FW::privateRecordsAdd(
DB::getInstance(),
$metadata
);
} elseif ( $action === 'delete' ) {
$handler_output = FW::privateRecordsDelete(
DB::getInstance(),
$metadata
);
} else {
$error .= 'unknown action name: ' . $action;
throw new InvalidArgumentException($error);
}
} else {
throw new InvalidArgumentException($error . 'empty action name');
}
return json_encode(array('OK' => $handler_output));
}
function spbc_update_postmeta_links($post_ID)
{
delete_post_meta($post_ID, '_spbc_links_checked');
delete_post_meta($post_ID, 'spbc_links_checked');
}
function spbc_update_postmeta_links__by_comment($id)
{
$comment = get_comment($id);
spbc_update_postmeta_links($comment->comment_post_ID);
}
// Install MU-plugin
function spbc_mu_plugin__install()
{
// If WPMU_PLUGIN_DIR is not exists -> create it
if ( ! is_dir(WPMU_PLUGIN_DIR) && ! mkdir(WPMU_PLUGIN_DIR) && ! is_dir(WPMU_PLUGIN_DIR) ) {
throw new \RuntimeException(sprintf('Directory "%s" was not created', WPMU_PLUGIN_DIR));
}
// Get data from info file and write it to new plugin file
$file = '<?php' . PHP_EOL . file_get_contents(SPBC_PLUGIN_DIR . '/install/security-malware-firewall-mu.php');
return @file_put_contents(WPMU_PLUGIN_DIR . '/0security-malware-firewall-mu.php', $file) ? true : false;
}
/**
* Uninstall MU-plugin
* @deprecated 2.125 Use Deactivator::muPluginUninstall
* @return bool
*/
function spbc_mu_plugin__uninstall()
{
return \CleantalkSP\SpbctWP\Deactivator::muPluginUninstall();
}
function spbc_user_is_admin()
{
global $spbc;
if (!empty($spbc->settings['data__set_cookies'])) {
return
Cookie::get('spbc_is_logged_in') === md5($spbc->data['salt'] . parse_url(get_option('home'), PHP_URL_HOST)) &&
Cookie::get('spbc_admin_logged_in') === md5($spbc->data['salt'] . 'admin' . parse_url(get_option('home'), PHP_URL_HOST));
}
return is_admin();
}
//Function to send logs
function spbc_send_logs($api_key = null)
{
global $spbc, $wpdb;
if ( $api_key == null ) {
if ( ! $spbc->is_mainsite && $spbc->ms__work_mode == 2 ) {
$api_key = $spbc->network_settings['spbc_key'];
} else {
$api_key = $spbc->settings['spbc_key'];
}
}
$wpms_snippet = SPBC_WPMS
? (" WHERE blog_id = " . get_current_blog_id() . ' AND ')
: " WHERE ";
$rows = $wpdb->get_results(
"SELECT id, datetime, timestamp_gmt, user_login, page, page_time, event, auth_ip, role, user_agent, browser_sign
FROM " . SPBC_TBL_SECURITY_LOG
. $wpms_snippet
. " sent <> 1"
. " ORDER BY datetime DESC"
. " LIMIT " . SPBC_SELECT_LIMIT . ";"
);
$rows_count = count($rows);
if ( $rows_count ) {
$data = array();
foreach ( $rows as $record ) {
$page_time = (string) $record->page_time;
if ((int)$page_time <= 0) {
$page_time = '1';
}
$data[] = array(
'log_id' => (string) $record->id,
'datetime' => (string) $record->datetime,
'datetime_gmt' => $record->timestamp_gmt,
'user_log' => (string) $record->user_login,
'event' => (string) $record->event,
'auth_ip' => strpos($record->auth_ip, ':') === false
? (int) sprintf('%u', ip2long($record->auth_ip))
: (string) $record->auth_ip,
'page_url' => (string) $record->page,
'event_runtime' => $page_time,
'role' => (string) $record->role,
);
// Adding user agent and browser sign if it's login event
if ( in_array(strval($record->event), array( 'login', 'login_2fa', 'login_new_device', 'logout', )) ) {
$data[] = array_merge(
array_pop($data),
array(
'user_agent' => $record->user_agent,
'browser_signature' => $record->browser_sign,
)
);
}
}
$result = SpbcAPI::method__security_logs($api_key, $data);
if ( empty($result['error']) ) {
//Clear local table if it's ok.
if ( $result['rows'] == $rows_count ) {
$updated_ids = array();
foreach ($data as $item) {
$updated_ids[] = $item['log_id'];
}
if ( SPBC_WPMS ) {
$wpdb->query(
"UPDATE " . SPBC_TBL_SECURITY_LOG
. " SET sent = 1
WHERE id IN ("
. implode(',', $updated_ids) .
")"
. ( $spbc->ms__work_mode == 2 ? '' : ' AND blog_id = ' . get_current_blog_id() )
. ";"
);
} else {
$wpdb->query(
"UPDATE " . SPBC_TBL_SECURITY_LOG
. " SET sent = 1
WHERE id IN ("
. implode(',', $updated_ids) .
");"
);
}
$result = $rows_count;
} else {
$result = array(
'error' => sprintf(__('Sent: %d. Confirmed receiving of %d rows.', 'security-malware-firewall'), $rows_count, intval($result['rows']))
);
}
}
} else {
$result = array(
'error' => 'NO_LOGS_TO_SEND'
);
}
global $spbc_cron;
if ( ! empty($spbc_cron) && empty($result['error']) ) {
$spbc->data['logs_last_sent'] = current_time('timestamp');
$spbc->data['last_sent_events_count'] = $result;
}
return $result;
}
/**
* @return bool
* @psalm-suppress RedundantCondition
*/
function spbc_set_api_key()
{
global $spbc;
$website = parse_url(get_option('home'), PHP_URL_HOST) . parse_url(get_option('home'), PHP_URL_PATH);
$platform = 'wordpress';
$user_ip = IP::get();
$timezone = spbc_wp_timezone_string();
$language = Server::get('HTTP_ACCEPT_LANGUAGE');
$is_wpms = is_multisite() && defined('SUBDOMAIN_INSTALL') && ! SUBDOMAIN_INSTALL;
$white_label = false;
$hoster_api_key = $spbc->ms__hoster_api_key;
$result = SpbcAPI::method__get_api_key(
'security',
get_network_option(0, 'admin_email'),
$website,
$platform,
$timezone,
$language,
$user_ip,
$is_wpms,
$white_label,
$hoster_api_key
);
if ( ! empty($result['error']) ) {
$spbc->data['key_is_ok'] = false;
$spbc->error_add('get_key', $result);
return false;
} else {
$api_key = trim($result['auth_key']);
$api_key = preg_match('/^[a-z\d]*$/', $api_key) ? $api_key : $spbc->settings['spbc_key']; // Check key format a-z\d
$api_key = is_main_site() || $spbc->ms__work_mode != 2 ? $api_key : $spbc->network_settings['spbc_key'];
$spbc->settings['spbc_key'] = $api_key;
$spbc->save('settings');
$spbc->data['user_token'] = ( ! empty($result['user_token']) ? $result['user_token'] : '' );
$spbc->data['key_is_ok'] = spbc_api_key__is_correct($api_key);
$spbc->data['key_changed'] = true;
$spbc->save('data');
$spbc->error_delete('get_key api_key');
return true;
}
}
/**
* The functions check to check an account
* Executes only via cron (on the main blog)
*
* @param null $spbc_key
*
* @return array|bool|bool[]|mixed|string[]
*/
function spbc_access_key_notices($spbc_key = null)
{
global $spbc;
$spbc_key = $spbc_key ?: $spbc->settings['spbc_key'];
if ( empty($spbc_key) ) {
if ( ! $spbc->is_mainsite && $spbc->ms__work_mode != 2 ) {
$spbc_key = ! empty($spbc->network_settings['spbc_key']) ? $spbc->network_settings['spbc_key'] : false;
if ( ! $spbc_key ) {
return array( 'error' => 'KEY_IS_NOT_OK_ON_MAIN_WPMS_SITE' );
}
} else {
$spbc_key = ! empty($spbc->settings['spbc_key']) ? $spbc->settings['spbc_key'] : false;
if ( ! $spbc_key ) {
return array( 'error' => 'KEY_IS_NOT_OK' );
}
}
}
try {
spbc_check_account_status($spbc_key);
} catch (\Exception $exception) {
return array( 'error' => $exception->getMessage() );
}
return true;
}
function spbc_PHP_logs__collect($last_log_sent)
{
$logs = array();
$start_timestamp = time();
// Try to get log from wp-content/debug/log if default file is not accessible
$file = ini_get('error_log');
$file = file_exists($file) && is_readable($file)
? $file
: WP_CONTENT_DIR . '/debug.log';
if ( file_exists($file) ) {
if ( is_readable($file) ) {
// Return if file is empty
if ( ! filesize($file) ) {
return array();
}
$fd = @fopen($file, 'rb');
if ( $fd ) {
$eol = spbc_PHP_logs__detect_EOL_type($file, false);
if (is_null($eol) || is_int($eol)) {
$eol = "\n";
}
for (
// Initialization
$fsize = filesize($file), $offset = 1024 * 5, $position = $fsize - $offset,
$max_log_size = 1024 * 1024 * 1, $max_read_size = 1024 * 1024 * 4,
$log_size = 0, $read = 0,
$log_count = 0;
// Conditions
$log_size < $max_log_size && // Max usefull data
$offset === 1024 * 5 && // End of file
$read < $max_read_size && // Max read depth
$log_count < 3500 &&
time() < $start_timestamp + 25;
// Iteartion adjustments
$position -= $offset
) {
$offset = $position < 0 ? $offset + $position : $offset;
$position = $position < 0 ? 0 : $position;
// Set pointer to $it * $offset from the EOF. Or 0 if it's negative.
fseek($fd, $position);
// Read $offset bytes
$it_logs = fread($fd, $offset);
// Clean to first EOL, splitting to array by PHP_EOL.
if ( $position != 0 ) {
$position_adjustment = strpos($it_logs, $eol);
$position += $position_adjustment + 1;
$it_logs = substr($it_logs, $position_adjustment);
}
$read += strlen($it_logs);
$it_logs = explode($eol, $it_logs);
// Filtering and parsing
foreach ( $it_logs as $log_line ) {
if ( spbc_PHP_logs__filter($log_line, $last_log_sent) ) {
$log_size += strlen($log_line);
$log_count++;
$parsed_log_line = spbc_PHP_logs__parse_line($log_line);
if ( $parsed_log_line ) {
$logs[] = $parsed_log_line;
}
}
}
}
return $logs;
} else {
return array( 'error' => 'COULDNT_OPEN_LOG_FILE' );
}
} else {
return array( 'error' => 'LOG_FILE_IS_UNACCESSIBLE' );
}
} else {
return array( 'error' => 'LOG_FILE_NOT_EXISTS' );
}
}
function spbc_PHP_logs__filter($line, $php_logs_last_sent)
{
$line = trim($line);
if ( ! empty($line) ) {
preg_match('/^\[(.*?\s\d\d:\d\d:\d\d.*?)]/', $line, $matches);
if ( isset($matches[1]) && strtotime($matches[1]) >= $php_logs_last_sent ) {
if ( preg_match('/^\[(.*?)\]\s+PHP\s(Warning|Fatal|Notice|Parse)/', $line) ) {
} else {
$line = false;
}
} else {
$line = false;
}
} else {
$line = false;
}
return $line;
}
function spbc_PHP_logs__parse_line($line)
{
if ( preg_match('/^\[(.*?)\]\s((.*?):\s+(.+))$/', $line, $matches) ) {
return array(
date('Y-m-d H:i:s', strtotime($matches[1])),
$matches[2],
);
}
}
function spbc_PHP_logs__send()
{
global $spbc;
if ( empty($spbc->settings['misc__backend_logs_enable']) || empty($spbc->settings['spbc_key']) ) {
return true;
}
$logs = spbc_PHP_logs__collect($spbc->data['last_php_log_sent']);
if ( empty($logs['error']) ) {
if ( ! empty($logs) ) {
$result = SpbcAPI::method__security_backend_logs($spbc->settings['spbc_key'], $logs);
if ( empty($result['error']) ) {
if ( isset($result['total_logs_found']) ) {
if ( $result['total_logs_found'] == count($logs) ) {
$spbc->data['last_php_log_sent'] = time();
$spbc->data['last_php_log_amount'] = $result['total_logs_found'];
$spbc->save('data');
return true;
} else {
return array( 'error' => 'LOGS_COUNT_DOES_NOT_MATCH' );
}
} else {
return array( 'error' => 'LOGS_COUNT_IS_EMPTY' );
}
} else {
return $result;
}
} else {
return true;
}
} else {
return $logs;
}
}
function spbc_check_ajax_referer($action = - 1, $query_arg = false, $die = true)
{
$res = true;
if ( function_exists('check_ajax_referer') ) {
/** @psalm-suppress ForbiddenCode */
$res = check_ajax_referer($action, $query_arg, $die);
if ( $res && ! current_user_can('manage_options') ) {
if ( $die ) {
wp_die('-1', 403);
}
$res = false;
}
}
return $res;
}
/**
* Check connection to the API servers
*
* @param array $urls_to_test
*
* @return array
*/
function spbc_test_connection($urls_to_test = array())
{
$out = array();
$urls_to_test = $urls_to_test ?: array_keys(\CleantalkSP\SpbctWP\Helpers\IP::$cleantalks_servers);
foreach ( $urls_to_test as $url ) {
$start = microtime(true);
$result = HTTP::getContentFromURL($url);
$out[ $url ] = array(
'result' => ! empty($result['error']) ? $result['error'] : 'OK',
'exec_time' => microtime(true) - $start,
);
}
return $out;
}
function spbc_sync($direct_call = false)
{
if ( ! $direct_call ) {
spbc_check_ajax_referer('spbc_secret_nonce', 'security');
}
global $spbc;
//Clearing all errors
$spbc->error_delete_all('and_save_data');
// If key provided by super admin
if ( $spbc->is_mainsite || $spbc->ms__work_mode != 2 ) {
// Checking account status
try {
spbc_check_account_status($spbc->api_key);
} catch ( Exception $exception ) {
$error_text = $exception->getMessage() === 'KEY_IS_NOT_VALID'
? sprintf(__('Access key is not valid. Key: %s.', 'security-malware-firewall'), $spbc->settings['spbc_key'])
: $exception->getMessage();
$spbc->error_add('apikey', $error_text);
}
}
// Sending logs.
$result = spbc_send_logs($spbc->api_key);
if ( empty($result['error']) ) {
$spbc->data['logs_last_sent'] = current_time('timestamp');
$spbc->data['last_sent_events_count'] = $result;
$spbc->error_delete('send_logs');
} else {
$spbc->error_add('send_logs', $result);
}
// If key provided by super admin
if ( is_main_site() ) {
// Updating signtaures
$result = spbc_scanner__signatures_update();
empty($result['error'])
? $spbc->error_delete('scanner_update_signatures', 'save')
: $spbc->error_add('scanner_update_signatures', $result);
}
$out = array(
'success' => true,
'reload' => $spbc->data['key_changed'],
);
// Sending FW logs
$result = spbc_send_firewall_logs($spbc->api_key);
if ( empty($result['error']) ) {
$spbc->fw_stats['last_send'] = current_time('timestamp');
$spbc->fw_stats['last_send_count'] = $result;
$spbc->error_delete('send_firewall_logs');
} else {
$spbc->error_add('send_firewall_logs', $result);
}
$spbc->data['key_changed'] = false;
$spbc->save('data');
$spbc->save('fw_stats', true, false);
// Do async actions after all so data can't be overwrite by sync actions
// Updating FW
//Reset last call of update_sec_fw
$spbc->remote_calls['update_security_firewall']['last_call'] = 0;
$spbc->save('remote_calls', true, false);
$result = spbc_security_firewall_update__init();
if ( ! empty($result['error']) ) {
$spbc->error_add('firewall_update', $result['error']);
}
// Get custom message for security firewall
$result_service_get = spbct_perform_service_get();
if ( ! empty($result_service_get['error']) ) {
if ($result_service_get['error_no'] !== 403) {
$spbc->error_add('service_customize', $result_service_get['error']);
}
}
if ( $direct_call ) {
return $out;
}
wp_send_json($out);
}
function spbct_perform_service_get()
{
global $spbc;
$result_service_get = SpbcAPI::method__service_get(
$spbc->api_key,
$spbc->data['user_token']
);
if ( empty($result_service_get['error']) ) {
$spbc->settings['fw__custom_message'] = isset($result_service_get['server_response'])
? $result_service_get['server_response']
: '';
$spbc->save('settings');
}
return $result_service_get;
}
// The functions sends daily reports about attempts to login.
function spbc_send_daily_report($skip_data_rotation = false)
{
if ( ! function_exists('wp_mail') ) {
add_action('plugins_loaded', 'spbc_send_daily_report');
return;
}
global $spbc, $wpdb, $spbc_tpl;
//If key is not ok, send daily report!
if ( ! $spbc->key_is_ok ) {
include_once SPBC_PLUGIN_DIR . 'templates/spbc_send_daily_report.php';
// Hours
$report_interval = 24 * 7;
$admin_email = spbc_get_admin_email();
if ( ! $admin_email ) {
error_log(
sprintf(
'%s: can\'t send the Daily report because of empty Admin email. File: %s, line %d.',
$spbc->data["wl_brandname"],
__FILE__,
__LINE__
)
);
return false;
}
$sql = sprintf(
'SELECT id,datetime,user_login,event,auth_ip,page,page_time
FROM %s WHERE datetime between now() - interval %d hour and now();',
SPBC_TBL_SECURITY_LOG,
$report_interval
);
$rows = $wpdb->get_results($sql);
foreach ( $rows as $k => $v ) {
if ( isset($v->datetime) ) {
$v->datetime_ts = strtotime($v->datetime);
}
$rows[$k] = $v;
}
usort($rows, "spbc_usort_desc");
$record_datetime = time();
$events = array();
$auth_failed_events = array();
$invalid_username_events = array();
$auth_failed_count = 0;
$invalid_username_count = 0;
$ips_data = '';
foreach ( $rows as $record ) {
if ( strtotime($record->datetime) > $record_datetime ) {
$record_datetime = strtotime($record->datetime);
}
$events[ $record->event ][ $record->user_login ][] = array(
'datetime' => $record->datetime,
'auth_ip' => $record->auth_ip,
'user_login' => $record->user_login,
'page' => $record->page ?: '-',
'page_time' => $record->page_time ?: 'Unknown'
);
switch ( $record->event ) {
case 'auth_failed':
$auth_failed_events[ $record->user_login ][ $record->auth_ip ] = array(
'attempts' => isset($auth_failed_events[ $record->user_login ][ $record->auth_ip ]['attempts'])
? $auth_failed_events[ $record->user_login ][ $record->auth_ip ]['attempts'] + 1
: 1,
'auth_ip' => $record->auth_ip,
'user_login' => $record->user_login
);
$auth_failed_count++;
break;
case 'invalid_username':
$invalid_username_events[ $record->user_login ][ $record->auth_ip ] = array(
'attempts' => isset($invalid_username_events[ $record->user_login ][ $record->auth_ip ]['attempts'])
? $invalid_username_events[ $record->user_login ][ $record->auth_ip ]['attempts'] + 1
: 1,
'auth_ip' => $record->auth_ip,
'user_login' => $record->user_login
);
$invalid_username_count++;
break;
}
if ( $ips_data != '' ) {
$ips_data .= ',';
}
$ips_data .= $record->auth_ip;
}
$ips_c = spbc_get_countries_by_ips($ips_data);
$event_part = '';
$auth_failed_part = sprintf(
"<p style=\"color: #666;\">%s</p>",
_("0 brute force attacks have been made for past day.")
);
if ( $auth_failed_count ) {
foreach ( $auth_failed_events as $e ) {
$ip_part = '';
foreach ( $e as $ip ) {
$country_part = spbc_report_country_part($ips_c, $ip['auth_ip']);
$ip_part .= sprintf(
"<a href=\"https://cleantalk.org/blacklists/%s\">%s</a>, #%d, %s<br />",
$ip['auth_ip'],
$ip['auth_ip'],
$ip['attempts'],
$country_part
);
}
$event_part .= sprintf($spbc_tpl['event_part_tpl'], $ip['user_login'], $ip_part);
}
$auth_failed_part = sprintf($spbc_tpl['auth_failed_part'], $event_part);
}
$invalid_username_part = sprintf(
"<p style=\"color: #666;\">%s</p>",
_('0 brute force attacks have been made for past day.')
);
if ( $invalid_username_count ) {
foreach ( $invalid_username_events as $e ) {
$ip_part = '';
foreach ( $e as $ip ) {
$country_part = spbc_report_country_part($ips_c, $ip['auth_ip']);
$ip_part .= sprintf(
"<a href=\"https://cleantalk.org/blacklists/%s\">%s</a>, #%d, %s<br />",
$ip['auth_ip'],
$ip['auth_ip'],
$ip['attempts'],
$country_part
);
}
$event_part .= sprintf(
$spbc_tpl['event_part_tpl'],
$ip['user_login'],
$ip_part
);
}
$invalid_username_part = sprintf($spbc_tpl['auth_failed_part'], $event_part);
}
$logins_part = sprintf(
"<p style=\"color: #666;\">%s</p>",
_('0 users have been logged in for past day.')
);
if ( isset($events['login']) && count($events['login']) ) {
$event_part = '';
foreach ( $events['login'] as $user_login => $e ) {
$l_part = '';
foreach ( $e as $e2 ) {
$country_part = spbc_report_country_part($ips_c, $e2['auth_ip']);
$l_part .= sprintf(
"%s, <a href=\"https://cleantalk.org/blacklists/%s\">%s</a>, %s<br />",
date("M d Y H:i:s", strtotime($e2['datetime'])),
$e2['auth_ip'],
$e2['auth_ip'],
$country_part
);
}
$event_part .= sprintf(
$spbc_tpl['event_part_tpl'],
$user_login,
$l_part
);
}
$logins_part = sprintf(
$spbc_tpl['logins_part_tpl'],
$event_part
);
}
$title_main_part = _('Daily security report');
$subject = sprintf(
'%s %s',
parse_url(get_option('home'), PHP_URL_HOST),
$title_main_part
);
$message_anounce = sprintf(
_('%s brute force attacks or failed logins, %d successful logins.'),
number_format($auth_failed_count + $invalid_username_count, 0, ',', ' '),
isset($events['login']) ? count($events['login']) : 0
);
$message = sprintf(
$spbc_tpl['message_tpl'],
$spbc_tpl['message_style'],
$title_main_part,
$message_anounce,
$auth_failed_part,
$invalid_username_part,
$logins_part,
$spbc->data["wl_brandname"]
);
$headers = array('Content-Type: text/html; charset=UTF-8');
wp_mail(
$admin_email,
$subject,
$message,
$headers
);
if ( ! $skip_data_rotation ) {
$sql = sprintf(
"delete from %s where datetime <= '%s';",
SPBC_TBL_SECURITY_LOG,
date("Y-m-d H:i:s", $record_datetime)
);
$wpdb->query($sql);
};
}
return null;
}
function spbc_private_list_add()
{
global $spbc, $current_user;
$ip = IP::get();
if ( Cookie::get('spbc_secfw_ip_wl') === md5($ip . $spbc->spbc_key) ) {
return;
}
if ( in_array('administrator', $current_user->roles) ) {
$res = spbc_private_list_add_api_call($ip);
if ( $res ) {
if ( ! headers_sent() ) {
$cookie_val = md5($ip . $spbc->spbc_key);
Cookie::set('spbc_secfw_ip_wl', $cookie_val, time() + 86400 * 25, '/', '', false, true);
}
// Add to the local database
$status_for_db = 1;
$version = IP::validate($ip);
if ( $version === 'v4' ) {
$data[] = ip2long($ip) . ',' . ip2long('255.255.255.255') . ',' . $status_for_db;
} elseif ( $version === 'v6' ) {
$data[] = $ip . ',' . '128' . ',' . $status_for_db;
} else {
wp_send_json_error('Local database: adding IP ' . $ip . ' failed: ip does not look like a valid IP address');
}
try {
$res_local = spbct_sfw_private_records_handler('add', json_encode($data, JSON_FORCE_OBJECT));
wp_send_json_success($res_local);
} catch (\Exception $e) {
wp_send_json_error('Local database: adding IP ' . $ip . ' failed: ' . $e->getMessage());
}
}
wp_send_json_error('API wrong answer.');
}
}
function spbc_private_list_add_api_call($ip)
{
global $spbc;
if ( IP::validate($ip) !== false ) {
$res = SpbcAPI::method__private_list_add__secfw_wl($spbc->user_token, $ip, $spbc->data['service_id']);
return isset($res['records'][0]['operation_status']) && $res['records'][0]['operation_status'] === 'SUCCESS';
}
return false;
}
/**
* Cron. Update statuses of files sent to the cloud sandbox.
*/
function spbc_scanner_update_pscan_files_status()
{
global $wpdb;
// Reading DB for NEW files
$undone_files_list = $wpdb->get_results(
'SELECT fast_hash'
. ' FROM ' . SPBC_TBL_SCAN_FILES
. ' WHERE pscan_processing_status <> "DONE" AND pscan_processing_status IS NOT NULL',
ARRAY_A
);
if ( !empty($undone_files_list) ) {
$files_fast_hashes_to_update = array();
foreach ( $undone_files_list as $file ) {
$files_fast_hashes_to_update[] = $file['fast_hash'];
}
spbc_scanner_pscan_check_analysis_status(true, $files_fast_hashes_to_update);
} else {
\CleantalkSP\SpbctWP\Cron::removeTask('scanner_update_pscan_files_status');
}
}
/**
* Cron. Resend files that were not added to the cloud sandbox queue.
*/
function spbc_scanner_resend_pscan_files($do_rescan = true)
{
global $wpdb;
// Reading DB for unqueued files
$unqueued_files_list = $wpdb->get_results(
'SELECT fast_hash, status'
. ' FROM ' . SPBC_TBL_SCAN_FILES
. ' WHERE pscan_pending_queue = 1',
ARRAY_A
);
if ( !empty($unqueued_files_list) ) {
foreach ( $unqueued_files_list as $file ) {
//fix for files sent to manual analysis
if ( !empty($file['status']) && $file['status'] === 'APPROVED_BY_CT') {
$update_sql =
'UPDATE ' . SPBC_TBL_SCAN_FILES
. ' SET '
. 'pscan_pending_queue = 0 '
. 'WHERE fast_hash = "' . $file['fast_hash'] . '"';
$wpdb->query($update_sql);
continue;
}
spbc_scanner_file_send(true, $file['fast_hash'], $do_rescan);
}
} else {
\CleantalkSP\SpbctWP\Cron::removeTask('scanner_resend_pscan_files');
}
}
function spbc_check_account_status($api_key)
{
global $spbc, $plugin_info;
// Checking account status
$result = SpbcAPI::method__notice_paid_till(
$api_key,
preg_replace('/http[s]?:\/\//', '', get_option('home'), 1), // Site URL
'security'
);
// API returns an error
if ( ! empty($result['error']) ) {
$spbc->data['key_is_ok'] = false;
$spbc->save('data');
throw new \RuntimeException($result['error']);
}
// Key is not valid
if ( ! $result['valid'] ) {
$spbc->data['key_is_ok'] = false;
$spbc->data['notice_trial'] = 0;
$spbc->save('data');
throw new \RuntimeException('KEY_IS_NOT_VALID');
}
$spbc->data['key_is_ok'] = true;
if ( isset($result['user_token']) ) {
$spbc->data['user_token'] = $result['user_token'];
}
$spbc->data['notice_show'] = isset($result['show_notice']) ? $result['show_notice'] : 0;
$spbc->data['notice_renew'] = isset($result['renew']) ? $result['renew'] : 0;
$spbc->data['notice_trial'] = isset($result['trial']) ? $result['trial'] : 0;
$spbc->data['notice_review'] = isset($result['show_review']) ? (int)$result['show_review'] : 0;
$spbc->data['notice_auto_update'] = isset($result['show_auto_update_notice']) ? $result['show_auto_update_notice'] : 0;
$spbc->data['service_id'] = isset($result['service_id']) ? $result['service_id'] : 0;
$spbc->data['user_id'] = isset($result['user_id']) ? $result['user_id'] : 0;
$spbc->data['moderate'] = isset($result['moderate']) ? $result['moderate'] : 0;
$spbc->data['auto_update_app '] = isset($result['auto_update_app']) ? $result['auto_update_app'] : 0;
$spbc->data['license_trial'] = isset($result['license_trial']) ? $result['license_trial'] : 0;
$spbc->data['account_name_ob'] = isset($result['account_name_ob']) ? $result['account_name_ob'] : '';
$spbc->data['extra_package']['backend_logs'] = isset($result['extra_package']) && is_array($result['extra_package']) && in_array('backend_logs', $result['extra_package'], true)
? 1
: 0;
//todo:temporary solution for description, until we found the way to transfer this from cloud
if (defined('SPBC_WHITELABEL_PLUGIN_DESCRIPTION')) {
$result['wl_plugin_description'] = SPBC_WHITELABEL_PLUGIN_DESCRIPTION;
}
//todo:temporary solution for FAQ
if (defined('SPBC_WHITELABEL_FAQ_LINK')) {
$result['wl_faq_url'] = SPBC_WHITELABEL_FAQ_LINK;
}
if ( $spbc->is_network && $spbc->is_mainsite && $spbc->ms__work_mode == 1 ) {
$spbc->data['services_count '] = isset($result['services_count']) ? $result['services_count'] : '';
$spbc->data['services_max'] = isset($result['services_max']) ? $result['services_max'] : '';
$spbc->data['services_utilization'] = isset($result['services_utilization']) ? $result['services_utilization'] : '';
}
if ( isset($result['wl_status']) && $result['wl_status'] === 'ON' ) {
$spbc->data['wl_mode_enabled'] = true;
$spbc->data['wl_brandname'] = isset($result['wl_brandname'])
? Sanitize::cleanTextField($result['wl_brandname'])
: $spbc->default_data['wl_brandname'];
$spbc->data['wl_url'] = isset($result['wl_url'])
? Sanitize::cleanUrl($result['wl_url'])
: $spbc->default_data['wl_url'];
if (isset($result['wl_faq_url'])) {
$spbc->data['wl_support_faq'] = Sanitize::cleanUrl($result['wl_faq_url']);
} elseif (isset($result['wl_support_url'])) {
$spbc->data['wl_support_faq'] = Sanitize::cleanUrl($result['wl_support_url']);
} else {
$spbc->data['wl_support_faq'] = $spbc->default_data['wl_support_url'];
}
$spbc->data['wl_support_url'] = isset($result['wl_support_url'])
? Sanitize::cleanUrl($result['wl_support_url'])
: $spbc->default_data['wl_support_url'];
$spbc->data['wl_support_email'] = isset($result['wl_support_email'])
? Sanitize::cleanEmail($result['wl_support_email'])
: $spbc->default_data['wl_support_email'];
$spbc->data['wl_plugin_description'] = isset($result['wl_plugin_description'])
? Sanitize::cleanTextField($result['wl_plugin_description'])
: $plugin_info['Description'];
} else {
$spbc->data['wl_mode_enabled'] = false;
$spbc->data['wl_brandname'] = $spbc->default_data['wl_brandname'];
$spbc->data['wl_url'] = $spbc->default_data['wl_url'];
$spbc->data['wl_support_faq'] = $spbc->default_data['wl_support_url'];
$spbc->data['wl_support_url'] = $spbc->default_data['wl_support_url'];
$spbc->data['wl_support_email'] = $spbc->default_data['wl_support_email'];
}
// Disable/enable the collecting backend PHP log depends on the extra package data
$spbc->settings['misc__backend_logs_enable'] = (int) $spbc->data['extra_package']['backend_logs'];
$spbc->save('settings');
$spbc->save('data');
if ( SPBC_WPMS ) {
$spbc->network_settings['moderate'] = $spbc->data['moderate'];
$spbc->network_settings['key_is_ok'] = $spbc->data['key_is_ok'];
$spbc->save('network_settings');
}
}
/**
* Clears the table with security logs. Leaves only 50 entries.
*/
function spbc_security_log_clear()
{
global $spbc, $wpdb;
$remain_ids = array();
// Getting ids of last 50 rows
try {
$ids = $wpdb->get_results(
"SELECT id
FROM " . SPBC_TBL_SECURITY_LOG
. " WHERE sent=1"
. ( SPBC_WPMS ? " AND blog_id = " . get_current_blog_id() : '' )
. " ORDER BY datetime DESC"
. " LIMIT 50;",
'ARRAY_N'
);
if ($ids) {
foreach ($ids as $id) {
$remain_ids[] = $id[0];
}
}
} catch (\Exception $e) {
return false;
}
if (empty($remain_ids)) {
return false;
}
if ( SPBC_WPMS ) {
$wpdb->query(
"DELETE FROM " . SPBC_TBL_SECURITY_LOG
. " WHERE sent = 1
AND id NOT IN ("
. implode(',', $remain_ids) .
")"
. ( $spbc->ms__work_mode == 2 ? '' : ' AND blog_id = ' . get_current_blog_id() )
. ";"
);
} else {
$wpdb->query(
"DELETE FROM " . SPBC_TBL_SECURITY_LOG
. " WHERE sent = 1
AND id NOT IN ("
. implode(',', $remain_ids) .
");"
);
}
return true;
}
/**
* Check whether the request is AMP
*
* @return bool
*/
function spbc_is_amp_request()
{
if (function_exists('amp_is_request')) {
return amp_is_request();
}
return false;
}
/**
* Parse CDN checker self-request to find CDN headers.
* @return array|null[]|string[]
*/
function spbc_cdn_checker__parse_request()
{
global $spbc;
if ($spbc->settings['secfw__get_ip__enable_cdn_auto_self_check']) {
return CDNHeadersChecker::check();
}
return array('error' => 'CDN checker disabled');
}
/**
* Send test request to host. Then it should be parsed to find CDN headers.
* @return void
* @psalm-suppress
*/
function spbc_cdn_checker__send_request()
{
global $spbc;
if ($spbc->settings['secfw__get_ip__enable_cdn_auto_self_check']) {
CDNHeadersChecker::sendCDNCheckerRequest();
}
}