: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if (is_admin()) { return false; } //Don't log admin pageviews
if (isset($_SERVER['HTTP_USER_AGENT'])) {
if (preg_match('/WordPress\/' . $this->wp_version . '/i', $_SERVER['HTTP_USER_AGENT'])) { return false; } //Ignore regular requests generated by WP UA.
$userID = get_current_user_id();
$userID = $this->effectiveUserID;
$user = new WP_User($userID);
if ($user && $user->exists()) {
if (wfConfig::get('liveTraf_ignorePublishers') && ($user->has_cap('publish_posts') || $user->has_cap('publish_pages'))) {
if (wfConfig::get('liveTraf_ignoreUsers')) {
$ignored = explode(',', wfConfig::get('liveTraf_ignoreUsers'));
foreach ($ignored as $entry) {
if($user->user_login == $entry){
if(wfConfig::get('liveTraf_ignoreIPs')){
$IPs = explode(',', wfConfig::get('liveTraf_ignoreIPs'));
foreach($IPs as $ignoreIP){
if( isset($_SERVER['HTTP_USER_AGENT']) && wfConfig::get('liveTraf_ignoreUA') ){
if($_SERVER['HTTP_USER_AGENT'] == wfConfig::get('liveTraf_ignoreUA')){
private function getDB(){
public function firewallBadIPs() {
if (wfBlock::isWhitelisted($IP)) {
//Range and UA pattern blocking
$patternBlocks = wfBlock::patternBlocks(true);
$userAgent = !empty($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
$referrer = !empty($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
foreach ($patternBlocks as $b) {
if ($b->matchRequest($IP, $userAgent, $referrer) !== wfBlock::MATCH_NONE) {
wfActivityReport::logBlockedIP($IP, null, 'advanced');
$this->currentRequest->actionDescription = __('UA/Referrer/IP Range not allowed', 'wordfence');
$this->do503(3600, __("Advanced blocking in effect.", 'wordfence')); //exits
$countryBlocks = wfBlock::countryBlocks(true);
foreach ($countryBlocks as $b) {
$match = $b->matchRequest($IP, false, false);
if ($match === wfBlock::MATCH_COUNTRY_REDIR_BYPASS) {
$bypassRedirDest = wfConfig::get('cbl_bypassRedirDest', '');
$this->getCurrentRequest()->actionDescription = __('redirected to bypass URL', 'wordfence');
$this->getCurrentRequest()->statusCode = 302;
$this->currentRequest->action = 'cbl:redirect';
wp_redirect($bypassRedirDest, 302);
else if ($match === wfBlock::MATCH_COUNTRY_REDIR) {
wfConfig::inc('totalCountryBlocked');
$this->getCurrentRequest()->actionDescription = sprintf(/* translators: URL */ __('blocked access via country blocking and redirected to URL (%s)', 'wordfence'), wfConfig::get('cbl_redirURL'));
$this->getCurrentRequest()->statusCode = 503;
if (!$this->getCurrentRequest()->action) {
$this->currentRequest->action = 'blocked:wordfence';
wfActivityReport::logBlockedIP($IP, null, 'country');
wp_redirect(wfConfig::get('cbl_redirURL'), 302);
else if ($match !== wfBlock::MATCH_NONE) {
$this->currentRequest->actionDescription = __('blocked access via country blocking', 'wordfence');
wfConfig::inc('totalCountryBlocked');
wfActivityReport::logBlockedIP($IP, null, 'country');
$this->do503(3600, __('Access from your area has been temporarily limited for security reasons', 'wordfence'));
$ipBlock = wfBlock::findIPBlock($IP);
if ($ipBlock !== false) {
$secsToGo = max(0, $ipBlock->expiration - time());
if (wfConfig::get('other_WFNet') && self::isAuthRequest()) { //It's an auth request and this IP has been blocked
$this->getCurrentRequest()->action = 'blocked:wfsnrepeat';
wordfence::wfsnReportBlockedAttempt($IP, 'login');
$reason = $ipBlock->reason;
if ($ipBlock->type == wfBlock::TYPE_IP_MANUAL || $ipBlock->type == wfBlock::TYPE_IP_AUTOMATIC_PERMANENT) {
$reason = __('Manual block by administrator', 'wordfence');
$this->do503($secsToGo, $reason); //exits
private function takeBlockingAction($configVar, $reason) {
if ($this->googleSafetyCheckOK()) {
$action = wfConfig::get($configVar . '_action');
if ($action == 'block') { //Rate limited - block temporarily
$secsToGo = wfBlock::blockDuration();
wfBlock::createRateBlock($reason, $IP, $secsToGo);
wfActivityReport::logBlockedIP($IP, null, 'throttle');
$this->tagRequestForBlock($reason);
$alertCallback = array(new wfBlockAlert($IP, $reason, $secsToGo), 'send');
do_action('wordfence_security_event', 'block', array(
wordfence::status(2, 'info', sprintf(/* translators: 1. IP address. 2. Description of firewall action. */ __('Blocking IP %1$s. %2$s', 'wordfence'), $IP, $reason));
else if ($action == 'throttle') { //Rate limited - throttle
$secsToGo = wfBlock::rateLimitThrottleDuration();
wfBlock::createRateThrottle($reason, $IP, $secsToGo);
wfActivityReport::logBlockedIP($IP, null, 'throttle');
do_action('wordfence_security_event', 'throttle', array(
wordfence::status(2, 'info', sprintf(/* translators: 1. IP address. 2. Description of firewall action. */ __('Throttling IP %1$s. %2$s', 'wordfence'), $IP, $reason));
wfConfig::inc('totalIPsThrottled');
$this->do503($secsToGo, $reason, false);
* Test if the current request is for wp-login.php or xmlrpc.php
private static function isAuthRequest() {
if ((strpos($_SERVER['REQUEST_URI'], '/wp-login.php') !== false)) {
public function do503($secsToGo, $reason, $sendEventToCentral = true){
if ($sendEventToCentral) {
do_action('wordfence_security_event', 'block', array(
'ip' => wfUtils::inet_ntop($this->currentRequest->IP),
'reason' => $this->currentRequest->actionDescription ? $this->currentRequest->actionDescription : $reason,
$this->currentRequest->statusCode = 503;
if (!$this->currentRequest->action) {
$this->currentRequest->action = 'blocked:wordfence';
if (!$this->currentRequest->actionDescription) {
$this->currentRequest->actionDescription = "blocked: " . $reason;
wfConfig::inc('total503s');
header('HTTP/1.1 503 Service Temporarily Unavailable');
header('Status: 503 Service Temporarily Unavailable');
header('Retry-After: ' . $secsToGo);
$customText = wpautop(wp_strip_all_tags(wfConfig::get('blockCustomText', '')));
require_once(dirname(__FILE__) . '/wf503.php');
private function redirect($URL){
private function googleSafetyCheckOK(){ //returns true if OK to block. Returns false if we must not block.
$cacheKey = md5( (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '') . ' ' . wfUtils::getIP());
//Cache so we can call this multiple times in one request
if(! isset(self::$gbSafeCache[$cacheKey])){
$nb = wfConfig::get('neverBlockBG');
if($nb == 'treatAsOtherCrawlers'){
self::$gbSafeCache[$cacheKey] = true; //OK to block because we're treating google like everyone else
} else if($nb == 'neverBlockUA' || $nb == 'neverBlockVerified'){
if(wfCrawl::isGoogleCrawler()){ //Check the UA using regex
if($nb == 'neverBlockVerified'){
if(wfCrawl::isVerifiedGoogleCrawler(wfUtils::getIP())){ //UA check passed, now verify using PTR if configured to
self::$gbSafeCache[$cacheKey] = false; //This is a verified Google crawler, so no we can't block it
self::$gbSafeCache[$cacheKey] = true; //This is a crawler claiming to be Google but it did not verify
self::$gbSafeCache[$cacheKey] = false; //User configured us to only do a UA check and this claims to be google so don't block
self::$gbSafeCache[$cacheKey] = true; //This isn't a Google UA, so it's OK to block
//error_log("Wordfence error: neverBlockBG option is not set.");
self::$gbSafeCache[$cacheKey] = false; //Oops the config option is not set. This should never happen because it's set on install. So we return false to indicate it's not OK to block just for safety.
if(! isset(self::$gbSafeCache[$cacheKey])){
//error_log("Wordfence assertion fail in googleSafetyCheckOK: cached value is not set.");
return false; //for safety
return self::$gbSafeCache[$cacheKey]; //return cached value
public function addStatus($level, $type, $msg){
//$msg = '[' . sprintf('%.2f', memory_get_usage(true) / (1024 * 1024)) . '] ' . $msg;
$this->getDB()->queryWrite("insert into " . $this->statusTable . " (ctime, level, type, msg) values (%s, %d, '%s', '%s')", sprintf('%.6f', microtime(true)), $level, $type, $msg);
public function getStatusEvents($lastCtime){
$lastCtime = $this->getDB()->querySingle("select ctime from " . $this->statusTable . " order by ctime desc limit 1000,1");
$results = $this->getDB()->querySelect("select ctime, level, type, msg from " . $this->statusTable . " where ctime > %f order by ctime asc", $lastCtime);
$timeOffset = 3600 * get_option('gmt_offset');
foreach($results as &$rec){
//$rec['timeAgo'] = wfUtils::makeTimeAgo(time() - $rec['ctime']);
$rec['date'] = date('M d H:i:s', (int) $rec['ctime'] + $timeOffset);
$rec['msg'] = wp_kses_data( (string) $rec['msg']);
public function getSummaryEvents(){
$results = $this->getDB()->querySelect("select ctime, level, type, msg from " . $this->statusTable . " where level = 10 order by ctime desc limit 100");
$timeOffset = 3600 * get_option('gmt_offset');
foreach($results as &$rec){
$rec['date'] = date('M d H:i:s', (int) $rec['ctime'] + $timeOffset);
if(strpos($rec['msg'], 'SUM_PREP:') === 0){
return array_reverse($results);
public function getGooglePattern() {
return $this->googlePattern;
* @param string|null $ip_string
public function __construct($ip_string = null) {
$this->setIPString($ip_string);
* Check if the supplied IP address is within the user supplied range.
public function isIPInRange($ip) {
$ip_string = $this->getIPString();
if (strpos($ip_string, '/') !== false) { //CIDR range -- 127.0.0.1/24
return wfUtils::subnetContainsIP($ip_string, $ip);
else if (strpos($ip_string, '[') !== false) //Bracketed range -- 127.0.0.[1-100]
if (strpos($ip_string, '.') !== false && strpos($ip, '.') !== false) {
if (preg_match('/:ffff:([^:]+)$/i', $ip_string, $matches)) {
$ip_string = $matches[1];
if (preg_match('/:ffff:([^:]+)$/i', $ip, $matches)) {
if (preg_match('/\[\d+\-\d+\]/', $ip_string)) {
$IPparts = explode('.', $ip);
$whiteParts = explode('.', $ip_string);
if (count($whiteParts) != 4 || count($IPparts) != 4) {
for ($i = 0; $i <= 3; $i++) {
if (preg_match('/^\[(\d+)\-(\d+)\]$/', $whiteParts[$i], $m)) {
if ($IPparts[$i] < $m[1] || $IPparts[$i] > $m[2]) {
else if ($whiteParts[$i] != $IPparts[$i]) {
if ($mismatch === false) {
return true; // Is whitelisted because we did not get a mismatch
else if ($ip_string == $ip) {
else if (strpos($ip_string, ':') !== false && strpos($ip, ':') !== false) {
$ip = strtolower(wfUtils::expandIPv6Address($ip));
$ip_string = strtolower(self::expandIPv6Range($ip_string));
if (preg_match('/\[[a-f0-9]+\-[a-f0-9]+\]/i', $ip_string)) {
$IPparts = explode(':', $ip);
$whiteParts = explode(':', $ip_string);
if (count($whiteParts) != 8 || count($IPparts) != 8) {
for ($i = 0; $i <= 7; $i++) {
if (preg_match('/^\[([a-f0-9]+)\-([a-f0-9]+)\]$/i', $whiteParts[$i], $m)) {
$ip_group = hexdec($IPparts[$i]);
$range_group_from = hexdec($m[1]);
$range_group_to = hexdec($m[2]);
if ($ip_group < $range_group_from || $ip_group > $range_group_to) {
else if ($whiteParts[$i] != $IPparts[$i]) {
if ($mismatch === false) {
return true; // Is whitelisted because we did not get a mismatch
else if ($ip_string == $ip) {
else if (strpos($ip_string, '-') !== false) { //Linear range -- 127.0.0.1 - 127.0.1.100
list($ip1, $ip2) = explode('-', $ip_string);
$ip1N = wfUtils::inet_pton($ip1);
$ip2N = wfUtils::inet_pton($ip2);
$ipN = wfUtils::inet_pton($ip);
return (strcmp($ip1N, $ipN) <= 0 && strcmp($ip2N, $ipN) >= 0);
else { //Treat as a literal IP
$ip1 = @wfUtils::inet_pton($ip_string);
$ip2 = @wfUtils::inet_pton($ip);
if ($ip1 !== false && $ip1 == $ip2) {
private static function repeatString($string, $count) {
return str_repeat($string, $count);
* Expand a compressed printable range representation of an IPv6 address.
* @todo Hook up exceptions for better error handling.
* @todo Allow IPv4 mapped IPv6 addresses (::ffff:192.168.1.1).
* @param string $ip_range
public static function expandIPv6Range($ip_range) {
$colon_count = substr_count($ip_range, ':');
$dbl_colon_count = substr_count($ip_range, '::');
if ($dbl_colon_count > 1) {
$dbl_colon_pos = strpos($ip_range, '::');
if ($dbl_colon_pos !== false) {
$ip_range = str_replace('::', self::repeatString(':0000',
(($dbl_colon_pos === 0 || $dbl_colon_pos === strlen($ip_range) - 2) ? 9 : 8) - $colon_count) . ':', $ip_range);
$ip_range = trim($ip_range, ':');
$colon_count = substr_count($ip_range, ':');
$groups = explode(':', $ip_range);
foreach ($groups as $group) {
if (preg_match('/\[([a-f0-9]{1,4})\-([a-f0-9]{1,4})\]/i', $group, $matches)) {
$expanded .= sprintf('[%s-%s]', str_pad(strtolower($matches[1]), 4, '0', STR_PAD_LEFT), str_pad(strtolower($matches[2]), 4, '0', STR_PAD_LEFT)) . ':';
} else if (preg_match('/[a-f0-9]{1,4}/i', $group)) {
$expanded .= str_pad(strtolower($group), 4, '0', STR_PAD_LEFT) . ':';
return trim($expanded, ':');
public function isValidRange() {
return $this->isValidCIDRRange() || $this->isValidBracketedRange() || $this->isValidLinearRange() || wfUtils::isValidIP($this->getIPString());
public function isValidCIDRRange() { //e.g., 192.0.2.1/24
$ip_string = $this->getIPString();
if (preg_match('/[^0-9a-f:\/\.]/i', $ip_string)) { return false; }
return wfUtils::isValidCIDRRange($ip_string);
public function isValidBracketedRange() { //e.g., 192.0.2.[1-10]
$ip_string = $this->getIPString();
if (preg_match('/[^0-9a-f:\.\[\]\-]/i', $ip_string)) { return false; }
if (strpos($ip_string, '.') !== false) { //IPv4
if (preg_match_all('/(\d+)/', $ip_string, $matches) > 0) {
foreach ($matches[1] as $match) {
if ($group > 255 || $group < 0) {
$group_regex = '([0-9]{1,3}|\[[0-9]{1,3}\-[0-9]{1,3}\])';
return preg_match('/^' . str_repeat("{$group_regex}\\.", 3) . $group_regex . '$/i', $ip_string) > 0;
if (strpos($ip_string, '::') !== false) {
$ip_string = self::expandIPv6Range($ip_string);