: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
echo wfConfig::get('detectProxyRecommendation', '-');
else if ($wfFunc == 'removeAlertEmail') {
$jwt = (isset($_GET['jwt']) && is_string($_GET['jwt'])) ? $_GET['jwt'] : '';
$payload = wfUtils::decodeJWT($jwt);
if ($payload && isset($payload['email'])) {
if (isset($_POST['resend'])) {
$email = trim(@$_POST['email']);
$alertEmails = wfConfig::getAlertEmails();
foreach ($alertEmails as $e) {
$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(
else if (!$payloadStatus) {
echo wfView::create('common/unsubscribe', array(
else if (isset($_POST['confirm'])) {
$confirm = wfUtils::truthyToBoolean($_POST['confirm']);
$alertEmails = wfConfig::getAlertEmails();
$updatedAlertEmails = array();
foreach ($alertEmails as $alertEmail) {
if ($alertEmail == $payload['email']) {
$updatedAlertEmails[] = $alertEmail;
wfConfig::set('alertEmails', implode(',', $updatedAlertEmails));
echo wfView::create('common/unsubscribe', array(
'email' => $payload['email'],
'state' => 'unsubscribed',
echo wfView::create('common/unsubscribe', array(
'email' => $payload['email'],
else if ($wfFunc == 'installLicense') {
if (wfUtils::isAdmin()) {
if (isset($_POST['license'])) {
$nonceValid = wp_verify_nonce(@$_POST['nonce'], 'wf-form');
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);
$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(
wfConfig::save(wfConfig::clean($changes));
echo wfView::create('common/license', array(
echo wfView::create('common/license', array(
'error' => sprintf(/* translators: Error message. */ __('An error occurred while saving the license: %s', 'wordfence'), $e->getMessage()),
echo wfView::create('common/license', array(
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 . '/GeoLite2-Country.mmdb';
if (!file_exists($destination) || wfConfig::get('needsGeoIPSync')) {
if (wfConfig::createLock('wfSyncGeoIP')) {
$status = get_transient('wfSyncGeoIPActive');
set_transient('wfSyncGeoIPActive', true, 3600);
wfConfig::releaseLock('wfSyncGeoIP');
wfUtils::requireIpLocator();
$wflogsLocator = wfIpLocator::getInstance(wfIpLocator::SOURCE_WFLOGS);
$bundledLocator = wfIpLocator::getInstance(wfIpLocator::SOURCE_BUNDLED);
if (!$wflogsLocator->isPreferred() || $wflogsLocator->getDatabaseVersion() !== $bundledLocator->getDatabaseVersion()) {
$source = dirname(__FILE__) . '/GeoLite2-Country.mmdb';
if (copy($source, $destination)) {
$sp = @fopen($source, "rb");
$scontext = hash_init('sha256');
$data = fread($sp, 65536);
hash_update($scontext, $data);
if ($scontext !== false) {
$shash = hash_final($scontext, false);
$dp = @fopen($destination, "rb");
$dcontext = hash_init('sha256');
$data = fread($dp, 65536);
hash_update($dcontext, $data);
if ($scontext !== false) {
$dhash = hash_final($dcontext, false);
if (hash_equals($shash, $dhash)) {
wfConfig::remove('needsGeoIPSync');
delete_transient('wfSyncGeoIPActive');
wfConfig::remove('needsGeoIPSync');
delete_transient('wfSyncGeoIPActive');
$sapi = @php_sapi_name();
$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';
@file_put_contents($tmpTemplate, "<?php exit('Access denied'); __halt_compiler(); ?>\n");
$tmpStat = @stat($tmpTemplate);
if ($tmpStat !== false) {
$mode = $tmpStat[2] & 0777;
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);
@rename($tmpTemplate, $template);
wfConfig::set('lastPermissionsTemplateCheck', time());
@chmod(WFWAF_LOG_PATH, (wfWAFWordPress::permissions() | 0755));
wfWAFWordPress::writeHtaccess();
$contents = self::_wflogsContents();
$validFiles = wfWAF::getInstance()->fileList();
foreach ($validFiles as &$vf) {
$validFiles = array_filter($validFiles);
$previousWflogsFileList = wfConfig::getJSON('previousWflogsFileList', array());
$wflogs = realpath(WFWAF_LOG_PATH);
foreach ($contents as $f) {
if (!in_array($f, $validFiles) && in_array($f, $previousWflogsFileList)) {
$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);
'apiKey' => wfConfig::get('apiKey'),
'isPaid' => !!wfConfig::get('isPaid'),
'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');
$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'));
$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);
add_action('wp_head', 'wordfence::addSyncAttackDataAjax');
add_action('login_head', 'wordfence::addSyncAttackDataAjax');
add_action('admin_head', 'wordfence::addSyncAttackDataAjax');
update_site_option('wordfence_syncAttackDataAttempts', ++$attempts);
wp_remote_post(add_query_arg('wordfence_syncAttackData', microtime(true), home_url('/')), array(
'sslverify' => apply_filters('https_local_ssl_verify', false)
if ($waf instanceof wfWAFWordPress && ($learningModeAttackException = $waf->getLearningModeAttackException())) {
$request = $log->getCurrentRequest();
$request->action = 'learned:waf';
$request->attackLogTime = microtime(true);
/** @var wfWAFRule $failedRule */
foreach ($learningModeAttackException->getFailedRules() as $failedRule) {
$ruleIDs[] = $failedRule->getRuleID();
'failedRules' => $ruleIDs,
'paramKey' => $learningModeAttackException->getParamKey(),
'paramValue' => $learningModeAttackException->getParamValue(),
if ($ruleIDs && $ruleIDs[0]) {
$rule = $waf->getRule($ruleIDs[0]);
$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) {
if(wfConfig::get('firewallEnabled')){
$wfLog->firewallBadIPs();
if (wfBlock::isWhitelisted($IP)) {
if (wfConfig::get('neverBlockBG') == 'neverBlockUA' && wfCrawl::isGoogleCrawler()) {
if (wfConfig::get('neverBlockBG') == 'neverBlockVerified' && wfCrawl::isVerifiedGoogleCrawler()) {
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 = __('Accessed a banned URL', 'wordfence');
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, __("Accessed a banned URL", 'wordfence'));
if (wfConfig::get('other_blockBadPOST') == '1' && $_SERVER['REQUEST_METHOD'] == 'POST' && empty($_SERVER['HTTP_USER_AGENT']) && empty($_SERVER['HTTP_REFERER'])) {
$reason = __('POST received with blank user-agent and referer', 'wordfence');
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, __("POST received with blank user-agent and referer", 'wordfence'));
private static function _wflogsContents() {
$dir = opendir(WFWAF_LOG_PATH);
while ($path = readdir($dir)) {
if ($path == '.' || $path == '..') { continue; }
* Removes a path within wflogs, recursing as necessary.
* @param array $processedDirs
* @return array The list of removed files/folders.
private static function _recursivelyRemoveWflogs($file, $processedDirs = array()) {
if (preg_match('~(?:^|/|\\\\)\.\.(?:/|\\\\|$)~', $file)) {
if (stripos(WFWAF_LOG_PATH, 'wflogs') === false) { //Sanity check -- if not in a wflogs folder, user will have to do removal manually
$path = rtrim(WFWAF_LOG_PATH, '/') . '/' . $file;