query("ALTER TABLE `{$issuesTable}` ADD `lastUpdated` INT UNSIGNED NOT NULL AFTER `time`");
$wpdb->query("ALTER TABLE `{$issuesTable}` ADD INDEX (`lastUpdated`)");
$wpdb->query("ALTER TABLE `{$issuesTable}` ADD INDEX (`status`)");
$wpdb->query("ALTER TABLE `{$issuesTable}` ADD INDEX (`ignoreP`)");
$wpdb->query("ALTER TABLE `{$issuesTable}` ADD INDEX (`ignoreC`)");
$wpdb->query("UPDATE `{$issuesTable}` SET `lastUpdated` = `time` WHERE `lastUpdated` = 0");
$wpdb->query("ALTER TABLE `{$pendingIssuesTable}` ADD `lastUpdated` INT UNSIGNED NOT NULL AFTER `time`");
$wpdb->query("ALTER TABLE `{$pendingIssuesTable}` ADD INDEX (`lastUpdated`)");
$wpdb->query("ALTER TABLE `{$pendingIssuesTable}` ADD INDEX (`status`)");
$wpdb->query("ALTER TABLE `{$pendingIssuesTable}` ADD INDEX (`ignoreP`)");
$wpdb->query("ALTER TABLE `{$pendingIssuesTable}` ADD INDEX (`ignoreC`)");
}
//---- Scheduled scan start hour and manual type
if (wfConfig::get('schedStartHour') < 0) {
wfConfig::set('schedStartHour', wfWAFUtils::random_int(0, 23));
if (wfConfig::get('schedMode') == 'manual') {
$sched = wfConfig::get_ser('scanSched', array());
if (is_array($sched) && is_array($sched[0])) { //Try to determine the closest matching value for manualScanType
$hours = array_fill(0, 24, 0);
$distinctHours = array();
$days = array_fill(0, 7, 0);
$distinctDays = array();
foreach ($sched as $dayIndex => $day) {
foreach ($day as $h => $enabled) {
if ($enabled) {
if (in_array($h, $distinctHours)) {
$distinctHours[] = $h;
}
$hours[$h]++;
if (in_array($dayIndex, $distinctDays)) {
$distinctDays[] = $dayIndex;
}
$days[$dayIndex]++;
}
}
}
sort($distinctHours, SORT_NUMERIC);
sort($distinctDays, SORT_NUMERIC);
if (count($distinctDays) == 7) {
if (count($distinctHours) == 1) {
wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_ONCE_DAILY);
wfConfig::set('schedStartHour', $distinctHours[0]);
}
else if (count($distinctHours) == 2) {
$matchesTwiceDaily = false;
if ($distinctHours[0] + 12 == $distinctHours[1]) {
$matchesTwiceDaily = true;
foreach ($sched as $dayIndex => $day) {
if (!$day[$distinctHours[0]] || !$day[$distinctHours[1]]) {
$matchesTwiceDaily = false;
}
}
}
if ($matchesTwiceDaily) {
wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_TWICE_DAILY);
wfConfig::set('schedStartHour', $distinctHours[0]);
}
else {
wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_CUSTOM);
}
}
else {
wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_CUSTOM);
}
}
else if (count($distinctDays) == 5 && count($distinctHours) == 1) {
if ($days[2] == 0 && $days[4] == 0 && $hours[$distinctHours[0]] == 5) {
wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_ODD_DAYS_WEEKENDS);
wfConfig::set('schedStartHour', $distinctHours[0]);
}
else if ($days[0] == 0 && $days[6] == 0 && $hours[$distinctHours[0]] == 5) {
wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_WEEKDAYS);
wfConfig::set('schedStartHour', $distinctHours[0]);
}
else {
wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_CUSTOM);
}
}
else if (count($distinctDays) == 2 && count($distinctHours) == 1) {
if ($distinctDays[0] == 0 && $distinctDays[1] == 6 && $hours[$distinctHours[0]] == 2) {
wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_WEEKENDS);
wfConfig::set('schedStartHour', $distinctHours[0]);
}
else {
wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_CUSTOM);
}
}
else {
wfConfig::set('manualScanType', wfScanner::MANUAL_SCHEDULING_CUSTOM);
}
}
//manualScanType
}
}
//---- Onboarding
if (!$freshAPIKey) {
wfOnboardingController::migrateOnboarding();
}
//7.0.2
if (!wfConfig::get('blocks702Migration')) {
$blocksTable = wfBlock::blocksTable();
$query = "UPDATE `{$blocksTable}` SET `type` = %d WHERE `type` = %d AND `parameters` IS NOT NULL AND `parameters` LIKE '%\"ipRange\"%'";
$wpdb->query($wpdb->prepare($query, wfBlock::TYPE_PATTERN, wfBlock::TYPE_IP_AUTOMATIC_PERMANENT));
$countryBlock = wfBlock::countryBlocks();
if (!count($countryBlock)) {
$query = "UPDATE `{$blocksTable}` SET `type` = %d WHERE `type` = %d AND `parameters` IS NOT NULL AND `parameters` LIKE '%\"blockLogin\"%' LIMIT 1";
$wpdb->query($wpdb->prepare($query, wfBlock::TYPE_COUNTRY, wfBlock::TYPE_IP_AUTOMATIC_PERMANENT));
}
$query = "DELETE FROM `{$blocksTable}` WHERE `type` = %d AND `parameters` IS NOT NULL AND `parameters` LIKE '%\"blockLogin\"%'";
$wpdb->query($wpdb->prepare($query, wfBlock::TYPE_IP_AUTOMATIC_PERMANENT));
wfConfig::set('blocks702Migration', 1);
}
//7.0.3
/*if (!wfConfig::get('generateAllOptionsNotification')) {
new wfNotification(null, wfNotification::PRIORITY_HIGH_WARNING, 'Developers: If you prefer to edit all Wordfence options on one page, you can enable the "All Options" page here:
Enable "All Options" Page
', 'wfplugin_devalloptions');
wfConfig::set('generateAllOptionsNotification', 1);
}*/
//7.1.9
if (wfConfig::get('loginSec_maxFailures') == 1) {
wfConfig::set('loginSec_maxFailures', 2);
}
$blocksTable = wfBlock::blocksTable();
$patternBlocks = wfBlock::patternBlocks();
foreach ($patternBlocks as $b) {
if (!empty($b->ipRange) && preg_match('/^\d+\-\d+$/', $b->ipRange)) { //Old-style range block using long2ip
$ipRange = new wfUserIPRange($b->ipRange);
$ipRange = $ipRange->getIPString();
$parameters = $b->parameters;
$parameters['ipRange'] = $ipRange;
$wpdb->query($wpdb->prepare("UPDATE `{$blocksTable}` SET `parameters` = %s WHERE `id` = %d", json_encode($parameters), $b->id));
}
}
wfConfig::set('needsGeoIPSync', true, wfConfig::DONT_AUTOLOAD);
// Set the default scan options based on scan type.
if (!wfConfig::get('config720Migration', false)) {
// Replace critical/warning checkboxes with setting based on numeric severity value.
if (wfConfig::hasCachedOption('alertOn_critical') && wfConfig::hasCachedOption('alertOn_warnings')) {
$alertOnCritical = wfConfig::get('alertOn_critical');
$alertOnWarnings = wfConfig::get('alertOn_warnings');
wfConfig::set('alertOn_scanIssues', $alertOnCritical || $alertOnWarnings);
if ($alertOnCritical && ! $alertOnWarnings) {
wfConfig::set('alertOn_severityLevel', wfIssues::SEVERITY_HIGH);
} else {
wfConfig::set('alertOn_severityLevel', wfIssues::SEVERITY_LOW);
}
}
// Update severity for existing issues where they are still using the old severity values.
foreach (wfIssues::$issueSeverities as $issueType => $severity) {
$wpdb->query($wpdb->prepare("UPDATE $issuesTable SET severity = %d
WHERE `type` = %s
AND severity in (0,1,2)
", $severity, $issueType));
}
$syncedOptions = array();
switch (wfConfig::get('scanType')) {
case wfScanner::SCAN_TYPE_LIMITED:
$syncedOptions = wfScanner::limitedScanTypeOptions();
break;
case wfScanner::SCAN_TYPE_STANDARD:
$syncedOptions = wfScanner::standardScanTypeOptions();
break;
case wfScanner::SCAN_TYPE_HIGH_SENSITIVITY:
$syncedOptions = wfScanner::highSensitivityScanTypeOptions();
break;
}
if ($syncedOptions) {
foreach ($syncedOptions as $key => $value) {
if (is_bool($value)) {
wfConfig::set($key, $value ? 1 : 0);
}
}
}
wfConfig::set('config720Migration', true);
}
//7.2.3
if (wfConfig::get('waf_status') === false) {
$firewall = new wfFirewall();
$firewall->syncStatus(true);
}
//7.3.1
//---- drop long deprecated tables
$tables = array('wfBadLeechers', 'wfBlockedCommentLog', 'wfBlocks', 'wfBlocksAdv', 'wfLeechers', 'wfLockedOut', 'wfNet404s', 'wfScanners', 'wfThrottleLog', 'wfVulnScanners');
foreach ($tables as $t) {
$schema->drop($t);
}
//7.5.10
$knownFilesTable = wfDB::networkTable('wfKnownFileList');
$wordpressPathColumn = $wpdb->get_row($wpdb->prepare("SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s AND COLUMN_NAME = 'wordpress_path'", $knownFilesTable));
if ($wordpressPathColumn === null) {
$wpdb->query("DELETE FROM `{$knownFilesTable}`");
$wpdb->query("ALTER TABLE `{$knownFilesTable}` ADD COLUMN wordpress_path TEXT NOT NULL");
}
$realPathColumn = $wpdb->get_row($wpdb->prepare("SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s AND COLUMN_NAME = 'real_path'", $fileModsTable));
if ($realPathColumn === null) {
$wpdb->query("DELETE FROM `{$fileModsTable}`");
$wpdb->query("ALTER TABLE `{$fileModsTable}` ADD COLUMN real_path TEXT NOT NULL AFTER filename");
}
//wfFileMods updates
$wpdb->query("ALTER TABLE {$fileModsTable} ALTER COLUMN oldMD5 SET DEFAULT ''");
//---- enable legacy 2fa if applicable
if (wfConfig::get('isPaid') && (wfCredentialsController::hasOld2FARecords() || version_compare(phpversion(), '5.3', '<'))) {
wfConfig::set(wfCredentialsController::ALLOW_LEGACY_2FA_OPTION, true);
}
//Record the installation timestamp if activating the plugin for the first time
if (get_option('wordfenceActivated') != 1 && wfConfig::get('satisfactionPromptInstallDate') == 0 && empty(wfConfig::get('apiKey'))) {
wfConfig::set('satisfactionPromptInstallDate', time());
}
//8.2.0
// -- in veryFirstAction since it must run on/after the `plugins_loaded` hook
//Check the How does Wordfence get IPs setting
wfUtils::requestDetectProxyCallback();
//Install new schedule. If schedule config is blank it will install the default 'auto' schedule.
wfScanner::shared()->scheduleScans();
//Check our minimum versions and generate the necessary warnings
if (!wp_next_scheduled('wordfence_version_check')) {
wp_schedule_single_event(time(), 'wordfence_version_check');
}
//Must be the final line
}
public static function _sendLegacy2FADeprecationEmail() {
$message = sprintf(/* translators: 1. site URL; 2. legacy 2FA page URL */ __(<<logHitOK()){
$request = $wfLog->getCurrentRequest();
if(is_404()){
if ($request) {
$request->statusCode = 404;
}
$wfLog->logLeechAndBlock('404');
} else {
$wfLog->logLeechAndBlock('hit');
}
}
}
public static function initProtection(){ //Basic protection during WAF learning period
// Infinite WP Client - Authentication Bypass < 1.9.4.5
// https://wpvulndb.com/vulnerabilities/10011
$iwpRule = new wfWAFRule(wfWAF::getInstance(), 0x80000000, null, 'auth-bypass', 100, 'Infinite WP Client - Authentication Bypass < 1.9.4.5', 0, 'block', null);
wfWAF::getInstance()->setRules(wfWAF::getInstance()->getRules() + array(0x80000000 => $iwpRule));
if (strrpos(wfWAF::getInstance()->getRequest()->getRawBody(), '_IWP_JSON_PREFIX_') !== false) {
$iwpRequestDataArray = explode('_IWP_JSON_PREFIX_', wfWAF::getInstance()->getRequest()->getRawBody());
$iwpRequest = json_decode(trim(base64_decode($iwpRequestDataArray[1])), true);
if (is_array($iwpRequest)) {
if (array_key_exists('iwp_action', $iwpRequest) &&
($iwpRequest['iwp_action'] === 'add_site' || $iwpRequest['iwp_action'] === 'readd_site')
) {
require_once ABSPATH . '/wp-admin/includes/plugin.php';
if (is_plugin_active('iwp-client/init.php')) {
$iwpPluginData = get_plugin_data(WP_PLUGIN_DIR . '/iwp-client/init.php');
if (version_compare('1.9.4.5', $iwpPluginData['Version'], '>')) {
remove_action('setup_theme', 'iwp_mmb_set_request');
}
}
if ((is_multisite() ? get_site_option('iwp_client_action_message_id') : get_option('iwp_client_action_message_id')) &&
(is_multisite() ? get_site_option('iwp_client_public_key') : get_option('iwp_client_public_key'))
) {
wfWAF::getInstance()->getStorageEngine()->logAttack(array($iwpRule), 'request.rawBody',
wfWAF::getInstance()->getRequest()->getRawBody(),
wfWAF::getInstance()->getRequest(),
wfWAF::getInstance()->getRequest()->getMetadata()
);
}
}
}
}
}
public static function install_actions(){
register_activation_hook(WORDFENCE_FCPATH, 'wordfence::installPlugin');
register_deactivation_hook(WORDFENCE_FCPATH, 'wordfence::uninstallPlugin');
$versionInOptions = ((is_multisite() && function_exists('get_network_option')) ? get_network_option(null, 'wordfence_version', false) : get_option('wordfence_version', false));
if( (! $versionInOptions) || version_compare(WORDFENCE_VERSION, $versionInOptions, '>')){
//Either there is no version in options or the version in options is greater and we need to run the upgrade
self::runInstall();
}
self::getLog()->initLogRequest();
//Fix wp_mail bug when $_SERVER['SERVER_NAME'] is undefined
add_filter('wp_mail_from', 'wordfence::fixWPMailFromAddress');
//These access wfConfig::get('apiKey') and will fail if runInstall hasn't executed.
if(defined('MULTISITE') && MULTISITE === true){
global $blog_id;
if($blog_id == 1 && get_option('wordfenceActivated') != 1){ return; } //Because the plugin is active once installed, even before it's network activated, for site 1 (WordPress team, why?!)
}
//User may be logged in or not, so register both handlers
add_action('wp_ajax_nopriv_wordfence_lh', 'wordfence::ajax_lh_callback');
add_action('wp_ajax_nopriv_wordfence_doScan', 'wordfence::ajax_doScan_callback');
add_action('wp_ajax_nopriv_wordfence_testAjax', 'wordfence::ajax_testAjax_callback');
if(wfUtils::hasLoginCookie()){ //may be logged in. Fast way to check. These aren't secure functions, this is just a perf optimization, along with every other use of hasLoginCookie()
add_action('wp_ajax_wordfence_lh', 'wordfence::ajax_lh_callback');
add_action('wp_ajax_wordfence_doScan', 'wordfence::ajax_doScan_callback');
add_action('wp_ajax_wordfence_testAjax', 'wordfence::ajax_testAjax_callback');
if (is_multisite()) {
add_action('wp_network_dashboard_setup', 'wordfence::addDashboardWidget');
} else {
add_action('wp_dashboard_setup', 'wordfence::addDashboardWidget');
}
}
add_action('wp_ajax_wordfence_wafStatus', 'wordfence::ajax_wafStatus_callback');
add_action('wp_ajax_nopriv_wordfence_wafStatus', 'wordfence::ajax_wafStatus_callback');
add_action('wordfence_start_scheduled_scan', 'wordfence::wordfenceStartScheduledScan');
add_action('wordfence_daily_cron', 'wordfence::dailyCron');
add_action('wordfence_daily_autoUpdate', 'wfConfig::autoUpdate');
add_action('wordfence_hourly_cron', 'wordfence::hourlyCron');
add_action('wordfence_version_check', array(wfVersionCheckController::shared(), 'checkVersionsAndWarn'));
add_action('plugins_loaded', 'wordfence::veryFirstAction');
add_action('init', 'wordfence::initAction');
//add_action('admin_bar_menu', 'wordfence::admin_bar_menu', 99);
add_action('template_redirect', 'wordfence::templateRedir', 1001);
add_action('shutdown', 'wordfence::shutdownAction');
if (!wfConfig::get('ajaxWatcherDisabled_front')) {
add_action('wp_enqueue_scripts', 'wordfence::enqueueAJAXWatcher');
}
if (!wfConfig::get('ajaxWatcherDisabled_admin')) {
add_action('admin_enqueue_scripts', 'wordfence::enqueueAJAXWatcher');
}
//add_action('wp_enqueue_scripts', 'wordfence::enqueueDashboard');
add_action('admin_enqueue_scripts', 'wordfence::enqueueDashboard');
add_action('wp_authenticate','wordfence::authAction', 1, 2);
add_action('wp_authenticate_user', 'wordfence::authUserAction', 1, 2); //A secondary lockout check for plugins that override the login flow and don't call the complete set of hooks
add_filter('authenticate', 'wordfence::authenticateFilter', 99, 3);
$lockout = wfBlock::lockoutForIP(wfUtils::getIP());
if ($lockout !== false) {
add_filter('xmlrpc_enabled', '__return_false');
}
add_action('login_init','wordfence::loginInitAction');
add_action('wp_login','wordfence::loginAction');
add_action('wp_logout','wordfence::logoutAction');
add_action('lostpassword_post', 'wordfence::lostPasswordPost', 1, 2);
$allowSeparatePrompt = ini_get('output_buffering') > 0;
if (wfConfig::get('loginSec_enableSeparateTwoFactor') && $allowSeparatePrompt) {
add_action('login_form', 'wordfence::showTwoFactorField');
}
if(wfUtils::hasLoginCookie()){
add_action('user_profile_update_errors', 'wordfence::validateProfileUpdate', 0, 3 );
add_action('profile_update', 'wordfence::profileUpdateAction', 99, 2);
}
add_action('validate_password_reset', 'wordfence::validatePassword', 10, 2);
// Add actions for the email summary
add_action('wordfence_email_activity_report', array('wfActivityReport', 'executeCronJob'));
//For debugging
//add_filter( 'cron_schedules', 'wordfence::cronAddSchedules' );
add_filter('wp_redirect', 'wordfence::wpRedirectFilter', 99, 2);
add_filter('wp_redirect_status', 'wordfence::wpRedirectStatusFilter', 99, 2);
//html|xhtml|atom|rss2|rdf|comment|export
if(wfConfig::get('other_hideWPVersion')){
add_filter('style_loader_src', 'wordfence::replaceVersion');
add_filter('script_loader_src', 'wordfence::replaceVersion');
add_action('upgrader_process_complete', 'wordfence::hideReadme');
}
add_filter('get_the_generator_html', 'wordfence::genFilter', 99, 2);
add_filter('get_the_generator_xhtml', 'wordfence::genFilter', 99, 2);
add_filter('get_the_generator_atom', 'wordfence::genFilter', 99, 2);
add_filter('get_the_generator_rss2', 'wordfence::genFilter', 99, 2);
add_filter('get_the_generator_rdf', 'wordfence::genFilter', 99, 2);
add_filter('get_the_generator_comment', 'wordfence::genFilter', 99, 2);
add_filter('get_the_generator_export', 'wordfence::genFilter', 99, 2);
add_filter('registration_errors', 'wordfence::registrationFilter', 99, 3);
add_filter('woocommerce_new_customer_data', 'wordfence::wooRegistrationFilter', 99, 1);
if (wfConfig::get('loginSec_disableAuthorScan')) {
add_filter('oembed_response_data', 'wordfence::oembedAuthorFilter', 99, 4);
add_filter('rest_request_before_callbacks', 'wordfence::jsonAPIAuthorFilter', 99, 3);
add_filter('rest_post_dispatch', 'wordfence::jsonAPIAdjustHeaders', 99, 3);
add_filter('wp_sitemaps_users_pre_url_list', '__return_false', 99, 0);
add_filter('wp_sitemaps_add_provider', 'wordfence::wpSitemapUserProviderFilter', 99, 2);
}
if (wfConfig::get('loginSec_disableApplicationPasswords')) {
add_filter('wp_is_application_passwords_available', '__return_false');
add_action('edit_user_profile', 'wordfence::showDisabledApplicationPasswordsMessage', -1);
add_action('show_user_profile', 'wordfence::showDisabledApplicationPasswordsMessage', -1);
// Override the wp_die handler to let the user know app passwords were disabled by the Wordfence option.
if (!empty($_SERVER['SCRIPT_FILENAME']) && $_SERVER['SCRIPT_FILENAME'] === ABSPATH . 'wp-admin/authorize-application.php') {
add_filter('wp_die_handler', function ($handler = null) {
return function ($message, $title, $args) {
if ($message === 'Application passwords are not available.') {
$message = __('Application passwords have been disabled by Wordfence.', 'wordfence');
}
_default_wp_die_handler($message, $title, $args);
};
}, 10, 1);
}
}
add_filter('rest_dispatch_request', 'wordfence::_filterCentralFromLiveTraffic', 99, 4);
// Change GoDaddy's limit login mu-plugin since it can interfere with the two factor auth message.
if (self::hasGDLimitLoginsMUPlugin()) {
add_action('login_errors', array('wordfence', 'fixGDLimitLoginsErrors'), 11);
}
add_action('upgrader_process_complete', 'wfUpdateCheck::syncAllVersionInfo');
add_action('upgrader_process_complete', 'wordfence::_scheduleRefreshUpdateNotification', 99, 2);
add_action('automatic_updates_complete', 'wordfence::_scheduleRefreshUpdateNotification', 99, 0);
add_action('wordfence_refreshUpdateNotification', 'wordfence::_refreshUpdateNotification', 99, 0);
add_action('wordfence_completeCoreUpdateNotification', 'wordfence::_completeCoreUpdateNotification', 99, 0);
add_action('wfls_xml_rpc_blocked', 'wordfence::checkSecurityNetwork');
add_action('wfls_registration_blocked', 'wordfence::checkSecurityNetwork');
add_action('wfls_page_footer', 'wordfence::_outputLoginSecurityTour');
add_action('wfls_settings_set', 'wordfence::queueCentralConfigurationSync', 10, 2);
if(is_admin()){
add_action('admin_init', 'wordfence::admin_init');
add_action('admin_head', 'wordfence::_retargetWordfenceSubmenuCallout');
add_action('admin_print_scripts', 'wordfence::_setupImportMap', 0);
add_filter('script_loader_tag', 'wordfence::_tagVueScriptAsModule' , 10, 3);
if(is_multisite()){
if(wfUtils::isAdminPageMU()){
add_action('network_admin_menu', 'wordfence::admin_menus', 10);
add_action('network_admin_menu', 'wordfence::admin_menus_20', 20);
add_action('network_admin_menu', 'wordfence::admin_menus_30', 30);
add_action('network_admin_menu', 'wordfence::admin_menus_40', 40);
add_action('network_admin_menu', 'wordfence::admin_menus_50', 50);
add_action('network_admin_menu', 'wordfence::admin_menus_60', 60);
add_action('network_admin_menu', 'wordfence::admin_menus_70', 70);
add_action('network_admin_menu', 'wordfence::admin_menus_80', 80);
add_action('network_admin_menu', 'wordfence::admin_menus_85', 85);
add_action('network_admin_menu', 'wordfence::admin_menus_90', 90);
} //else don't show menu
} else {
add_action('admin_menu', 'wordfence::admin_menus', 10);
add_action('admin_menu', 'wordfence::admin_menus_20', 20);
add_action('admin_menu', 'wordfence::admin_menus_30', 30);
add_action('admin_menu', 'wordfence::admin_menus_40', 40);
add_action('admin_menu', 'wordfence::admin_menus_50', 50);
add_action('admin_menu', 'wordfence::admin_menus_60', 60);
add_action('admin_menu', 'wordfence::admin_menus_70', 70);
add_action('admin_menu', 'wordfence::admin_menus_80', 80);
add_action('admin_menu', 'wordfence::admin_menus_85', 85);
add_action('admin_menu', 'wordfence::admin_menus_90', 90);
}
add_action('in_admin_header', 'wordfence::_finishWordfenceSubmenuCalloutPatch', 10);
add_filter('plugin_action_links_' . plugin_basename(realpath(dirname(__FILE__) . '/../wordfence.php')), 'wordfence::_pluginPageActionLinks');
add_filter('pre_current_active_plugins', 'wordfence::registerDeactivationPrompt');
}
add_action('request', 'wordfence::preventAuthorNScans');
add_action('password_reset', 'wordfence::actionPasswordReset');
wfCredentialsController::registerObservers();;
$adminUsers = new wfAdminUserMonitor();
if ($adminUsers->isEnabled()) {
add_action('set_user_role', array($adminUsers, 'updateToUserRole'), 10, 3);
add_action('grant_super_admin', array($adminUsers, 'grantSuperAdmin'), 10, 1);
add_action('revoke_super_admin', array($adminUsers, 'revokeSuperAdmin'), 10, 1);
} else if (wfConfig::get_ser('adminUserList', false)) {
// reset this in the event it's disabled or the network is too large
wfConfig::set_ser('adminUserList', false);
}
if (wfConfig::liveTrafficEnabled()) {
add_action('wp_head', 'wordfence::wfLogHumanHeader');
add_action('login_head', 'wordfence::wfLogHumanHeader');
}
add_action('wordfence_processAttackData', 'wordfence::processAttackData');
if (!empty($_GET['wordfence_syncAttackData']) && get_site_option('wordfence_syncingAttackData') <= time() - 60 && get_site_option('wordfence_lastSyncAttackData', 0) < time() - 8) {
if (wfUtils::funcEnabled('ignore_user_abort')) {
ignore_user_abort(true);
}
update_site_option('wordfence_syncingAttackData', time());
header('Content-Type: text/javascript');
define('WORDFENCE_SYNCING_ATTACK_DATA', true);
add_action('init', 'wordfence::syncAttackData', 10, 0);
add_filter('woocommerce_unforce_ssl_checkout', '__return_false');
}
add_action('wordfence_batchReportBlockedAttempts', 'wordfence::wfsnBatchReportBlockedAttempts');
add_action('wordfence_batchReportFailedAttempts', 'wordfence::wfsnBatchReportFailedAttempts');
add_action('wordfence_batchSendSecurityEvents', 'wfCentral::sendPendingSecurityEvents');
add_action('wordfence_batchSendAuditEvents', 'wfAuditLog::sendPendingAuditEvents');
if (wfConfig::get('other_hideWPVersion')) {
add_filter('update_feedback', 'wordfence::restoreReadmeForUpgrade');
}
add_action('rest_api_init', 'wordfence::initRestAPI');
if (wfCentral::isConnected()) {
add_action('wordfence_security_event', 'wfCentral::sendSecurityEvent', 10, 3);
} else {
add_action('wordfence_security_event', 'wfCentral::sendAlertCallback', 10, 3);
}
if (!wfConfig::get('wordfenceI18n', true)) {
add_filter('gettext', function ($translation, $text, $domain) {
if ($domain === 'wordfence') {
return $text;
}
return $translation;
}, 10, 3);
}
wfScanMonitor::registerActions();
wfUpdateCheck::installPluginAPIFixer();
wfAuditLog::shared()->registerHooks();
}
public static function registerDeactivationPrompt() {
$deleteMain = (bool) wfConfig::get('deleteTablesOnDeact');
$deleteLoginSecurity = (bool) \WordfenceLS\Controller_Settings::shared()->get('delete-deactivation');
echo wfView::create(
'offboarding/deactivation-prompt',
array(
'deactivationOption' => wfDeactivationOption::forState($deleteMain, $deleteLoginSecurity),
'wafOptimized' => defined('WFWAF_AUTO_PREPEND') && WFWAF_AUTO_PREPEND && (!defined('WFWAF_SUBDIRECTORY_INSTALL') || !WFWAF_SUBDIRECTORY_INSTALL),
'deactivate' => array_key_exists('wf_deactivate', $_GET)
)
)->render();
}
public static function showDisabledApplicationPasswordsMessage() {
echo wfView::create('user/disabled-application-passwords', array('isAdmin' => self::isCurrentUserAdmin()))->render();
}
public static function _pluginPageActionLinks($links) {
if (!wfConfig::get('isPaid')) {
$links = array_merge(array('aWordfencePluginCallout' => '' . esc_html__('Upgrade To Premium', 'wordfence') . ' (opens in new tab)'), $links);
}
return $links;
}
public static function _outputLoginSecurityTour() {
if (WORDFENCE_LS_FROM_CORE) {
echo wfView::create('tours/login-security', array())->render();
}
}
public static function fixWPMailFromAddress($from_email) {
if ($from_email == 'wordpress@') { //$_SERVER['SERVER_NAME'] is undefined so we get an incomplete email address
wordfence::status(4, 'info', __("wp_mail from address is incomplete, attempting to fix", 'wordfence'));
$urls = array(get_site_url(), get_home_url());
foreach ($urls as $u) {
if (!empty($u)) {
$u = preg_replace('#^[^/]*//+([^/]+).*$#', '\1', $u);
if (substr($u, 0, 4) == 'www.') {
$u = substr($u, 4);
}
if (!empty($u)) {
wordfence::status(4, 'info', sprintf(/* translators: Email address. */ __("Fixing wp_mail from address: %s", 'wordfence'), $from_email . $u));
return $from_email . $u;
}
}
}
//Can't fix it, return it as it was
}
return $from_email;
}
public static function wpRedirectFilter($location, $status) {
self::getLog()->initLogRequest();
self::getLog()->getCurrentRequest()->statusCode = $status;
return $location;
}
public static function wpRedirectStatusFilter($status, $location) {
self::getLog()->initLogRequest();
self::getLog()->getCurrentRequest()->statusCode = $status;
self::getLog()->logHit();
return $status;
}
public static function enqueueAJAXWatcher() {
$wafDisabled = !WFWAF_ENABLED || (class_exists('wfWAFConfig') && wfWAFConfig::isDisabled());
if (wfUtils::isAdmin() && !$wafDisabled) {
wp_enqueue_style('wordfenceAJAXcss', wfUtils::getBaseURL() . wfUtils::versionedAsset('css/wordfenceBox.css'), '', WORDFENCE_VERSION);
wp_enqueue_script('wfi18njs', wfUtils::getBaseURL() . wfUtils::versionedAsset('js/wfi18n.js'), array(), WORDFENCE_VERSION);
wp_enqueue_script('wordfenceAJAXjs', wfUtils::getBaseURL() . wfUtils::versionedAsset('js/admin.ajaxWatcher.js'), array('jquery'), WORDFENCE_VERSION);
wp_localize_script('wordfenceAJAXjs', 'WFAJAXWatcherVars', array(
'nonce' => wp_create_nonce('wf-waf-error-page'),
));
self::setupI18nJSStrings();
}
}
private static function isWordfencePage($includeWfls = true) {
$page = wfUtils::array_get($_GET, 'page', '');
return (!empty($page) && (preg_match('/^Wordfence/', $page) || ($includeWfls && $page == 'WFLS')));
}
private static function isWordfenceSubpage($page, $subpage) {
return array_key_exists('page', $_GET) && $_GET['page'] == ('Wordfence' . ucfirst($page)) && array_key_exists('subpage', $_GET) && $_GET['subpage'] == $subpage;
}
public static function enqueueDashboard() {
if (wfUtils::isAdmin()) {
wp_enqueue_style('wf-adminbar', wfUtils::getBaseURL() . wfUtils::versionedAsset('css/wf-adminbar.css'), '', WORDFENCE_VERSION);
wp_enqueue_style('wordfence-license-global-style', wfLicense::current()->getGlobalStylesheet(), '', WORDFENCE_VERSION);
wp_enqueue_script('wordfenceDashboardjs', wfUtils::getBaseURL() . wfUtils::versionedAsset('js/wfdashboard.js'), array('jquery'), WORDFENCE_VERSION);
if (wfConfig::get('showAdminBarMenu')) {
wp_enqueue_script('wordfencePopoverjs', wfUtils::getBaseURL() . wfUtils::versionedAsset('js/wfpopover.js'), array('jquery'), WORDFENCE_VERSION);
wp_localize_script('wordfenceDashboardjs', 'WFDashVars', array(
'ajaxURL' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('wp-ajax'),
));
}
}
}
public static function ajax_testAjax_callback(){
die("WFSCANTESTOK");
}
public static function ajax_doScan_callback(){
@ignore_user_abort(true);
self::$wordfence_wp_version = false;
if (!defined('DONOTCACHEDB')) { define('DONOTCACHEDB', true); }
//This is messy, but not sure of a better way to do this without guaranteeing we get $wp_version
require(ABSPATH . 'wp-includes/version.php'); /** @var string $wp_version */
self::$wordfence_wp_version = $wp_version;
require_once(dirname(__FILE__) . '/wfScan.php');
wfScan::wfScanMain();
} //END doScan
public static function ajax_lh_callback(){
self::getLog()->canLogHit = false;
$UA = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
$isCrawler = empty($UA);
if ($UA) {
if (wfCrawl::isCrawler($UA) || wfCrawl::isGoogleCrawler()) {
$isCrawler = true;
}
}
@ob_end_clean();
if(! headers_sent()){
header('Content-type: text/javascript');
header("Connection: close");
header("Content-Length: 0");
header("X-Robots-Tag: noindex");
if (!$isCrawler) {
wfLog::cacheHumanRequester(wfUtils::getIP(), $UA);
}
}
flush();
if (!$isCrawler && array_key_exists('hid', $_GET)) {
$hid = $_GET['hid'];
$hid = wfUtils::decrypt($hid);
if (!is_string($hid) || !preg_match('/^\d+$/', $hid)) { exit(); }
$db = new wfDB();
$table_wfHits = wfDB::networkTable('wfHits');
$db->queryWrite("update {$table_wfHits} set jsRun=1 where id=%d", $hid);
}
die("");
}
/**
* Returns the AJAX mapping for admin-ajax.php actions recognized by this plugin. The array contains the nonce name
* under the key `nonce` and a callable for the permissions check under the key `permissions`.
*
* @return array
*/
protected static function _ajaxActions() {
$adminOnlyWPAJAX = array('nonce' => 'wp-ajax', 'permissions' => function() { return wfUtils::isAdmin(); });
$actions = array(
//Admin-only, keyed on `wp-ajax` nonce
'acknowledgeAdminUser' => $adminOnlyWPAJAX,
'activate' => $adminOnlyWPAJAX,
'activityLogUpdate' => $adminOnlyWPAJAX,
'autoUpdateChoice' => $adminOnlyWPAJAX,
'avatarLookup' => $adminOnlyWPAJAX,
'blockIP' => $adminOnlyWPAJAX,
'bulkOperation' => $adminOnlyWPAJAX,
'checkHtaccess' => $adminOnlyWPAJAX,
'createBlock' => $adminOnlyWPAJAX,
'dashboardShowMore' => $adminOnlyWPAJAX,
'deleteAdminUser' => $adminOnlyWPAJAX,
'deleteBlocks' => $adminOnlyWPAJAX,
'deleteDatabaseOption' => $adminOnlyWPAJAX,
'deleteFile' => $adminOnlyWPAJAX,
'deleteIssue' => $adminOnlyWPAJAX,
'disableDirectoryListing' => $adminOnlyWPAJAX,
'dismissNotification' => $adminOnlyWPAJAX,
'downgradeLicense' => $adminOnlyWPAJAX,
'downloadHtaccess' => $adminOnlyWPAJAX,
'downloadLogFile' => $adminOnlyWPAJAX,
'email_summary_email_address_debug' => $adminOnlyWPAJAX,
'enableAllOptionsPage' => $adminOnlyWPAJAX,
'exportDiagnostics' => $adminOnlyWPAJAX,
'exportSettings' => $adminOnlyWPAJAX,
'fixFPD' => $adminOnlyWPAJAX,
'getBlocks' => $adminOnlyWPAJAX,
'hideFileHtaccess' => $adminOnlyWPAJAX,
'hideNoticeForUser' => $adminOnlyWPAJAX,
'importSettings' => $adminOnlyWPAJAX,
'installAutoPrepend' => $adminOnlyWPAJAX,
'installLicense' => $adminOnlyWPAJAX,
'killScan' => $adminOnlyWPAJAX,
'loadIssues' => $adminOnlyWPAJAX,
'loadLiveTraffic' => $adminOnlyWPAJAX,
'loadStaticPanel' => $adminOnlyWPAJAX,
'mailingSignup' => $adminOnlyWPAJAX,
'makePermanentBlocks' => $adminOnlyWPAJAX,
'misconfiguredHowGetIPsChoice' => $adminOnlyWPAJAX,
'permBlockIP' => $adminOnlyWPAJAX,
'recentTraffic' => $adminOnlyWPAJAX,
'recordTOUPP' => $adminOnlyWPAJAX,
'removeExclusion' => $adminOnlyWPAJAX,
'restoreDefaults' => $adminOnlyWPAJAX,
'restoreFile' => $adminOnlyWPAJAX,
'reverseLookup' => $adminOnlyWPAJAX,
'revokeAdminUser' => $adminOnlyWPAJAX,
'saveDebuggingConfig' => $adminOnlyWPAJAX,
'saveDisclosureState' => $adminOnlyWPAJAX,
'saveOptions' => $adminOnlyWPAJAX,
'scan' => $adminOnlyWPAJAX,
'sendActivityLog' => $adminOnlyWPAJAX,
'sendDiagnostic' => $adminOnlyWPAJAX,
'sendTestEmail' => $adminOnlyWPAJAX,
'setDeactivationOption' => $adminOnlyWPAJAX,
'switchLiveTrafficSecurityOnlyChoice' => $adminOnlyWPAJAX,
'ticker' => $adminOnlyWPAJAX,
'tourClosed' => $adminOnlyWPAJAX,
'unblockIP' => $adminOnlyWPAJAX,
'unblockNetwork' => $adminOnlyWPAJAX,
'unblockRange' => $adminOnlyWPAJAX,
'uninstallAutoPrepend' => $adminOnlyWPAJAX,
'unlockOutIP' => $adminOnlyWPAJAX,
'updateAlertEmail' => $adminOnlyWPAJAX,
'updateAllIssues' => $adminOnlyWPAJAX,
'updateIPPreview' => $adminOnlyWPAJAX,
'updateIssueStatus' => $adminOnlyWPAJAX,
'updateWAFRules' => $adminOnlyWPAJAX,
'utilityScanForBlacklisted' => $adminOnlyWPAJAX,
'wfcentral_disconnect' => $adminOnlyWPAJAX,
'wfcentral_step1' => $adminOnlyWPAJAX,
'wfcentral_step2' => $adminOnlyWPAJAX,
'wfcentral_step3' => $adminOnlyWPAJAX,
'wfcentral_step4' => $adminOnlyWPAJAX,
'wfcentral_step5' => $adminOnlyWPAJAX,
'wfcentral_step6' => $adminOnlyWPAJAX,
'whitelistWAFParamKey' => $adminOnlyWPAJAX,
'whois' => $adminOnlyWPAJAX,
'wordfenceSatisfactionChoice' => $adminOnlyWPAJAX,
//Others
'dismissAdminNotice' => array('nonce' => 'wf-ajax-dismiss-admin-notice', 'permissions' => function() {
$user = wp_get_current_user();
return ($user instanceof WP_User && $user->exists() && wfAdminNoticeQueue::hasAnyNotice($user));
}),
);
return $actions;
}
/**
* Returns an array of all AJAX action nonces available to the current user.
*
* @return array
*/
public static function ajaxNonces() {
$ajaxActions = self::_ajaxActions();
$uniqueNonceKeys = array();
foreach ($ajaxActions as $action => $config) {
if (!call_user_func($config['permissions'])) { continue; }
if (!array_key_exists($config['nonce'], $uniqueNonceKeys)) {
$uniqueNonceKeys[$config['nonce']] = array(
'nonce' => wp_create_nonce($config['nonce']),
'actions' => array(),
);
}
$uniqueNonceKeys[$config['nonce']]['actions'][] = $action;
}
return $uniqueNonceKeys;
}
public static function ajaxReceiver() {
$func = (isset($_POST['action']) && $_POST['action']) ? $_POST['action'] : $_GET['action'];
$func = str_replace('wordfence_', '', $func); //func is e.g. wordfence_ticker so need to munge it
$actions = self::_ajaxActions();
if (!array_key_exists($func, $actions)) {
wfUtils::send_json(array('errorMsg' => __('Unknown AJAX action.', 'wordfence')));
}
$handler = $actions[$func];
if (!call_user_func($handler['permissions'])) {
wfUtils::send_json(array('errorMsg' => __('You do not have permission to perform this action.', 'wordfence')));
}
if (!array_key_exists('nonce', $_POST) && !array_key_exists('nonce', $_GET)) {
wfUtils::send_json(array('errorMsg' => __('Your browser sent an invalid security token to Wordfence. Please try reloading this page or signing out and in again.', 'wordfence'), 'tokenInvalid' => 1));
}
$nonce = (isset($_POST['nonce']) && $_POST['nonce']) ? $_POST['nonce'] : $_GET['nonce'];
if (!wp_verify_nonce($nonce, $handler['nonce'])) {
wfUtils::send_json(array('errorMsg' => __('Your browser sent an invalid security token to Wordfence. Please try reloading this page or signing out and in again.', 'wordfence'), 'tokenInvalid' => 1));
}
$returnArr = call_user_func('wordfence::ajax_' . $func . '_callback');
if ($returnArr === false) {
$returnArr = array('errorMsg' => __('Wordfence encountered an internal error executing that request.', 'wordfence'));
}
if (!is_array($returnArr)) {
error_log("Function " . wp_kses($func, array()) . " did not return an array and did not generate an error.");
$returnArr = array();
}
if (isset($returnArr['nonce'])) {
error_log("Wordfence ajax function return an array with 'nonce' already set. This could be a bug.");
}
$returnArr['nonce'] = wp_create_nonce($handler['nonce']);
$returnArr['nonce-key'] = $handler['nonce'];
wfUtils::send_json($returnArr);
}
public static function validateProfileUpdate($errors, $update, $userData){
wordfence::validatePassword($errors, $userData);
}
public static function validatePassword($errors, $userData) {
$password = (isset($_POST['pass1']) && trim($_POST['pass1'])) ? $_POST['pass1'] : false;
$user_id = isset($userData->ID) ? $userData->ID : false;
$username = isset($_POST["user_login"]) ? $_POST["user_login"] : $userData->user_login;
if ($password == false) { return $errors; }
if ($errors->get_error_data("pass")) { return $errors; }
$result = wfCredentialsController::maybePerformStrongPasswordCheck($username, $password, isset($userData->ID) ? $userData : null);
if (is_wp_error($result)) {
$errors->add($result->get_error_code(), $result->get_error_message(), $result->get_error_data()); //Can use WP_Error->merge_from() in the future when minimum WP is 5.6
}
$twoFactorUsers = wfConfig::get_ser('twoFactorUsers', array()); //Legacy 2FA check
if (preg_match(self::$passwordCodePattern, $password) && is_array($twoFactorUsers) && count($twoFactorUsers) > 0) {
$errors->add('pass', __('Passwords containing a space followed by "wf" without quotes are not allowed.', 'wordfence'));
return $errors;
}
$result = wfCredentialsController::maybePerformBreachedPasswordCheck($username, $password, isset($userData->ID) ? $userData : null);
if (is_wp_error($result)) {
$errors->add($result->get_error_code(), $result->get_error_message(), $result->get_error_data()); //Can use WP_Error->merge_from() in the future when minimum WP is 5.6
}
return $errors;
}
public static function isStrongPasswd($passwd, $username ) {
$passwd = trim($passwd);
$lowerPasswd = strtolower($passwd);
$passwdLength = strlen($lowerPasswd);
if ($passwdLength < 12)
return false;
if ($lowerPasswd == strtolower( $username ) )
return false;
if (preg_match('/(?:password|passwd|mypass|wordpress)/i', $passwd))
return false;
if (preg_match('/(.)\1{2,}/', $lowerPasswd)) //Disallow any character repeated 3 or more times
return false;
/*
* Check for ordered sequences of at least 4 characters for alphabetic sequences and 3 characters for other sequences, ignoring case
* Examples:
* - 321
* - abcd
* - abab
*/
$last = null;
$sequenceLength = 1;
$alphabetic = true;
for ($i = 0; $i < $passwdLength; $i++) {
$current = ord($lowerPasswd[$i]);
if ($last !== null) {
if (abs($current - $last) === 1) {
$alphabetic &= ctype_alpha($lowerPasswd[$i]);
if (++$sequenceLength > ($alphabetic ? 3 : 2))
return false;
}
else {
$sequenceLength = 1;
$alphabetic = true;
}
}
$last = $current;
}
$characterTypes = array(
'/[a-z]/',
'/[A-Z]/',
'/[0-9]/',
'/[^a-zA-Z0-9]/'
);
foreach ($characterTypes as $type) {
if (!preg_match($type, $passwd))
return false;
}
return true;
}
public static function lostPasswordPost($errors = null, $user = null) {
$IP = wfUtils::getIP();
if ($request = self::getLog()->getCurrentRequest()) {
$request->action = 'lostPassword';
$request->save();
}
if (wfBlock::isWhitelisted($IP)) {
return;
}
$lockout = wfBlock::lockoutForIP(wfUtils::getIP());
if ($lockout !== false) {
$lockout->recordBlock();
$customText = wpautop(wp_strip_all_tags(wfConfig::get('blockCustomText', '')));
require(dirname(__FILE__) . '/wfLockedOut.php');
}
if ($user === null) {
if (empty($_POST['user_login'])) { return; }
$user_login = $_POST['user_login'];
if (is_array($user_login)) { $user_login = wfUtils::array_first($user_login); }
$user_login = trim($user_login);
$user = get_user_by('login', $user_login);
if (!$user) {
$user = get_user_by('email', $user_login);
}
}
if ($user === false && wfConfig::get('loginSec_maskLoginErrors')) {
if (self::hasWoocommerce() && isset($_POST['wc_reset_password'], $_POST['user_login'])) {
$redirectUrl = add_query_arg('reset-link-sent', 'true', wc_get_account_endpoint_url('lost-password'));
}
else {
$redirectUrl = !empty($_REQUEST['redirect_to']) ? $_REQUEST['redirect_to'] : 'wp-login.php?checkemail=confirm';
}
wp_safe_redirect($redirectUrl);
exit;
}
if($user){
$alertCallback = array(new wfLostPasswdFormAlert($user, wfUtils::getIP()), 'send');
do_action('wordfence_security_event', 'lostPasswdForm', array(
'email' => $user->user_email,
'ip' => wfUtils::getIP(),
), $alertCallback);
}
// do not count password reset attempts if there is a user logged in with the edit_users capability
// because they're probably using the "send password reset" feature in the WP admin and therefore we shouldn't
// be locking them out!
if(wfConfig::get('loginSecurityEnabled') && !current_user_can( 'edit_users' ) ){
$tKey = self::getForgotPasswordFailureCountTransient($IP);
$forgotAttempts = get_transient($tKey);
if($forgotAttempts){
$forgotAttempts++;
} else {
$forgotAttempts = 1;
}
if($forgotAttempts >= wfConfig::get('loginSec_maxForgotPasswd')){
self::lockOutIP($IP, wfWAFBlockI18n::getBlockDescription(
wfWAFBlockI18n::WFWAF_BLOCK_LOGINSEC_FORGOTPASSWD,
wfConfig::get('loginSec_maxForgotPasswd'),
$_POST['user_login']
));
$customText = wpautop(wp_strip_all_tags(wfConfig::get('blockCustomText', '')));
require(dirname(__FILE__) . '/wfLockedOut.php');
}
set_transient($tKey, $forgotAttempts, wfConfig::get('loginSec_countFailMins') * 60);
}
}
public static function lockOutIP($IP, $reason) {
wfBlock::createLockout($reason, $IP, wfBlock::lockoutDuration(), time(), time(), 1);
self::getLog()->tagRequestForLockout($reason);
$alertCallback = array(new wfLoginLockoutAlert($IP, $reason), 'send');
do_action('wordfence_security_event', 'loginLockout', array(
'ip' => $IP,
'reason' => $reason,
'duration' => wfBlock::lockoutDuration(),
), $alertCallback);
}
public static function getLoginFailureCountTransient($IP) {
return 'wflginfl_' . bin2hex(wfUtils::inet_pton($IP));
}
public static function getForgotPasswordFailureCountTransient($IP) {
return 'wffgt_' . bin2hex(wfUtils::inet_pton($IP));
}
public static function clearLockoutCounters($IP) {
delete_transient(self::getLoginFailureCountTransient($IP));
delete_transient(self::getForgotPasswordFailureCountTransient($IP));
}
public static function veryFirstAction() {
/** @var wpdb $wpdb ; */
global $wpdb;
//8.2.0 Legacy 2FA migration
if (wfCredentialsController::useLegacy2FA() && !wfConfig::getBool('finalLegacy2FAMigration', false)) {
if (wfCredentialsController::hasOld2FARecords()) {
$admins = array();
if (is_multisite()) {
$superAdmins = get_super_admins();
foreach ($superAdmins as $userLogin) {
$user = get_user_by('login', $userLogin);
if ($user && $user->exists()) {
$admins[] = $user;
}
}
}
else {
$user_query = new WP_User_Query(array(
'blog_id' => get_current_blog_id(),
'role' => 'administrator',
));
$users = $user_query->get_results();
if (is_array($users)) {
$admins = $users;
}
}
wfAdminNoticeQueue::addAdminNotice(wfAdminNotice::SEVERITY_CRITICAL,
__('NOTICE: This site is using Wordfence\'s legacy 2FA feature, which was replaced with an improved version in 2019. The legacy 2FA feature will be discontinued around July 1, 2026.', 'wordfence') . '
' .
__('At that time, 2FA codes will no longer be checked, however a valid login and password will still be required for site access. This applies to both SMS and Authenticator methods.', 'wordfence') . '
' .
/* translators: 1. Legacy 2FA page URL. */
sprintf(__('To continue using 2FA, you will need to switch this site to the newer method on the Legacy Two-Factor Authentication page, and then users will need to set up 2FA with an authenticator app.', 'wordfence'), esc_url(wfUtils::maybeNetworkAdminURL('admin.php?page=WordfenceTools&subpage=twofactor'))),
'legacy2faDeprecation', array_map(function ($u) { return $u->ID; }, $admins));
add_action('init', 'wordfence::_sendLegacy2FADeprecationEmail');
}
else {
wfConfig::set(wfCredentialsController::ALLOW_LEGACY_2FA_OPTION, false);
wfConfig::set(wfCredentialsController::DISABLE_LEGACY_2FA_OPTION, true);
}
wfConfig::set('finalLegacy2FAMigration', true);
}
self::initProtection();
$wfFunc = wfUtils::array_get($_GET, '_wfsf');
if ($wfFunc == 'unlockEmail') {
$nonceValid = false;
if (isset($_POST['nonce']) && is_string($_POST['nonce'])) {
$nonceValid = wp_verify_nonce($_POST['nonce'], 'wf-form');
if (!$nonceValid && method_exists(wfWAF::getInstance(), 'createNonce')) {
$nonceValid = wfWAF::getInstance()->verifyNonce($_POST['nonce'], 'wf-form');
}
}
if(!$nonceValid){
die(__("Sorry but your browser sent an invalid security token when trying to use this form.", 'wordfence'));
}
$numTries = get_transient('wordfenceUnlockTries');
if($numTries > 10){
printf("%s
%s
",
esc_html__('Please wait 3 minutes and try again', 'wordfence'),
esc_html__('You have used this form too much. Please wait 3 minutes and try again.', 'wordfence')
);
exit();
}
if(! $numTries){ $numTries = 1; } else { $numTries = $numTries + 1; }
set_transient('wordfenceUnlockTries', $numTries, 180);
$email = trim(@$_POST['email']);
global $wpdb;
$ws = $wpdb->get_results($wpdb->prepare("SELECT ID, user_login FROM $wpdb->users WHERE user_email = %s", $email));
$found = false;
foreach($ws as $user){
$userDat = get_userdata($user->ID);
if(wfUtils::isAdmin($userDat)){
if($email == $userDat->user_email){
$found = true;
break;
}
}
}
if(! $found){
foreach(wfConfig::getAlertEmails() as $alertEmail){
if($alertEmail == $email){
$found = true;
break;
}
}
}
if($found){
$key = wfUtils::bigRandomHex();
$IP = wfUtils::getIP();
set_transient('wfunlock_' . $key, $IP, 1800);
$content = wfUtils::tmpl('email_unlockRequest.php', array(
'siteName' => get_bloginfo('name', 'raw'),
'siteURL' => wfUtils::getSiteBaseURL(),
'unlockHref' => wfUtils::getSiteBaseURL() . '?_wfsf=unlockAccess&key=' . $key,
'key' => $key,
'IP' => $IP
));
wp_mail($email, __("Unlock email requested", 'wordfence'), $content, "Content-Type: text/html");
}
echo "" . esc_html__('Your request was received', 'wordfence') . "
" .
esc_html(sprintf(/* translators: Email address. */ __("We received a request to email \"%s\" instructions to unlock their access. If that is the email address of a site administrator or someone on the Wordfence alert list, they have been emailed instructions on how to regain access to this system. The instructions we sent will expire 30 minutes from now.", 'wordfence'), wp_kses($email, array())))
. "
";
exit();
} else if($wfFunc == 'unlockAccess'){
if (!preg_match('/^(?:(?:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9](?::|$)){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))$/i', get_transient('wfunlock_' . $_GET['key']))) {
_e("Invalid key provided for authentication.", 'wordfence');
exit();
}
if($_GET['func'] == 'unlockMyIP'){
wfBlock::unblockIP(wfUtils::getIP());
if (class_exists('wfWAFIPBlocksController')) { wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings(); }
self::clearLockoutCounters(wfUtils::getIP());
header('Location: ' . wp_login_url());
exit();
} else if($_GET['func'] == 'unlockAllIPs'){
wordfence::status(1, 'info', __("Request received via unlock email link to unblock all IPs.", 'wordfence'));
wfBlock::removeAllIPBlocks();
if (class_exists('wfWAFIPBlocksController')) { wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings(); }
self::clearLockoutCounters(wfUtils::getIP());
header('Location: ' . wp_login_url());
exit();
} else if($_GET['func'] == 'disableRules'){
wfConfig::set('firewallEnabled', 0);
wfConfig::set('loginSecurityEnabled', 0);
wordfence::status(1, 'info', __("Request received via unlock email link to unblock all IPs via disabling firewall rules.", 'wordfence'));
wfBlock::removeAllIPBlocks();
wfBlock::removeAllCountryBlocks();
if (class_exists('wfWAFIPBlocksController')) { wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings(); }
self::clearLockoutCounters(wfUtils::getIP());
header('Location: ' . wp_login_url());
exit();
} else {
_e("Invalid function specified. Please check the link we emailed you and make sure it was not cut-off by your email reader.", 'wordfence');
exit();
}
}
else if ($wfFunc == 'detectProxy') {
wfUtils::doNotCache();
if (wfUtils::processDetectProxyCallback()) {
self::getLog()->getCurrentRequest()->action = 'scan:detectproxy'; //Exempt a valid callback from live traffic
echo wfConfig::get('detectProxyRecommendation', '-');
}
else {
echo '0';
}
exit();
}
else if ($wfFunc == 'removeAlertEmail') {
wfUtils::doNotCache();
$payloadStatus = false;
$jwt = (isset($_GET['jwt']) && is_string($_GET['jwt'])) ? $_GET['jwt'] : '';
if (!empty($jwt)) {
$payload = wfUtils::decodeJWT($jwt);
if ($payload && isset($payload['email'])) {
$payloadStatus = true;
}
}
if (isset($_POST['resend'])) {
$email = trim(wfUtils::array_get($_POST, 'email', ''));
$found = false;
$alertEmails = wfConfig::getAlertEmails();
foreach ($alertEmails as $e) {
if ($e == $email) {
$found = true;
break;
}
}
if ($found) {
$content = wfUtils::tmpl('email_unsubscribeRequest.php', array(
'siteName' => get_bloginfo('name', 'raw'),
'siteURL' => wfUtils::getSiteBaseURL(),
'IP' => wfUtils::getIP(),
'jwt' => wfUtils::generateJWT(array('email' => $email)),
));
wp_mail($email, __("Unsubscribe Requested", 'wordfence'), $content, "Content-Type: text/html");
}
echo wfView::create('common/unsubscribe', array(
'state' => 'resent',
))->render();
exit();
}
else if (!$payloadStatus) {
echo wfView::create('common/unsubscribe', array(
'state' => 'bad',
))->render();
exit();
}
else if (isset($_POST['confirm'])) {
$confirm = wfUtils::truthyToBoolean($_POST['confirm']);
if ($confirm) {
$found = false;
$alertEmails = wfConfig::getAlertEmails();
$updatedAlertEmails = array();
foreach ($alertEmails as $alertEmail) {
if ($alertEmail == $payload['email']) {
$found = true;
}
else {
$updatedAlertEmails[] = $alertEmail;
}
}
if ($found) {
wfConfig::set('alertEmails', implode(',', $updatedAlertEmails));
}
echo wfView::create('common/unsubscribe', array(
'jwt' => $_GET['jwt'],
'email' => $payload['email'],
'state' => 'unsubscribed',
))->render();
exit();
}
}
echo wfView::create('common/unsubscribe', array(
'jwt' => $_GET['jwt'],
'email' => $payload['email'],
'state' => 'prompt',
))->render();
exit();
}
else if ($wfFunc == 'installLicense') {
if (wfUtils::isAdmin()) {
wfUtils::doNotCache();
if (isset($_POST['license'])) {
$nonceValid = wp_verify_nonce(@$_POST['nonce'], 'wf-form');
if (!$nonceValid) {
die(__('Sorry but your browser sent an invalid security token when trying to use this form.', 'wordfence'));
}
$changes = array('apiKey' => $_POST['license']);
$errors = wfConfig::validate($changes);
if ($errors !== true) {
$error = __('An error occurred while saving the license.', 'wordfence');
if (count($errors) == 1) {
$error = sprintf(/* translators: Error message. */ __('An error occurred while saving the license: %s', 'wordfence'), $errors[0]['error']);
}
echo wfView::create('common/license', array(
'state' => 'bad',
'error' => $error,
))->render();
exit();
}
try {
wfConfig::save(wfConfig::clean($changes));
echo wfView::create('common/license', array(
'state' => 'installed',
))->render();
exit();
}
catch (Exception $e) {
echo wfView::create('common/license', array(
'state' => 'bad',
'error' => sprintf(/* translators: Error message. */ __('An error occurred while saving the license: %s', 'wordfence'), $e->getMessage()),
))->render();
exit();
}
}
echo wfView::create('common/license', array(
'state' => 'prompt',
))->render();
exit();
}
}
if (is_main_site() && wfUtils::isAdmin()) {
if (wp_next_scheduled('wordfence_daily_cron') === false) {
wp_schedule_event(time() + 600, 'daily', 'wordfence_daily_cron');
wordfence::status(2, 'info', __("Rescheduled missing daily cron", 'wordfence'));
}
if (wp_next_scheduled('wordfence_hourly_cron') === false) {
wp_schedule_event(time() + 600, 'hourly', 'wordfence_hourly_cron');
wordfence::status(2, 'info', __("Rescheduled missing hourly cron", 'wordfence'));
}
}
// Sync the WAF data with the database.
if (!WFWAF_SUBDIRECTORY_INSTALL && $waf = wfWAF::getInstance()) {
$homeurl = wfUtils::wpHomeURL();
$siteurl = wfUtils::wpSiteURL();
//Sync the GeoIP database if needed
$destination = WFWAF_LOG_PATH . '/' . wfIpLocation::DATABASE_FILE_NAME;
if (!file_exists($destination) || wfConfig::get('needsGeoIPSync')) {
$allowSync = false;
if (wfConfig::createLock('wfSyncGeoIP')) {
$status = get_transient('wfSyncGeoIPActive');
if (!$status) {
$allowSync = true;
set_transient('wfSyncGeoIPActive', true, 3600);
}
wfConfig::releaseLock('wfSyncGeoIP');
}
if ($allowSync) {
wfUtils::requireIpLocator();
try {
$wflogsLocator = wfIpLocator::getInstance(wfIpLocator::SOURCE_WFLOGS);
$bundledLocator = wfIpLocator::getInstance(wfIpLocator::SOURCE_BUNDLED);
if (!$wflogsLocator->isPreferred() || $wflogsLocator->getDatabaseVersion() !== $bundledLocator->getDatabaseVersion()) {
$source = dirname(__FILE__) . '/' . wfIpLocation::DATABASE_FILE_NAME;
if (copy($source, $destination)) {
$shash = '';
$dhash = '';
$sp = @fopen($source, "rb");
if ($sp) {
$scontext = hash_init('sha256');
while (!feof($sp)) {
$data = fread($sp, 65536);
if ($data === false) {
$scontext = false;
break;
}
hash_update($scontext, $data);
}
fclose($sp);
if ($scontext !== false) {
$shash = hash_final($scontext, false);
}
}
$dp = @fopen($destination, "rb");
if ($dp) {
$dcontext = hash_init('sha256');
while (!feof($dp)) {
$data = fread($dp, 65536);
if ($data === false) {
$dcontext = false;
break;
}
hash_update($dcontext, $data);
}
fclose($dp);
if ($scontext !== false) {
$dhash = hash_final($dcontext, false);
}
}
if (hash_equals($shash, $dhash)) {
wfConfig::remove('needsGeoIPSync');
delete_transient('wfSyncGeoIPActive');
}
}
}
else {
wfConfig::remove('needsGeoIPSync');
delete_transient('wfSyncGeoIPActive');
}
}
catch (Exception $e) {
//Ignore
}
}
}
try {
$sapi = @php_sapi_name();
if ($sapi != "cli") {
$lastPermissionsTemplateCheck = wfConfig::getInt('lastPermissionsTemplateCheck', 0);
if (defined('WFWAF_LOG_PATH') && ($lastPermissionsTemplateCheck + 43200) < time()) { //Run no more frequently than every 12 hours
$timestamp = preg_replace('/[^0-9]/', '', microtime(false)); //We avoid using tmpfile since it can potentially create one with different permissions than the defaults
$tmpTemplate = rtrim(WFWAF_LOG_PATH, '/') . "/template.{$timestamp}.tmp";
$template = rtrim(WFWAF_LOG_PATH, '/') . '/template.php';
@unlink($tmpTemplate);
@file_put_contents($tmpTemplate, "\n");
$tmpStat = @stat($tmpTemplate);
if ($tmpStat !== false) {
$mode = $tmpStat[2] & 0777;
$updatedMode = 0600;
if (($mode & 0020) == 0020) { //Group writable
$updatedMode = $updatedMode | 0060;
}
if (defined('WFWAF_LOG_FILE_MODE')) {
$updatedMode = WFWAF_LOG_FILE_MODE;
}
$stat = @stat($template);
if ($stat === false || ($stat[2] & 0777) != $updatedMode) {
@chmod($tmpTemplate, $updatedMode);
@unlink($template);
@rename($tmpTemplate, $template);
}
@unlink($tmpTemplate);
}
else {
@unlink($tmpTemplate);
}
wfConfig::set('lastPermissionsTemplateCheck', time());
@chmod(WFWAF_LOG_PATH, (wfWAFWordPress::permissions() | 0755));
wfWAFWordPress::writeHtaccess();
$contents = self::_wflogsContents();
if ($contents) {
$validFiles = wfWAF::getInstance()->fileList();
foreach ($validFiles as &$vf) {
$vf = basename($vf);
}
$validFiles = array_filter($validFiles);
$previousWflogsFileList = wfConfig::getJSON('previousWflogsFileList', array());
$wflogs = realpath(WFWAF_LOG_PATH);
$filesRemoved = array();
foreach ($contents as $f) {
if (!in_array($f, $validFiles) && in_array($f, $previousWflogsFileList)) {
$fullPath = $f;
$removed = self::_recursivelyRemoveWflogs($f);
$filesRemoved = array_merge($filesRemoved, $removed);
}
}
$contents = self::_wflogsContents();
wfConfig::setJSON('previousWflogsFileList', $contents);
if (!empty($filesRemoved)) {
$removalHistory = wfConfig::getJSON('diagnosticsWflogsRemovalHistory', array());
$removalHistory = array_slice($removalHistory, 0, 4);
array_unshift($removalHistory, array(time(), $filesRemoved));
wfConfig::setJSON('diagnosticsWflogsRemovalHistory', $removalHistory);
}
}
}
}
}
catch (Exception $e) {
//Ignore
}
try {
$configDefaults = array(
'apiKey' => wfConfig::get('apiKey'),
'isPaid' => !!wfConfig::get('isPaid'),
'siteURL' => $siteurl,
'homeURL' => $homeurl,
'whitelistedIPs' => (string) wfConfig::get('whitelisted'),
'whitelistedServiceIPs' => json_encode(wfUtils::whitelistedServiceIPs()),
'howGetIPs' => (string) wfConfig::get('howGetIPs'),
'howGetIPs_trusted_proxies_unified' => implode("\n", wfUtils::unifiedTrustedProxies()),
'detectProxyRecommendation' => (string) wfConfig::get('detectProxyRecommendation'),
'other_WFNet' => !!wfConfig::get('other_WFNet', true),
'pluginABSPATH' => ABSPATH,
'serverIPs' => json_encode(wfUtils::serverIPs()),
'blockCustomText' => wpautop(wp_strip_all_tags(wfConfig::get('blockCustomText', ''))),
'disableWAFIPBlocking' => wfConfig::get('disableWAFIPBlocking'),
'wordpressVersion' => wfConfig::get('wordpressVersion'),
'wordpressPluginVersions' => wfConfig::get_ser('wordpressPluginVersions'),
'wordpressThemeVersions' => wfConfig::get_ser('wordpressThemeVersions'),
'WPLANG' => get_site_option('WPLANG'),
);
if (wfUtils::isAdmin()) {
$errorNonceKey = 'errorNonce_' . get_current_user_id();
$configDefaults[$errorNonceKey] = wp_create_nonce('wf-waf-error-page'); //Used by the AJAX watcher script
}
foreach ($configDefaults as $key => $value) {
$waf->getStorageEngine()->setConfig($key, $value, 'synced');
}
if (wfConfig::get('timeoffset_wf') !== false) {
$waf->getStorageEngine()->setConfig('timeoffset_wf', wfConfig::get('timeoffset_wf'), 'synced');
}
else {
$waf->getStorageEngine()->unsetConfig('timeoffset_wf', 'synced');
}
if (class_exists('wfWAFIPBlocksController')) {
wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings();
}
if (wfUtils::isAdmin()) {
if ($waf->getStorageEngine()->getConfig('wafStatus', '') == 'learning-mode') {
if ($waf->getStorageEngine()->getConfig('learningModeGracePeriodEnabled', false)) {
if ($waf->getStorageEngine()->getConfig('learningModeGracePeriod', 0) <= time()) {
// Reached the end of the grace period, activate the WAF.
$waf->getStorageEngine()->setConfig('wafStatus', 'enabled');
$waf->getStorageEngine()->setConfig('learningModeGracePeriodEnabled', 0);
$waf->getStorageEngine()->unsetConfig('learningModeGracePeriod');
$firewall = new wfFirewall();
$firewall->syncStatus(true);
}
}
}
}
if (empty($_GET['wordfence_syncAttackData'])) {
$table_wfHits = wfDB::networkTable('wfHits');
if ($waf->getStorageEngine() instanceof wfWAFStorageMySQL) {
$lastAttackMicroseconds = floatval($waf->getStorageEngine()->getConfig('lastAttackDataTruncateTime'));
} else {
$lastAttackMicroseconds = $wpdb->get_var("SELECT MAX(attackLogTime) FROM {$table_wfHits}");
}
if (get_site_option('wordfence_lastSyncAttackData', 0) < time() - 8) {
if ($waf->getStorageEngine()->hasNewerAttackData($lastAttackMicroseconds)) {
if (get_site_option('wordfence_syncingAttackData') <= time() - 60) {
// Could be the request to itself is not completing, add ajax to the head as a workaround
$attempts = get_site_option('wordfence_syncAttackDataAttempts', 0);
if ($attempts > 10) {
add_action('wp_head', 'wordfence::addSyncAttackDataAjax');
add_action('login_head', 'wordfence::addSyncAttackDataAjax');
add_action('admin_head', 'wordfence::addSyncAttackDataAjax');
} else {
update_site_option('wordfence_syncAttackDataAttempts', ++$attempts);
wp_remote_post(add_query_arg('wordfence_syncAttackData', microtime(true), home_url('/')), array(
'timeout' => 0.01,
'blocking' => false,
'sslverify' => apply_filters('https_local_ssl_verify', false)
));
}
}
}
}
}
if ($waf instanceof wfWAFWordPress && ($learningModeAttackException = $waf->getLearningModeAttackException())) {
$log = self::getLog();
$log->initLogRequest();
$request = $log->getCurrentRequest();
$request->action = 'learned:waf';
$request->attackLogTime = microtime(true);
$ruleIDs = array();
/** @var wfWAFRule $failedRule */
foreach ($learningModeAttackException->getFailedRules() as $failedRule) {
$ruleIDs[] = $failedRule->getRuleID();
}
$actionData = array(
'learningMode' => 1,
'failedRules' => $ruleIDs,
'paramKey' => $learningModeAttackException->getParamKey(),
'paramValue' => $learningModeAttackException->getParamValue(),
);
if ($ruleIDs && $ruleIDs[0]) {
$rule = $waf->getRule($ruleIDs[0]);
if ($rule) {
$request->actionDescription = $rule->getDescription();
$actionData['category'] = $rule->getCategory();
$actionData['ssl'] = $waf->getRequest()->getProtocol() === 'https';
$actionData['fullRequest'] = base64_encode($waf->getRequest());
}
}
$request->actionData = wfRequestModel::serializeActionData($actionData);
register_shutdown_function(array($request, 'save'));
self::scheduleSendAttackData();
}
} catch (wfWAFStorageFileException $e) {
// We don't have anywhere to write files in this scenario.
} catch (wfWAFStorageEngineMySQLiException $e) {
// Ignore and continue
}
}
if(wfConfig::get('firewallEnabled')){
$wfLog = self::getLog();
$wfLog->firewallBadIPs();
$IP = wfUtils::getIP();
if (wfBlock::isWhitelisted($IP)) {
return;
}
if (wfConfig::get('neverBlockBG') == 'neverBlockUA' && wfCrawl::isGoogleCrawler()) {
return;
}
if (wfConfig::get('neverBlockBG') == 'neverBlockVerified' && wfCrawl::isVerifiedGoogleCrawler()) {
return;
}
if (wfConfig::get('bannedURLs', false)) {
$URLs = explode("\n", wfUtils::cleanupOneEntryPerLine(wfConfig::get('bannedURLs')));
foreach ($URLs as $URL) {
if (preg_match(wfUtils::patternToRegex($URL, ''), $_SERVER['REQUEST_URI'])) {
$reason = wfWAFBlockI18n::getBlockDescription(wfWAFBlockI18n::WFWAF_BLOCK_BANNEDURL);
wfBlock::createIP($reason, $IP, wfBlock::blockDuration(), time(), time(), 1, wfBlock::TYPE_IP_AUTOMATIC_TEMPORARY);
wfActivityReport::logBlockedIP($IP, null, 'bannedurl');
$wfLog->tagRequestForBlock($reason);
$wfLog->do503(3600, $reason);
//exits
}
}
}
if (wfConfig::get('other_blockBadPOST') == '1' && $_SERVER['REQUEST_METHOD'] == 'POST' && empty($_SERVER['HTTP_USER_AGENT']) && empty($_SERVER['HTTP_REFERER'])) {
$reason = wfWAFBlockI18n::getBlockDescription(wfWAFBlockI18n::WFWAF_BLOCK_BADPOST);
wfBlock::createIP($reason, $IP, wfBlock::blockDuration(), time(), time(), 1, wfBlock::TYPE_IP_AUTOMATIC_TEMPORARY);
wfActivityReport::logBlockedIP($IP, null, 'badpost');
$wfLog->tagRequestForBlock($reason);
$wfLog->do503(3600, $reason);
//exits
}
}
}
private static function _wflogsContents() {
$dir = opendir(WFWAF_LOG_PATH);
if ($dir) {
$contents = array();
while ($path = readdir($dir)) {
if ($path == '.' || $path == '..') { continue; }
$contents[] = $path;
}
closedir($dir);
return $contents;
}
return false;
}
/**
* Removes a path within wflogs, recursing as necessary.
*
* @param string $file
* @param array $processedDirs
* @return array The list of removed files/folders.
*/
private static function _recursivelyRemoveWflogs($file, $processedDirs = array()) {
if (preg_match('~(?:^|/|\\\\)\.\.(?:/|\\\\|$)~', $file)) {
return array();
}
if (stripos(WFWAF_LOG_PATH, 'wflogs') === false) { //Sanity check -- if not in a wflogs folder, user will have to do removal manually
return array();
}
$path = rtrim(WFWAF_LOG_PATH, '/') . '/' . $file;
if (is_link($path)) {
if (@unlink($path)) {
return array($file);
}
return array();
}
if (is_dir($path)) {
$real = realpath($file);
if (in_array($real, $processedDirs)) {
return array();
}
$processedDirs[] = $real;
$count = 0;
$dir = opendir($path);
if ($dir) {
$contents = array();
while ($sub = readdir($dir)) {
if ($sub == '.' || $sub == '..') { continue; }
$contents[] = $sub;
}
closedir($dir);
$filesRemoved = array();
foreach ($contents as $f) {
$removed = self::_recursivelyRemoveWflogs($file . '/' . $f, $processedDirs);
$filesRemoved = array($filesRemoved, $removed);
}
}
if (@rmdir($path)) {
$filesRemoved[] = $file;
}
return $filesRemoved;
}
if (@unlink($path)) {
return array($file);
}
return array();
}
public static function loginAction($username){
if(sizeof($_POST) < 1){ return; } //only execute if login form is posted
if(! $username){ return; }
wfConfig::inc('totalLogins');
$user = get_user_by('login', $username);
$userID = $user ? $user->ID : 0;
$userIsAdmin = wfUtils::isAdmin($userID);
self::getLog()->logLogin('loginOK', 0, $username);
if ($userIsAdmin) {
wfConfig::set_ser('lastAdminLogin', array(
'userID' => $userID,
'username' => $username,
'firstName' => $user->first_name,
'lastName' => $user->last_name,
'time' => wfUtils::localHumanDateShort(),
'IP' => wfUtils::getIP()
));
}
$salt = wp_salt('logged_in');
$cookieName = 'wf_loginalerted_' . hash_hmac('sha256', $userID, $salt);
$cookieValue = hash_hmac('sha256', $username, $salt);
$firstOnly = wfConfig::get($userIsAdmin ? 'alertOn_firstAdminLoginOnly' : 'alertOn_firstNonAdminLoginOnly');
$newDevice = !isset($_COOKIE[$cookieName]) || !hash_equals($cookieValue, $_COOKIE[$cookieName]);
if ($userIsAdmin) {
$securityEvent = 'adminLogin';
$alertCallback = array(new wfAdminLoginAlert($cookieName, $cookieValue, $username, wfUtils::getIP()), 'send');
}
else {
$securityEvent = 'nonAdminLogin';
$alertCallback = array(new wfNonAdminLoginAlert($cookieName, $cookieValue, $username, wfUtils::getIP()), 'send');
}
if ($newDevice && $firstOnly) {
$securityEvent .= 'NewLocation';
}
do_action('wordfence_security_event', $securityEvent, array(
'username' => $username,
'ip' => wfUtils::getIP(),
), $alertCallback);
if ($firstOnly) {
wfUtils::setcookie($cookieName, $cookieValue, time() + (86400 * 365), '/', null, wfUtils::isFullSSL(), true);
}
}
public static function registrationFilter($errors, $sanitizedLogin, $userEmail) {
if (wfConfig::get('loginSec_blockAdminReg') && $sanitizedLogin == 'admin') {
$errors->add('user_login_error', __('ERROR: You can\'t register using that username', 'wordfence'));
}
return $errors;
}
public static function wooRegistrationFilter($wooCustomerData) {
/*
$wooCustomerData matches:
array(
'user_login' => $username,
'user_pass' => $password,
'user_email' => $email,
'role' => 'customer',
)
*/
if (wfConfig::get('loginSec_blockAdminReg') && is_array($wooCustomerData) && isset($wooCustomerData['user_login']) && isset($wooCustomerData['user_email']) && preg_match('/^admin\d*$/i', $wooCustomerData['user_login'])) {
//Converts a username of `admin` generated from something like `admin@example.com` to `adminexample`
$emailComponents = explode('@', $wooCustomerData['user_email']);
if (strpos(wfUtils::array_last($emailComponents), '.') === false) { //e.g., admin@localhost
$wooCustomerData['user_login'] .= wfUtils::array_last($emailComponents);
}
else { //e.g., admin@example.com
$hostComponents = explode('.', wfUtils::array_last($emailComponents));
array_pop($hostComponents);
$wooCustomerData['user_login'] .= wfUtils::array_last($hostComponents);
}
//If it's still `admin` at this point, it will fall through and get blocked by wordfence::blacklistedUsernames
}
return $wooCustomerData;
}
public static function oembedAuthorFilter($data, $post, $width, $height) {
unset($data['author_name']);
unset($data['author_url']);
return $data;
}
public static function jsonAPIAuthorFilter($response, $handler, $request) {
$route = $request->get_route();
if (!current_user_can('edit_others_posts')) {
$urlBase = wfWP_REST_Users_Controller::wfGetURLBase();
if (preg_match('~' . preg_quote($urlBase, '~') . '/*$~i', $route)) {
$error = new WP_Error('rest_user_cannot_view', __('Sorry, you are not allowed to list users.', 'wordfence'), array('status' => rest_authorization_required_code()));
$response = rest_ensure_response($error);
if (!defined('WORDFENCE_REST_API_SUPPRESSED')) { define('WORDFENCE_REST_API_SUPPRESSED', true); }
}
else if (preg_match('~' . preg_quote($urlBase, '~') . '/+(\d+)/*$~i', $route, $matches)) {
$id = (int) $matches[1];
if (get_current_user_id() !== $id) {
$error = new WP_Error('rest_user_invalid_id', __('Invalid user ID.', 'wordfence'), array('status' => 404));
$response = rest_ensure_response($error);
if (!defined('WORDFENCE_REST_API_SUPPRESSED')) { define('WORDFENCE_REST_API_SUPPRESSED', true); }
}
}
}
return $response;
}
public static function jsonAPIAdjustHeaders($response, $server, $request) {
if (defined('WORDFENCE_REST_API_SUPPRESSED')) {
$response->header('Allow', 'GET');
}
return $response;
}
public static function wpSitemapUserProviderFilter($provider, $name) {
if ($name === 'users') {
return false;
}
return $provider;
}
public static function _filterCentralFromLiveTraffic($dispatch_result, $request, $route, $handler) {
if (preg_match('~^/wordfence/v\d+/~i', $route)) {
self::getLog()->canLogHit = false;
}
return $dispatch_result;
}
public static function showTwoFactorField() {
$existingContents = ob_get_contents();
if (!preg_match('/wftwofactornonce:([0-9]+)\/(.+?)\s/', $existingContents, $matches)) {
return;
}
$userID = intval($matches[1]);
$twoFactorNonce = preg_replace('/[^a-f0-9]/i', '', $matches[2]);
if (!self::verifyTwoFactorIntermediateValues($userID, $twoFactorNonce)) {
return;
}
//Strip out the username and password fields
$formPosition = strrpos($existingContents, '