: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @param bool $retrieve if true, fetch and return the deleted rows
* @return bool|array true(or an array of blocks, if $retrieve is specified) or false on failure
public static function removeBlockIDs($blockIDs, $retrieve=false) {
$blocksTable = wfBlock::blocksTable();
$blockIDs = array_map('intval', $blockIDs);
$inClause = implode(', ', $blockIDs);
$blocks = $wpdb->get_results("SELECT * FROM `{$blocksTable}` WHERE `id` IN (".$inClause.")");
$query = "DELETE FROM `{$blocksTable}` WHERE `id` IN (" . $inClause . ")";
if($wpdb->query($query)!==false) {
* Removes all IP blocks (i.e., manual, wfsn, or rate limited)
public static function removeAllIPBlocks() {
$blocksTable = wfBlock::blocksTable();
$wpdb->query("DELETE FROM `{$blocksTable}` WHERE `type` IN (" . implode(', ', array(self::TYPE_IP_MANUAL, self::TYPE_IP_AUTOMATIC_TEMPORARY, self::TYPE_IP_AUTOMATIC_PERMANENT, self::TYPE_WFSN_TEMPORARY, self::TYPE_RATE_BLOCK, self::TYPE_RATE_THROTTLE, self::TYPE_LOCKOUT)) . ")");
* Removes all country blocks
public static function removeAllCountryBlocks() {
$blocksTable = wfBlock::blocksTable();
$wpdb->query("DELETE FROM `{$blocksTable}` WHERE `type` IN (" . implode(', ', array(self::TYPE_COUNTRY)) . ")");
* Removes all blocks that were created by WFSN responses.
public static function removeTemporaryWFSNBlocks() {
$blocksTable = wfBlock::blocksTable();
$wpdb->query($wpdb->prepare("DELETE FROM `{$blocksTable}` WHERE `type` = %d", self::TYPE_WFSN_TEMPORARY));
* Converts all blocks to non-expiring whose ID is in the given array.
public static function makePermanentBlockIDs($blockIDs) {
$blocksTable = wfBlock::blocksTable();
//TODO: revise this if we support user-customizable durations
self::TYPE_WFSN_TEMPORARY,
self::TYPE_RATE_THROTTLE,
self::TYPE_IP_AUTOMATIC_TEMPORARY,
$blockIDs = array_map('intval', $blockIDs);
$query = $wpdb->prepare("UPDATE `{$blocksTable}` SET `expiration` = %d, `type` = %d WHERE `id` IN (" . implode(', ', $blockIDs) . ") AND `type` IN (" . implode(', ', $supportedTypes) . ") AND (`expiration` > UNIX_TIMESTAMP())", self::DURATION_FOREVER, self::TYPE_IP_AUTOMATIC_PERMANENT);
$blockIDs = array_map('intval', $blockIDs);
$query = $wpdb->prepare("UPDATE `{$blocksTable}` SET `expiration` = %d, `type` = %d WHERE `id` IN (" . implode(', ', $blockIDs) . ") AND `type` IN (" . implode(', ', $supportedTypes) . ") AND (`expiration` > UNIX_TIMESTAMP())", self::DURATION_FOREVER, self::TYPE_IP_MANUAL);
* Removes all specific IP blocks and lockouts that can result in the given IP being blocked.
public static function unblockIP($ip) {
$blocksTable = wfBlock::blocksTable();
$ipHex = wfDB::binaryValueToSQLHex(wfUtils::inet_pton($ip));
$wpdb->query("DELETE FROM `{$blocksTable}` WHERE `IP` = {$ipHex}");
* Removes all lockouts that can result in the given IP being blocked.
public static function unlockOutIP($ip) {
$blocksTable = wfBlock::blocksTable();
$ipHex = wfDB::binaryValueToSQLHex(wfUtils::inet_pton($ip));
$wpdb->query($wpdb->prepare("DELETE FROM `{$blocksTable}` WHERE `IP` = {$ipHex} AND `type` = %d", self::TYPE_LOCKOUT));
* Constructs a wfBlock instance. This _does not_ create a new record in the table, only fetches or updates an existing one.
* @param bool $blockedTime
* @param bool $lastAttempt
* @param bool $blockedHits
* @param bool $expiration
* @param bool $parameters
public function __construct($id, $type = false, $ip = false, $blockedTime = false, $reason = false, $lastAttempt = false, $blockedHits = false, $expiration = false, $parameters = false) {
$this->_blockedTime = $blockedTime;
$this->_reason = $reason;
$this->_lastAttempt = $lastAttempt;
$this->_blockedHits = $blockedHits;
$this->_expiration = $expiration;
$this->_parameters = $parameters;
public function __get($key) {
if ($this->_type === false) { $this->_fetch(); }
if ($this->_type === false) { $this->_fetch(); }
if ($this->_type === false) { $this->_fetch(); }
return $this->_blockedTime;
if ($this->_type === false) { $this->_fetch(); }
if ($this->_type === false) { $this->_fetch(); }
return $this->_lastAttempt;
if ($this->_type === false) { $this->_fetch(); }
return $this->_blockedHits;
if ($this->_type === false) { $this->_fetch(); }
return $this->_expiration;
if ($this->_type === false) { $this->_fetch(); }
return $this->_parameters;
if ($this->type != self::TYPE_COUNTRY) { throw new OutOfBoundsException("{$key} is not a valid property for this block type"); }
return $this->parameters['blockLogin'];
if ($this->type != self::TYPE_COUNTRY) { throw new OutOfBoundsException("{$key} is not a valid property for this block type"); }
return $this->parameters['blockSite'];
if ($this->type != self::TYPE_COUNTRY) { throw new OutOfBoundsException("{$key} is not a valid property for this block type"); }
return $this->parameters['countries'];
if ($this->type != self::TYPE_PATTERN) { throw new OutOfBoundsException("{$key} is not a valid property for this block type"); }
return $this->parameters['ipRange'];
if ($this->type != self::TYPE_PATTERN) { throw new OutOfBoundsException("{$key} is not a valid property for this block type"); }
return $this->parameters['hostname'];
if ($this->type != self::TYPE_PATTERN) { throw new OutOfBoundsException("{$key} is not a valid property for this block type"); }
return $this->parameters['userAgent'];
if ($this->type != self::TYPE_PATTERN) { throw new OutOfBoundsException("{$key} is not a valid property for this block type"); }
return $this->parameters['referrer'];
throw new OutOfBoundsException("{$key} is not a valid property");
public function __isset($key) {
if ($this->_type === false) { $this->_fetch(); }
return !empty($this->_parameters);
if ($this->type != self::TYPE_COUNTRY) { return false; }
return !empty($this->parameters['blockLogin']);
if ($this->type != self::TYPE_COUNTRY) { return false; }
return !empty($this->parameters['blockSite']);
if ($this->type != self::TYPE_COUNTRY) { return false; }
return !empty($this->parameters['countries']);
if ($this->type != self::TYPE_PATTERN) { return false; }
return !empty($this->parameters['ipRange']);
if ($this->type != self::TYPE_PATTERN) { return false; }
return !empty($this->parameters['hostname']);
if ($this->type != self::TYPE_PATTERN) { return false; }
return !empty($this->parameters['userAgent']);
if ($this->type != self::TYPE_PATTERN) { return false; }
return !empty($this->parameters['referrer']);
* Fetches the record for the block from the database and populates the instance variables.
private function _fetch() {
$blocksTable = wfBlock::blocksTable();
$row = $wpdb->get_row($wpdb->prepare("SELECT * FROM `{$blocksTable}` WHERE `id` = %d", $this->id), ARRAY_A);
$this->_type = $row['type'];
if ($ip == self::MARKER_COUNTRY || $ip == self::MARKER_PATTERN) {
$this->_ip = wfUtils::inet_ntop($ip);
$this->_blockedTime = $row['blockedTime'];
$this->_reason = $row['reason'];
$this->_lastAttempt = $row['lastAttempt'];
$this->_blockedHits = $row['blockedHits'];
$this->_expiration = $row['expiration'];
$parameters = $row['parameters'];
if ($parameters === null) {
$this->_parameters = null;
$this->_parameters = @json_decode($parameters, true);
* Tests the block parameters against the given request. If matched, this will return the corresponding wfBlock::MATCH_
* constant. If not, it will return wfBlock::MATCH_NONE.
public function matchRequest($ip, $userAgent, $referrer) {
case self::TYPE_IP_MANUAL:
case self::TYPE_IP_AUTOMATIC_TEMPORARY:
case self::TYPE_IP_AUTOMATIC_PERMANENT:
case self::TYPE_WFSN_TEMPORARY:
case self::TYPE_RATE_BLOCK:
case self::TYPE_RATE_THROTTLE:
if (wfUtils::inet_pton($ip) == wfUtils::inet_pton($this->ip))
$match = (!empty($this->ipRange) || !empty($this->hostname) || !empty($this->userAgent) || !empty($this->referrer));
if (!empty($this->ipRange)) {
$range = new wfUserIPRange($this->ipRange);
$match = $match && $range->isIPInRange($ip);
if (!empty($this->hostname)) {
$hostname = wfUtils::reverseLookup($ip);
$match = $match && preg_match(wfUtils::patternToRegex($this->hostname), $hostname);
if (!empty($this->userAgent)) {
$match = $match && fnmatch($this->userAgent, $userAgent, FNM_CASEFOLD);
if (!empty($this->referrer)) {
$match = $match && fnmatch($this->referrer, $referrer, FNM_CASEFOLD);
return self::MATCH_PATTERN;
if (!wfConfig::get('isPaid')) {
//Bypass Redirect URL Hit
$bareRequestURI = wfUtils::extractBareURI($_SERVER['REQUEST_URI']);
$bareBypassRedirURI = wfUtils::extractBareURI(wfConfig::get('cbl_bypassRedirURL', ''));
if ($bareBypassRedirURI && $bareRequestURI == $bareBypassRedirURI) {
$bypassRedirDest = wfConfig::get('cbl_bypassRedirDest', '');
wfUtils::setcookie('wfCBLBypass', wfBlock::countryBlockingBypassCookieValue(), time() + (86400 * 365), '/', null, wfUtils::isFullSSL(), true);
return self::MATCH_COUNTRY_REDIR_BYPASS;
$bareBypassViewURI = wfUtils::extractBareURI(wfConfig::get('cbl_bypassViewURL', ''));
if ($bareBypassViewURI && $bareBypassViewURI == $bareRequestURI) {
wfUtils::setcookie('wfCBLBypass', wfBlock::countryBlockingBypassCookieValue(), time() + (86400 * 365), '/', null, wfUtils::isFullSSL(), true);
if ($this->_shouldBypassCountryBlocking()) { //Has valid bypass cookie
add_filter('authenticate', array($this, '_checkForBlockedCountryFilter'), 1, 1);
if (!$this->blockLogin && $this->_isAuthRequest()) { //Not blocking login and this is a login request
else if (!$this->blockSite && !$this->_isAuthRequest()) { //Not blocking site and this may be a site request
else if (is_user_logged_in() && !wfConfig::get('cbl_loggedInBlocked', false)) { //Not blocking logged in users and a login session exists
if ($this->blockSite && $this->blockLogin) {
return $this->_checkForBlockedCountry();
//Block the login form itself and any attempt to authenticate
if ($this->blockLogin && $this->_isAuthRequest()) {
return $this->_checkForBlockedCountry();
//Block requests that aren't to the login page, xmlrpc.php, or a user already logged in
if ($this->blockSite && !$this->_isAuthRequest() && !defined('XMLRPC_REQUEST')) {
return $this->_checkForBlockedCountry();
//XMLRPC is inaccesible when public portion of the site and auth is disabled
if ($this->blockLogin && $this->blockSite && defined('XMLRPC_REQUEST')) {
return $this->_checkForBlockedCountry();
* Returns whether or not the current request should be treated as an auth request.
private function _isAuthRequest() {
if ((strpos($_SERVER['REQUEST_URI'], '/wp-login.php') !== false)) {
* Tests whether or not the country blocking bypass cookie is set and valid.
private function _shouldBypassCountryBlocking() {
if (isset($_COOKIE['wfCBLBypass']) && $_COOKIE['wfCBLBypass'] == wfBlock::countryBlockingBypassCookieValue()) {
* Checks the country block against the requesting IP, returning the action to take.
private function _checkForBlockedCountry() {
$blockedCountries = $this->countries;
$bareRequestURI = untrailingslashit(wfUtils::extractBareURI($_SERVER['REQUEST_URI']));
if ($country = wfUtils::IP2Country($IP)) {
foreach ($blockedCountries as $blocked) {
if (strtoupper($blocked) == strtoupper($country)) { //At this point we know the user has been blocked
if (wfConfig::get('cbl_action') == 'redir') {
$redirURL = wfConfig::get('cbl_redirURL');
$eRedirHost = wfUtils::extractHostname($redirURL);
$isExternalRedir = false;
if ($eRedirHost && $eRedirHost != wfUtils::extractHostname(home_url())) { //It's an external redirect...
if ((!$isExternalRedir) && untrailingslashit(wfUtils::extractBareURI($redirURL)) == $bareRequestURI) { //Is this the URI we want to redirect to, then don't block it
return self::MATCH_COUNTRY_REDIR;
return self::MATCH_COUNTRY_BLOCK;
* Filter hook for the country blocking check. Does nothing if not blocked, otherwise presents the block page and exits.
* Note: Must remain `public` for callback to work.
public function _checkForBlockedCountryFilter($user) {
$block = $this->_checkForBlockedCountry();
if ($block == self::MATCH_NONE) {
$log->getCurrentRequest()->actionDescription = __('blocked access via country blocking', 'wordfence');
wfConfig::inc('totalCountryBlocked');
wfActivityReport::logBlockedIP(wfUtils::getIP(), null, 'country');
$log->do503(3600, __('Access from your area has been temporarily limited for security reasons', 'wordfence')); //exits
* Adds $quantity to the blocked count and sets the timestamp for lastAttempt.
* @param bool|int $timestamp
public function recordBlock($quantity = 1, $timestamp = false) {
if ($timestamp === false) {
$blocksTable = wfBlock::blocksTable();
$wpdb->query($wpdb->prepare("UPDATE `{$blocksTable}` SET `blockedHits` = `blockedHits` + %d, `lastAttempt` = GREATEST(`lastAttempt`, %d) WHERE `id` = %d", $quantity, $timestamp, $this->id));
$this->_type = false; //Trigger a re-fetch next access
* Returns an array suitable for JSON of the values needed to edit the block.
public function editValues() {
'blockLogin' => wfUtils::truthyToInt($this->blockLogin),
'blockSite' => wfUtils::truthyToInt($this->blockSite),
'countries' => $this->countries,
'reason' => $this->reason,
'expiration' => $this->expiration,