feat: initial InformatiQ Toolkit plugin
Merges informatiq-wp-secure + informatiq-utils + HoneypotFields into
a single unified plugin with the following improvements:
- Fixed deactivation bug: all protection methods now guard themselves
with their own option check so toggling off via AJAX takes effect
immediately without any hook re-registration.
- Added rate-limiting for good/legitimate bots (Googlebot, Bingbot,
DuckDuckBot, Yandex, etc.) via transient sliding-window counters;
configurable per-bot limits in goodbots.conf (BotName|req/min);
returns HTTP 429 with Retry-After: 60 when over limit.
- Unified MySQL-backed logging (itk_bot_log + itk_honeypot_log tables)
replaces the old wp_options-based 100-entry cap.
- New Dashboard tab with terminal-style bot activity monitor: total
blocked, today's count, rate-limited hits, top threat sources
(bar chart), top IPs, top honeypot form types, active-module
status panel.
- All optimizations from utils.php merged into Optimization tab as
toggleable settings (was always-on before).
- Single admin page (Settings → InformatiQ Toolkit) with 8 tabs:
Dashboard | Bot Blocker | Protection | Optimization | Honeypot |
Bot Logs | Honeypot Logs | Config Files.
- Config file editor for badbots.conf, goodbots.conf, referrers.conf,
networks.conf, allowed-ips.conf with AJAX save and transient flush.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 11:45:26 +02:00
|
|
|
<?php
|
|
|
|
|
if (!defined('ABSPATH')) exit;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ITK Protection
|
|
|
|
|
*
|
|
|
|
|
* Handles wp-login protection, security headers, sensitive file blocking,
|
|
|
|
|
* malicious query blocking, and custom login URL.
|
|
|
|
|
*
|
|
|
|
|
* Deactivation bug fix: every method checks its own option key before acting.
|
|
|
|
|
* Hooks are always registered; guards live inside the callbacks.
|
|
|
|
|
*/
|
|
|
|
|
class ITK_Protection {
|
|
|
|
|
|
|
|
|
|
private array $allowed_ips = [];
|
|
|
|
|
private string $allowed_ips_file;
|
|
|
|
|
|
|
|
|
|
public function __construct() {
|
|
|
|
|
$this->allowed_ips_file = ITK_PATH . 'config/allowed-ips.conf';
|
|
|
|
|
$this->load_allowed_ips();
|
|
|
|
|
|
|
|
|
|
add_action('init', [$this, 'protect_wp_login'], 0);
|
|
|
|
|
add_action('init', [$this, 'block_sensitive_files'], 0);
|
|
|
|
|
add_action('init', [$this, 'block_malicious_queries'], 0);
|
|
|
|
|
add_action('init', [$this, 'block_author_scans'], 0);
|
|
|
|
|
add_action('init', [$this, 'custom_login_url'], 0);
|
|
|
|
|
add_action('send_headers', [$this, 'add_security_headers']);
|
|
|
|
|
add_action('wp_loaded', [$this, 'wp_loaded_custom_login']);
|
|
|
|
|
|
|
|
|
|
add_filter('the_generator', '__return_empty_string');
|
|
|
|
|
add_filter('wp_redirect', [$this, 'redirect_filter'], 10, 2);
|
|
|
|
|
add_filter('network_site_url', [$this, 'network_url_filter'], 10, 3);
|
|
|
|
|
add_filter('site_url', [$this, 'site_url_filter'], 10, 4);
|
|
|
|
|
add_filter('wp_handle_upload_prefilter', [$this, 'filter_uploaded_files']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── wp-login protection ──────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
public function protect_wp_login(): void {
|
2026-04-13 10:00:16 +02:00
|
|
|
if (ITK_Whitelist::allowed()) return;
|
feat: initial InformatiQ Toolkit plugin
Merges informatiq-wp-secure + informatiq-utils + HoneypotFields into
a single unified plugin with the following improvements:
- Fixed deactivation bug: all protection methods now guard themselves
with their own option check so toggling off via AJAX takes effect
immediately without any hook re-registration.
- Added rate-limiting for good/legitimate bots (Googlebot, Bingbot,
DuckDuckBot, Yandex, etc.) via transient sliding-window counters;
configurable per-bot limits in goodbots.conf (BotName|req/min);
returns HTTP 429 with Retry-After: 60 when over limit.
- Unified MySQL-backed logging (itk_bot_log + itk_honeypot_log tables)
replaces the old wp_options-based 100-entry cap.
- New Dashboard tab with terminal-style bot activity monitor: total
blocked, today's count, rate-limited hits, top threat sources
(bar chart), top IPs, top honeypot form types, active-module
status panel.
- All optimizations from utils.php merged into Optimization tab as
toggleable settings (was always-on before).
- Single admin page (Settings → InformatiQ Toolkit) with 8 tabs:
Dashboard | Bot Blocker | Protection | Optimization | Honeypot |
Bot Logs | Honeypot Logs | Config Files.
- Config file editor for badbots.conf, goodbots.conf, referrers.conf,
networks.conf, allowed-ips.conf with AJAX save and transient flush.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 11:45:26 +02:00
|
|
|
$options = get_option('itk_security', []);
|
|
|
|
|
if (empty($options['protect_wp_login'])) return;
|
|
|
|
|
|
|
|
|
|
$uri = $_SERVER['REQUEST_URI'] ?? '';
|
|
|
|
|
if (strpos($uri, 'wp-login.php') === false) return;
|
|
|
|
|
|
|
|
|
|
$ip = $this->get_client_ip();
|
|
|
|
|
$is_allowed = false;
|
|
|
|
|
foreach ($this->allowed_ips as $allowed) {
|
|
|
|
|
if ($ip === $allowed) { $is_allowed = true; break; }
|
|
|
|
|
if (strpos($allowed, '/') !== false && $this->ip_in_cidr($ip, $allowed)) {
|
|
|
|
|
$is_allowed = true; break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$is_allowed) {
|
|
|
|
|
$this->send_403($options, 'Access to login page not allowed from your IP address.');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
|
|
|
|
$protocol = $_SERVER['SERVER_PROTOCOL'] ?? '';
|
|
|
|
|
if (empty($ua) || $protocol === 'HTTP/1.0') {
|
|
|
|
|
$this->send_403($options, 'Invalid request detected.');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Security headers ─────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
public function add_security_headers(): void {
|
|
|
|
|
$options = get_option('itk_security', []);
|
|
|
|
|
if (empty($options['add_security_headers'])) return;
|
|
|
|
|
if (headers_sent()) return;
|
|
|
|
|
|
|
|
|
|
header_remove('X-Powered-By');
|
|
|
|
|
header('X-Content-Type-Options: nosniff');
|
|
|
|
|
header('X-Frame-Options: SAMEORIGIN');
|
|
|
|
|
header('X-XSS-Protection: 1; mode=block');
|
|
|
|
|
header('Referrer-Policy: strict-origin-when-cross-origin');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Sensitive file blocking ──────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
public function block_sensitive_files(): void {
|
2026-04-13 10:00:16 +02:00
|
|
|
if (ITK_Whitelist::allowed()) return;
|
feat: initial InformatiQ Toolkit plugin
Merges informatiq-wp-secure + informatiq-utils + HoneypotFields into
a single unified plugin with the following improvements:
- Fixed deactivation bug: all protection methods now guard themselves
with their own option check so toggling off via AJAX takes effect
immediately without any hook re-registration.
- Added rate-limiting for good/legitimate bots (Googlebot, Bingbot,
DuckDuckBot, Yandex, etc.) via transient sliding-window counters;
configurable per-bot limits in goodbots.conf (BotName|req/min);
returns HTTP 429 with Retry-After: 60 when over limit.
- Unified MySQL-backed logging (itk_bot_log + itk_honeypot_log tables)
replaces the old wp_options-based 100-entry cap.
- New Dashboard tab with terminal-style bot activity monitor: total
blocked, today's count, rate-limited hits, top threat sources
(bar chart), top IPs, top honeypot form types, active-module
status panel.
- All optimizations from utils.php merged into Optimization tab as
toggleable settings (was always-on before).
- Single admin page (Settings → InformatiQ Toolkit) with 8 tabs:
Dashboard | Bot Blocker | Protection | Optimization | Honeypot |
Bot Logs | Honeypot Logs | Config Files.
- Config file editor for badbots.conf, goodbots.conf, referrers.conf,
networks.conf, allowed-ips.conf with AJAX save and transient flush.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 11:45:26 +02:00
|
|
|
$options = get_option('itk_security', []);
|
|
|
|
|
$uri = $_SERVER['REQUEST_URI'] ?? '';
|
|
|
|
|
|
|
|
|
|
if (!empty($options['protect_wp_includes'])) {
|
|
|
|
|
if (preg_match('#^/wp-includes/[^/]+\.php$#i', $uri)) {
|
|
|
|
|
$this->send_403($options, 'Access to this file is not allowed.');
|
|
|
|
|
}
|
|
|
|
|
if (preg_match('#^/wp-admin/includes/#i', $uri)) {
|
|
|
|
|
$this->send_403($options, 'Access to this file is not allowed.');
|
|
|
|
|
}
|
|
|
|
|
if (preg_match('#^/wp-includes/theme-compat/#i', $uri)) {
|
|
|
|
|
$this->send_403($options, 'Access to this file is not allowed.');
|
|
|
|
|
}
|
|
|
|
|
if (preg_match('#/wp-includes/js/tinymce/langs/.+\.php#i', $uri)) {
|
|
|
|
|
$this->send_403($options, 'Access to this file is not allowed.');
|
|
|
|
|
}
|
|
|
|
|
if (preg_match('#(license\.txt|wp-config-sample\.php|readme\.html)$#i', $uri)) {
|
|
|
|
|
$this->send_403($options, 'Access to this file is not allowed.');
|
|
|
|
|
}
|
|
|
|
|
if (preg_match('#(?:^|/)\.(?!well-known)#', $uri)) {
|
|
|
|
|
$this->send_403($options, 'Access to hidden files is not allowed.');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!empty($options['protect_uploads'])) {
|
|
|
|
|
if (preg_match('#^/wp-content/uploads/.*\.(?:php[1-6]?|pht|phtml?)$#i', $uri)) {
|
|
|
|
|
$this->send_403($options, 'PHP files are not allowed in the uploads directory.');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!empty($options['block_xmlrpc'])) {
|
|
|
|
|
if (strpos($uri, 'xmlrpc.php') !== false) {
|
|
|
|
|
$this->send_403($options, 'XML-RPC is disabled on this site.');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Malicious query blocking ─────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
public function block_malicious_queries(): void {
|
2026-04-13 10:00:16 +02:00
|
|
|
if (ITK_Whitelist::allowed()) return;
|
feat: initial InformatiQ Toolkit plugin
Merges informatiq-wp-secure + informatiq-utils + HoneypotFields into
a single unified plugin with the following improvements:
- Fixed deactivation bug: all protection methods now guard themselves
with their own option check so toggling off via AJAX takes effect
immediately without any hook re-registration.
- Added rate-limiting for good/legitimate bots (Googlebot, Bingbot,
DuckDuckBot, Yandex, etc.) via transient sliding-window counters;
configurable per-bot limits in goodbots.conf (BotName|req/min);
returns HTTP 429 with Retry-After: 60 when over limit.
- Unified MySQL-backed logging (itk_bot_log + itk_honeypot_log tables)
replaces the old wp_options-based 100-entry cap.
- New Dashboard tab with terminal-style bot activity monitor: total
blocked, today's count, rate-limited hits, top threat sources
(bar chart), top IPs, top honeypot form types, active-module
status panel.
- All optimizations from utils.php merged into Optimization tab as
toggleable settings (was always-on before).
- Single admin page (Settings → InformatiQ Toolkit) with 8 tabs:
Dashboard | Bot Blocker | Protection | Optimization | Honeypot |
Bot Logs | Honeypot Logs | Config Files.
- Config file editor for badbots.conf, goodbots.conf, referrers.conf,
networks.conf, allowed-ips.conf with AJAX save and transient flush.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 11:45:26 +02:00
|
|
|
$options = get_option('itk_security', []);
|
|
|
|
|
if (empty($options['block_malicious_queries'])) return;
|
|
|
|
|
|
|
|
|
|
$qs = $_SERVER['QUERY_STRING'] ?? '';
|
|
|
|
|
if (empty($qs)) return;
|
|
|
|
|
|
|
|
|
|
$patterns = [
|
|
|
|
|
'(eval\()',
|
|
|
|
|
'(127\.0\.0\.1)',
|
|
|
|
|
'([a-z0-9]{2000})',
|
|
|
|
|
'(javascript:)(.*)(;)',
|
|
|
|
|
'(base64_encode)(.*)(\()',
|
|
|
|
|
'(GLOBALS|REQUEST)(=|\[|%)',
|
|
|
|
|
'(<|%3C)(.*)script(.*)(>|%3)',
|
|
|
|
|
'(boot\.ini|etc/passwd|self/environ)',
|
|
|
|
|
'(thumbs?(_editor|open)?|tim(thumb)?)\.php',
|
|
|
|
|
'(\'|\\")(.*)(drop|insert|md5|select|union)',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
foreach ($patterns as $pattern) {
|
|
|
|
|
if (preg_match('#' . $pattern . '#i', $qs)) {
|
|
|
|
|
$this->send_403($options, 'Malicious query detected.');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$method = strtolower($_SERVER['REQUEST_METHOD'] ?? '');
|
|
|
|
|
if (preg_match('#^(connect|debug|delete|move|put|trace|track)$#', $method)) {
|
|
|
|
|
$this->send_403($options, 'This request method is not allowed.');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Author scan blocking ─────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
public function block_author_scans(): void {
|
2026-04-13 10:00:16 +02:00
|
|
|
if (ITK_Whitelist::allowed()) return;
|
feat: initial InformatiQ Toolkit plugin
Merges informatiq-wp-secure + informatiq-utils + HoneypotFields into
a single unified plugin with the following improvements:
- Fixed deactivation bug: all protection methods now guard themselves
with their own option check so toggling off via AJAX takes effect
immediately without any hook re-registration.
- Added rate-limiting for good/legitimate bots (Googlebot, Bingbot,
DuckDuckBot, Yandex, etc.) via transient sliding-window counters;
configurable per-bot limits in goodbots.conf (BotName|req/min);
returns HTTP 429 with Retry-After: 60 when over limit.
- Unified MySQL-backed logging (itk_bot_log + itk_honeypot_log tables)
replaces the old wp_options-based 100-entry cap.
- New Dashboard tab with terminal-style bot activity monitor: total
blocked, today's count, rate-limited hits, top threat sources
(bar chart), top IPs, top honeypot form types, active-module
status panel.
- All optimizations from utils.php merged into Optimization tab as
toggleable settings (was always-on before).
- Single admin page (Settings → InformatiQ Toolkit) with 8 tabs:
Dashboard | Bot Blocker | Protection | Optimization | Honeypot |
Bot Logs | Honeypot Logs | Config Files.
- Config file editor for badbots.conf, goodbots.conf, referrers.conf,
networks.conf, allowed-ips.conf with AJAX save and transient flush.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 11:45:26 +02:00
|
|
|
$options = get_option('itk_security', []);
|
|
|
|
|
if (empty($options['block_author_scans'])) return;
|
|
|
|
|
|
|
|
|
|
$uri = $_SERVER['REQUEST_URI'] ?? '';
|
|
|
|
|
$qs = $_SERVER['QUERY_STRING'] ?? '';
|
|
|
|
|
|
|
|
|
|
if (strpos($uri, '/wp-admin') === false && preg_match('/author=\d+/i', $qs)) {
|
|
|
|
|
wp_redirect(home_url(), 301);
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Custom login URL ─────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
public function custom_login_url(): void {
|
|
|
|
|
$options = get_option('itk_security', []);
|
|
|
|
|
if (empty($options['enable_custom_login'])) return;
|
|
|
|
|
|
|
|
|
|
$slug = $this->custom_slug($options);
|
|
|
|
|
$path = parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH) ?? '';
|
|
|
|
|
$qs = parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_QUERY) ?? '';
|
|
|
|
|
|
|
|
|
|
if (strpos($path, '/' . $slug) !== false) {
|
|
|
|
|
if (!session_id()) session_start();
|
|
|
|
|
$_SESSION['itk_login_access'] = time();
|
|
|
|
|
require_once ABSPATH . 'wp-login.php';
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$blocked = ['/wp-login.php', '/wp-admin/', '/login/', '/admin/'];
|
|
|
|
|
foreach ($blocked as $b) {
|
|
|
|
|
if (strpos($path, $b) !== false) {
|
|
|
|
|
if (defined('DOING_AJAX') && DOING_AJAX) return;
|
|
|
|
|
if (!session_id()) session_start();
|
|
|
|
|
if (isset($_SESSION['itk_login_access']) && (time() - $_SESSION['itk_login_access']) < 300) return;
|
|
|
|
|
if (is_user_logged_in()) return;
|
|
|
|
|
$this->send_403($options, 'Access denied. Please use the correct login URL.');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function wp_loaded_custom_login(): void {
|
|
|
|
|
$options = get_option('itk_security', []);
|
|
|
|
|
if (empty($options['enable_custom_login'])) return;
|
|
|
|
|
|
|
|
|
|
global $pagenow;
|
|
|
|
|
if ($pagenow === 'wp-login.php') {
|
|
|
|
|
if (!session_id()) session_start();
|
|
|
|
|
if (!isset($_SESSION['itk_login_access'])) {
|
|
|
|
|
$this->send_403($options, 'Access denied. Please use the correct login URL.');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function redirect_filter(string $location, int $status): string {
|
|
|
|
|
$options = get_option('itk_security', []);
|
|
|
|
|
if (empty($options['enable_custom_login'])) return $location;
|
|
|
|
|
return str_replace('wp-login.php', $this->custom_slug($options), $location);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function network_url_filter(string $url, string $path): string {
|
|
|
|
|
$options = get_option('itk_security', []);
|
|
|
|
|
if (empty($options['enable_custom_login'])) return $url;
|
|
|
|
|
if (strpos($path, 'wp-login.php') !== false) {
|
|
|
|
|
return str_replace('wp-login.php', $this->custom_slug($options), $url);
|
|
|
|
|
}
|
|
|
|
|
return $url;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function site_url_filter(string $url, string $path): string {
|
|
|
|
|
$options = get_option('itk_security', []);
|
|
|
|
|
if (empty($options['enable_custom_login'])) return $url;
|
|
|
|
|
if (strpos($path, 'wp-login.php') !== false) {
|
|
|
|
|
return str_replace('wp-login.php', $this->custom_slug($options), $url);
|
|
|
|
|
}
|
|
|
|
|
return $url;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── File upload filter ───────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
public function filter_uploaded_files(array $file): array {
|
|
|
|
|
$options = get_option('itk_security', []);
|
|
|
|
|
if (empty($options['protect_uploads'])) return $file;
|
|
|
|
|
|
|
|
|
|
if (preg_match('/\.(php|phtml|php\d|pht|exe|dll|asp|aspx|jsp|cgi|pl)$/i', $file['name'] ?? '')) {
|
|
|
|
|
$file['error'] = 'PHP and executable files cannot be uploaded.';
|
|
|
|
|
}
|
|
|
|
|
return $file;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ── Helpers ──────────────────────────────────────────────── */
|
|
|
|
|
|
|
|
|
|
private function send_403(array $options, string $message): void {
|
|
|
|
|
$code = $options['response_code'] ?? '403';
|
|
|
|
|
$redir = $options['redirect_url'] ?? '';
|
|
|
|
|
if ($code === '301_custom' && !empty($redir)) {
|
|
|
|
|
header('Location: ' . esc_url_raw($redir), true, 301);
|
|
|
|
|
} else {
|
|
|
|
|
status_header(403);
|
|
|
|
|
echo esc_html($message);
|
|
|
|
|
}
|
|
|
|
|
exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function custom_slug(array $options): string {
|
|
|
|
|
return !empty($options['custom_login_slug']) ? $options['custom_login_slug'] : 'thoushallpass';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function load_allowed_ips(): void {
|
|
|
|
|
$defaults = ['127.0.0.1', '::1'];
|
|
|
|
|
if (file_exists($this->allowed_ips_file)) {
|
|
|
|
|
$lines = file($this->allowed_ips_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
|
|
|
foreach ($lines as $line) {
|
|
|
|
|
$line = trim($line);
|
|
|
|
|
if ($line !== '' && $line[0] !== '#') {
|
|
|
|
|
$this->allowed_ips[] = $line;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (empty($this->allowed_ips)) {
|
|
|
|
|
$this->allowed_ips = $defaults;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function get_client_ip(): string {
|
|
|
|
|
$keys = [
|
|
|
|
|
'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED',
|
|
|
|
|
'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED',
|
|
|
|
|
'REMOTE_ADDR',
|
|
|
|
|
];
|
|
|
|
|
foreach ($keys as $key) {
|
|
|
|
|
if (empty($_SERVER[$key])) continue;
|
|
|
|
|
$ip = trim(explode(',', $_SERVER[$key])[0]);
|
|
|
|
|
if (filter_var($ip, FILTER_VALIDATE_IP)) return $ip;
|
|
|
|
|
}
|
|
|
|
|
return 'UNKNOWN';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function ip_in_cidr(string $ip, string $cidr): bool {
|
|
|
|
|
[$subnet, $mask] = explode('/', $cidr, 2);
|
|
|
|
|
if (!filter_var($subnet, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) return false;
|
|
|
|
|
if (!is_numeric($mask) || $mask < 0 || $mask > 32) return false;
|
|
|
|
|
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) return false;
|
|
|
|
|
$mask_dec = ~((1 << (32 - (int)$mask)) - 1);
|
|
|
|
|
return (ip2long($ip) & $mask_dec) === (ip2long($subnet) & $mask_dec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function get_allowed_ips(): array { return $this->allowed_ips; }
|
|
|
|
|
public function get_allowed_ips_file(): string { return $this->allowed_ips_file; }
|
|
|
|
|
}
|