feat: global IP/CIDR/UA whitelist bypassing all restrictions
- class-itk-whitelist.php: static class with 5min transient cache, supports exact IP, CIDR notation, and ua: prefix for UA substrings - config/whitelist.conf: editable config file (template with examples) - whitelist check added to bot-blocker, WAF, protection (4 methods), and honeypot validator — matched requests skip all ITK enforcement - admin: whitelist.conf added to Config Files editor tab Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
14
config/whitelist.conf
Normal file
14
config/whitelist.conf
Normal file
@@ -0,0 +1,14 @@
|
||||
# InformatiQ Toolkit – Global IP/UA Whitelist
|
||||
# Entries in this file bypass ALL plugin restrictions (bot blocker, WAF, honeypot, protection).
|
||||
#
|
||||
# IP/CIDR entries (one per line):
|
||||
# 1.2.3.4
|
||||
# 10.0.0.0/8
|
||||
# 192.168.1.0/24
|
||||
#
|
||||
# User-Agent substring entries (prefix with ua:):
|
||||
# ua:Googlebot
|
||||
# ua:Bingbot
|
||||
# ua:MyMonitoringTool
|
||||
#
|
||||
# Lines starting with # are comments. Blank lines are ignored.
|
||||
@@ -285,6 +285,7 @@ class ITK_Admin {
|
||||
'referrers' => ITK_PATH . 'config/referrers.conf',
|
||||
'networks' => ITK_PATH . 'config/networks.conf',
|
||||
'allowed-ips'=> ITK_PATH . 'config/allowed-ips.conf',
|
||||
'whitelist' => ITK_PATH . 'config/whitelist.conf',
|
||||
];
|
||||
|
||||
if (!isset($allowed[$file])) wp_send_json_error('invalid file');
|
||||
@@ -299,6 +300,7 @@ class ITK_Admin {
|
||||
delete_transient('itk_referrers_list');
|
||||
delete_transient('itk_networks_list');
|
||||
delete_transient('itk_goodbots_list');
|
||||
ITK_Whitelist::invalidate_cache();
|
||||
|
||||
wp_send_json_success();
|
||||
}
|
||||
@@ -1395,6 +1397,7 @@ class ITK_Admin {
|
||||
'referrers' => ['Bad Referrers', 'config/referrers.conf', 'One domain substring per line.'],
|
||||
'networks' => ['Bad Networks', 'config/networks.conf', 'One IP or CIDR range per line (e.g. 1.2.3.0/24).'],
|
||||
'allowed-ips' => ['Allowed IPs', 'config/allowed-ips.conf','IPs/CIDRs allowed to access wp-login.php (one per line).'],
|
||||
'whitelist' => ['Whitelist', 'config/whitelist.conf', 'IPs, CIDRs, or UA substrings (ua:...) that bypass all restrictions. One per line.'],
|
||||
];
|
||||
|
||||
$active_file = sanitize_key($_GET['file'] ?? 'badbots');
|
||||
|
||||
@@ -37,6 +37,9 @@ class ITK_Bot_Blocker {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip all checks for whitelisted IPs/UAs.
|
||||
if (ITK_Whitelist::allowed()) return;
|
||||
|
||||
$options = get_option('itk_security', []);
|
||||
$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||
$referrer = $_SERVER['HTTP_REFERER'] ?? '';
|
||||
|
||||
@@ -146,6 +146,9 @@ class ITK_Honeypot {
|
||||
/* ── Validators ───────────────────────────────────────────── */
|
||||
|
||||
private function check_honeypot(string $form_type): bool {
|
||||
// Whitelisted IPs/UAs always pass honeypot validation.
|
||||
if (ITK_Whitelist::allowed()) return true;
|
||||
|
||||
// 1. Honeypot field must be empty
|
||||
foreach ($_POST as $key => $val) {
|
||||
if (strpos($key, self::FIELD_PREFIX) === 0 && !empty($val)) {
|
||||
|
||||
@@ -37,6 +37,7 @@ class ITK_Protection {
|
||||
/* ── wp-login protection ──────────────────────────────────── */
|
||||
|
||||
public function protect_wp_login(): void {
|
||||
if (ITK_Whitelist::allowed()) return;
|
||||
$options = get_option('itk_security', []);
|
||||
if (empty($options['protect_wp_login'])) return;
|
||||
|
||||
@@ -80,6 +81,7 @@ class ITK_Protection {
|
||||
/* ── Sensitive file blocking ──────────────────────────────── */
|
||||
|
||||
public function block_sensitive_files(): void {
|
||||
if (ITK_Whitelist::allowed()) return;
|
||||
$options = get_option('itk_security', []);
|
||||
$uri = $_SERVER['REQUEST_URI'] ?? '';
|
||||
|
||||
@@ -120,6 +122,7 @@ class ITK_Protection {
|
||||
/* ── Malicious query blocking ─────────────────────────────── */
|
||||
|
||||
public function block_malicious_queries(): void {
|
||||
if (ITK_Whitelist::allowed()) return;
|
||||
$options = get_option('itk_security', []);
|
||||
if (empty($options['block_malicious_queries'])) return;
|
||||
|
||||
@@ -154,6 +157,7 @@ class ITK_Protection {
|
||||
/* ── Author scan blocking ─────────────────────────────────── */
|
||||
|
||||
public function block_author_scans(): void {
|
||||
if (ITK_Whitelist::allowed()) return;
|
||||
$options = get_option('itk_security', []);
|
||||
if (empty($options['block_author_scans'])) return;
|
||||
|
||||
|
||||
@@ -54,6 +54,9 @@ class ITK_WAF {
|
||||
if (is_admin() && !wp_doing_ajax()) return;
|
||||
if (function_exists('current_user_can') && current_user_can('manage_options')) return;
|
||||
|
||||
// Skip all WAF checks for whitelisted IPs/UAs.
|
||||
if (ITK_Whitelist::allowed()) return;
|
||||
|
||||
$options = get_option('itk_waf', []);
|
||||
$rules = $this->load_rules();
|
||||
if (empty($rules)) return;
|
||||
|
||||
90
includes/class-itk-whitelist.php
Normal file
90
includes/class-itk-whitelist.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
/**
|
||||
* ITK Whitelist
|
||||
*
|
||||
* Loads config/whitelist.conf and provides a fast static check.
|
||||
* Any IP/CIDR or UA substring match bypasses all ITK restrictions.
|
||||
*/
|
||||
class ITK_Whitelist {
|
||||
|
||||
private static ?bool $result = null;
|
||||
|
||||
/**
|
||||
* Returns true if the current request is whitelisted.
|
||||
* Result is cached for the lifetime of the request.
|
||||
*/
|
||||
public static function allowed(): bool {
|
||||
if (self::$result !== null) return self::$result;
|
||||
self::$result = self::check();
|
||||
return self::$result;
|
||||
}
|
||||
|
||||
private static function check(): bool {
|
||||
$ip = self::get_ip();
|
||||
$ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
|
||||
|
||||
$entries = self::load();
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
if (str_starts_with($entry, 'ua:')) {
|
||||
$needle = substr($entry, 3);
|
||||
if ($needle !== '' && stripos($ua, $needle) !== false) return true;
|
||||
} elseif (str_contains($entry, '/')) {
|
||||
if (self::ip_in_cidr($ip, $entry)) return true;
|
||||
} else {
|
||||
if ($ip === $entry) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static function load(): array {
|
||||
$transient = 'itk_whitelist';
|
||||
$cached = get_transient($transient);
|
||||
if ($cached !== false) return $cached;
|
||||
|
||||
$file = ITK_PATH . 'config/whitelist.conf';
|
||||
$entries = [];
|
||||
|
||||
if (file_exists($file)) {
|
||||
foreach (file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
|
||||
$line = trim($line);
|
||||
if ($line === '' || $line[0] === '#') continue;
|
||||
$entries[] = $line;
|
||||
}
|
||||
}
|
||||
|
||||
set_transient($transient, $entries, 300); // 5-minute cache
|
||||
return $entries;
|
||||
}
|
||||
|
||||
public static function invalidate_cache(): void {
|
||||
delete_transient('itk_whitelist');
|
||||
self::$result = null;
|
||||
}
|
||||
|
||||
private static function get_ip(): string {
|
||||
// Prefer X-Forwarded-For first header (same logic as bot blocker)
|
||||
if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
||||
return trim(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0]);
|
||||
}
|
||||
return $_SERVER['REMOTE_ADDR'] ?? '';
|
||||
}
|
||||
|
||||
private static function ip_in_cidr(string $ip, string $cidr): bool {
|
||||
[$subnet, $bits] = explode('/', $cidr, 2);
|
||||
$bits = (int)$bits;
|
||||
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) &&
|
||||
filter_var($subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
|
||||
$ip_long = ip2long($ip);
|
||||
$sub_long = ip2long($subnet);
|
||||
if ($ip_long === false || $sub_long === false) return false;
|
||||
$mask = $bits === 0 ? 0 : (~0 << (32 - $bits));
|
||||
return ($ip_long & $mask) === ($sub_long & $mask);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ require_once ITK_PATH . 'includes/class-itk-database.php';
|
||||
require_once ITK_PATH . 'includes/class-itk-hp-api.php';
|
||||
require_once ITK_PATH . 'includes/class-itk-bot-api.php';
|
||||
require_once ITK_PATH . 'includes/class-itk-attacks-api.php';
|
||||
require_once ITK_PATH . 'includes/class-itk-whitelist.php';
|
||||
require_once ITK_PATH . 'includes/class-itk-bot-blocker.php';
|
||||
require_once ITK_PATH . 'includes/class-itk-protection.php';
|
||||
require_once ITK_PATH . 'includes/class-itk-optimization.php';
|
||||
|
||||
Reference in New Issue
Block a user