![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/soundstudiopro.com/private_html/includes/ |
<?php
/**
* SHIELD PROTECTION MIDDLEWARE
*
* Include this file at the top of your main entry points (index.php, etc.)
* to enable bot protection screening.
*
* Usage: require_once 'includes/shield.php';
*
* If Shield is disabled, this file does nothing.
*/
// Load config (only if not already loaded)
if (!defined('SHIELD_ENABLED')) {
require_once __DIR__ . '/../config/shield_config.php';
}
// Start session if not started (and headers not sent)
if (session_status() === PHP_SESSION_NONE && !headers_sent()) {
session_start();
}
/**
* Main Shield check - call this to protect a page
*/
function shield_protect() {
global $SHIELD_BOT_WHITELIST, $SHIELD_IP_WHITELIST, $SHIELD_SKIP_PATHS, $SHIELD_SKIP_EXTENSIONS;
// Check if Shield is enabled
if (!shield_is_enabled()) {
return true; // Shield disabled, allow all
}
$request_uri = $_SERVER['REQUEST_URI'] ?? '/';
$request_path = parse_url($request_uri, PHP_URL_PATH);
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
$user_agent = $_SERVER['HTTP_USER_AGENT'] ?? '';
// Skip static files
$extension = strtolower(pathinfo($request_path, PATHINFO_EXTENSION));
if (in_array($extension, $SHIELD_SKIP_EXTENSIONS)) {
return true;
}
// Skip configured paths
foreach ($SHIELD_SKIP_PATHS as $skip_path) {
if (strpos($request_path, $skip_path) === 0) {
return true;
}
}
// Check IP whitelist
if (shield_is_ip_whitelisted($ip, $SHIELD_IP_WHITELIST)) {
return true;
}
// Whitelist speed test and monitoring tools (GTmetrix, Lighthouse, etc.)
$speed_test_bots = ['GTmetrix', 'PageSpeed', 'Lighthouse', 'PTST', 'Chrome-Lighthouse',
'pingdom', 'uptimerobot', 'StatusCake', 'Site24x7', 'Datadog',
'WebPageTest', 'YSlow', 'DareBoost'];
foreach ($speed_test_bots as $bot) {
if (stripos($user_agent, $bot) !== false) {
shield_log('speed_test_allowed', $ip, ['bot' => $bot, 'action' => 'bypassed']);
return true;
}
}
// Check for verified search engine bots
if (shield_is_verified_bot($ip, $user_agent, $SHIELD_BOT_WHITELIST)) {
shield_log('bot_allowed', $ip, ['bot' => 'verified_search_engine', 'ua' => $user_agent]);
return true;
}
// Check for valid verification cookie
if (shield_has_valid_cookie()) {
return true;
}
// Check rate limit for challenges
if (!shield_check_rate_limit($ip)) {
shield_log('rate_limited', $ip, ['action' => 'blocked']);
http_response_code(429);
die('Too many requests. Please try again later.');
}
// Redirect to challenge
shield_redirect_to_challenge($request_uri);
exit;
}
/**
* Check if IP is in whitelist (supports CIDR notation)
*/
function shield_is_ip_whitelisted($ip, $whitelist) {
foreach ($whitelist as $allowed) {
if (strpos($allowed, '/') !== false) {
// CIDR notation
if (shield_ip_in_cidr($ip, $allowed)) {
return true;
}
} else {
if ($ip === $allowed) {
return true;
}
}
}
return false;
}
/**
* Check if IP is in CIDR range
*/
function shield_ip_in_cidr($ip, $cidr) {
list($subnet, $mask) = explode('/', $cidr);
$ip_long = ip2long($ip);
$subnet_long = ip2long($subnet);
$mask_long = -1 << (32 - $mask);
return ($ip_long & $mask_long) === ($subnet_long & $mask_long);
}
/**
* Verify if a bot claiming to be a search engine is legitimate
* Uses reverse DNS verification
*/
function shield_is_verified_bot($ip, $user_agent, $whitelist) {
foreach ($whitelist as $bot_name => $config) {
// Check if user agent contains the bot identifier
if (stripos($user_agent, $config['ua_contains']) === false) {
continue;
}
// Do reverse DNS lookup
$hostname = gethostbyaddr($ip);
if ($hostname === $ip) {
// DNS lookup failed - not verified
shield_log('bot_unverified', $ip, ['claimed' => $bot_name, 'reason' => 'dns_failed']);
return false;
}
// Check if hostname ends with allowed suffix
$verified = false;
foreach ($config['dns_suffix'] as $suffix) {
if (substr($hostname, -strlen($suffix)) === $suffix) {
// Forward DNS to verify
$forward_ip = gethostbyname($hostname);
if ($forward_ip === $ip) {
$verified = true;
break;
}
}
}
if ($verified) {
return true;
} else {
shield_log('bot_unverified', $ip, [
'claimed' => $bot_name,
'hostname' => $hostname,
'reason' => 'dns_mismatch'
]);
return false;
}
}
return false;
}
/**
* Check if visitor has valid verification cookie
*/
function shield_has_valid_cookie() {
$cookie_name = 'shield_verified';
if (!isset($_COOKIE[$cookie_name])) {
return false;
}
$token = $_COOKIE[$cookie_name];
// Parse token
$parts = explode('.', $token);
if (count($parts) !== 3) {
return false;
}
list($payload_b64, $expires, $signature) = $parts;
// Check expiration
if (time() > (int)$expires) {
return false;
}
// Verify signature
$expected_sig = hash_hmac('sha256', $payload_b64 . '.' . $expires, SHIELD_SECRET_KEY);
if (!hash_equals($expected_sig, $signature)) {
return false;
}
// Optionally verify payload matches current visitor
$payload = json_decode(base64_decode($payload_b64), true);
if (!$payload) {
return false;
}
// Loose IP check (first 3 octets) to allow for dynamic IPs within ISP
$current_ip = $_SERVER['REMOTE_ADDR'] ?? '';
$token_ip = $payload['ip'] ?? '';
$current_prefix = implode('.', array_slice(explode('.', $current_ip), 0, 3));
$token_prefix = implode('.', array_slice(explode('.', $token_ip), 0, 3));
if ($current_prefix !== $token_prefix) {
shield_log('cookie_ip_mismatch', $current_ip, [
'token_ip' => $token_ip
]);
return false;
}
return true;
}
/**
* Generate verification cookie
*/
function shield_generate_cookie($ip, $fingerprint = '') {
$payload = [
'ip' => $ip,
'fp' => substr(hash('sha256', $fingerprint), 0, 16),
'ts' => time()
];
$payload_b64 = base64_encode(json_encode($payload));
$expires = time() + SHIELD_COOKIE_LIFETIME;
$signature = hash_hmac('sha256', $payload_b64 . '.' . $expires, SHIELD_SECRET_KEY);
$token = $payload_b64 . '.' . $expires . '.' . $signature;
setcookie('shield_verified', $token, [
'expires' => $expires,
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax'
]);
return $token;
}
/**
* Check rate limit for challenges
*/
function shield_check_rate_limit($ip) {
$rate_dir = __DIR__ . '/../cache/shield_rates/';
if (!is_dir($rate_dir)) {
@mkdir($rate_dir, 0755, true);
}
$key = hash('sha256', $ip);
$file = $rate_dir . $key . '.json';
$window = 3600; // 1 hour
$limit = SHIELD_RATE_LIMIT;
$data = ['count' => 0, 'reset_time' => time() + $window];
if (file_exists($file)) {
$content = @file_get_contents($file);
if ($content) {
$existing = json_decode($content, true);
if ($existing && isset($existing['reset_time'])) {
if (time() > $existing['reset_time']) {
$data = ['count' => 0, 'reset_time' => time() + $window];
} else {
$data = $existing;
}
}
}
}
if ($data['count'] >= $limit) {
return false;
}
$data['count']++;
@file_put_contents($file, json_encode($data), LOCK_EX);
return true;
}
/**
* Redirect to challenge page
*/
function shield_redirect_to_challenge($return_url) {
$challenge_token = bin2hex(random_bytes(16));
$challenge_time = time();
// Store challenge in session
$_SESSION['shield_challenge'] = [
'token' => $challenge_token,
'time' => $challenge_time,
'return_url' => $return_url
];
// Redirect to challenge page
$challenge_url = '/shield_challenge.php?t=' . $challenge_token;
header('Location: ' . $challenge_url);
exit;
}
/**
* Log Shield events with enhanced tracking
*/
function shield_log($event, $ip, $data = []) {
$log_dir = __DIR__ . '/../logs/';
if (!is_dir($log_dir)) {
@mkdir($log_dir, 0755, true);
}
$log_file = $log_dir . 'shield_' . date('Y-m-d') . '.log';
// Enhanced log entry with more context
$log_entry = [
'time' => date('Y-m-d H:i:s'),
'timestamp' => time(),
'event' => $event,
'ip' => $ip,
'user_agent' => substr($_SERVER['HTTP_USER_AGENT'] ?? 'unknown', 0, 500),
'request_uri' => $_SERVER['REQUEST_URI'] ?? '/',
'method' => $_SERVER['REQUEST_METHOD'] ?? 'GET',
'data' => $data
];
@file_put_contents($log_file, json_encode($log_entry) . "\n", FILE_APPEND | LOCK_EX);
// Also log to database for easier querying (if table exists)
shield_log_to_db($log_entry);
}
/**
* Get IP intelligence (geolocation, organization, etc.)
* Uses ip-api.com (free, 45 requests/minute)
*/
function shield_get_ip_info($ip) {
// Check cache first
$cache_dir = __DIR__ . '/../cache/ip_info/';
if (!is_dir($cache_dir)) {
@mkdir($cache_dir, 0755, true);
}
$cache_file = $cache_dir . md5($ip) . '.json';
// Cache for 24 hours
if (file_exists($cache_file) && (time() - filemtime($cache_file)) < 86400) {
$cached = json_decode(file_get_contents($cache_file), true);
if ($cached) return $cached;
}
// Query ip-api.com (free, no API key needed)
$url = "http://ip-api.com/json/" . urlencode($ip) . "?fields=status,message,country,countryCode,region,regionName,city,zip,lat,lon,timezone,isp,org,as,asname,reverse,mobile,proxy,hosting,query";
$context = stream_context_create([
'http' => [
'timeout' => 3,
'ignore_errors' => true
]
]);
$response = @file_get_contents($url, false, $context);
if ($response) {
$data = json_decode($response, true);
if ($data && isset($data['status']) && $data['status'] === 'success') {
// Enhance with our own analysis
$data['is_datacenter'] = $data['hosting'] ?? false;
$data['is_vpn_proxy'] = $data['proxy'] ?? false;
$data['is_mobile'] = $data['mobile'] ?? false;
// Check if it's a known search engine
$data['is_search_engine'] = false;
$hostname = $data['reverse'] ?? '';
$org = strtolower($data['org'] ?? '');
$isp = strtolower($data['isp'] ?? '');
if (strpos($hostname, 'googlebot.com') !== false || strpos($hostname, 'google.com') !== false) {
$data['is_search_engine'] = true;
$data['search_engine'] = 'Google';
} elseif (strpos($hostname, 'search.msn.com') !== false || strpos($org, 'microsoft') !== false) {
$data['is_search_engine'] = true;
$data['search_engine'] = 'Bing';
} elseif (strpos($hostname, 'yandex') !== false) {
$data['is_search_engine'] = true;
$data['search_engine'] = 'Yandex';
} elseif (strpos($org, 'facebook') !== false || strpos($isp, 'facebook') !== false) {
$data['is_search_engine'] = true;
$data['search_engine'] = 'Facebook';
} elseif (strpos($org, 'twitter') !== false) {
$data['is_search_engine'] = true;
$data['search_engine'] = 'Twitter';
}
// Cache the result
@file_put_contents($cache_file, json_encode($data));
return $data;
}
}
// Fallback: just do reverse DNS
$hostname = @gethostbyaddr($ip);
return [
'status' => 'partial',
'query' => $ip,
'reverse' => ($hostname !== $ip) ? $hostname : null,
'country' => 'Unknown',
'countryCode' => '??',
'city' => 'Unknown',
'isp' => 'Unknown',
'org' => 'Unknown'
];
}
/**
* Log to database for persistent tracking
*/
function shield_log_to_db($entry) {
try {
require_once __DIR__ . '/../config/database.php';
$pdo = getDBConnection();
if (!$pdo) return;
// Create table if not exists (with IP info columns)
$pdo->exec("CREATE TABLE IF NOT EXISTS shield_logs (
id INT AUTO_INCREMENT PRIMARY KEY,
event_time DATETIME NOT NULL,
event_type VARCHAR(50) NOT NULL,
ip_address VARCHAR(45) NOT NULL,
user_agent TEXT,
request_uri VARCHAR(500),
request_method VARCHAR(10),
event_data JSON,
is_bot TINYINT(1) DEFAULT 0,
bot_name VARCHAR(50),
is_threat TINYINT(1) DEFAULT 0,
country VARCHAR(100),
country_code VARCHAR(5),
city VARCHAR(100),
isp VARCHAR(255),
org VARCHAR(255),
hostname VARCHAR(255),
is_datacenter TINYINT(1) DEFAULT 0,
is_vpn_proxy TINYINT(1) DEFAULT 0,
is_search_engine TINYINT(1) DEFAULT 0,
search_engine_name VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_event_time (event_time),
INDEX idx_event_type (event_type),
INDEX idx_ip (ip_address),
INDEX idx_is_bot (is_bot),
INDEX idx_is_threat (is_threat),
INDEX idx_country (country_code),
INDEX idx_is_datacenter (is_datacenter)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
// Add new columns if they don't exist (for existing tables)
try {
$pdo->exec("ALTER TABLE shield_logs ADD COLUMN IF NOT EXISTS country VARCHAR(100)");
$pdo->exec("ALTER TABLE shield_logs ADD COLUMN IF NOT EXISTS country_code VARCHAR(5)");
$pdo->exec("ALTER TABLE shield_logs ADD COLUMN IF NOT EXISTS city VARCHAR(100)");
$pdo->exec("ALTER TABLE shield_logs ADD COLUMN IF NOT EXISTS isp VARCHAR(255)");
$pdo->exec("ALTER TABLE shield_logs ADD COLUMN IF NOT EXISTS org VARCHAR(255)");
$pdo->exec("ALTER TABLE shield_logs ADD COLUMN IF NOT EXISTS hostname VARCHAR(255)");
$pdo->exec("ALTER TABLE shield_logs ADD COLUMN IF NOT EXISTS is_datacenter TINYINT(1) DEFAULT 0");
$pdo->exec("ALTER TABLE shield_logs ADD COLUMN IF NOT EXISTS is_vpn_proxy TINYINT(1) DEFAULT 0");
$pdo->exec("ALTER TABLE shield_logs ADD COLUMN IF NOT EXISTS is_search_engine TINYINT(1) DEFAULT 0");
$pdo->exec("ALTER TABLE shield_logs ADD COLUMN IF NOT EXISTS search_engine_name VARCHAR(50)");
} catch (Exception $e) {
// Columns might already exist
}
// Get IP intelligence
$ip_info = shield_get_ip_info($entry['ip']);
// Determine if this is a bot and if it's a threat
$is_bot = 0;
$bot_name = null;
$is_threat = 0;
$ua = $entry['user_agent'] ?? '';
// Check for known good bots
$good_bots = ['Googlebot', 'bingbot', 'DuckDuckBot', 'YandexBot', 'Baiduspider',
'facebookexternalhit', 'Twitterbot', 'LinkedInBot', 'Slackbot'];
foreach ($good_bots as $bot) {
if (stripos($ua, $bot) !== false) {
$is_bot = 1;
$bot_name = $bot;
break;
}
}
// Check for suspicious/threat indicators
$threat_indicators = ['verify_too_fast', 'bot_unverified', 'rate_limited',
'blocked', 'bot_indicators_detected'];
if (in_array($entry['event'], $threat_indicators)) {
$is_threat = 1;
}
// Check for bad bot patterns
$bad_patterns = ['python-requests', 'curl/', 'wget/', 'scrapy', 'HeadlessChrome',
'PhantomJS', 'selenium', 'puppeteer'];
foreach ($bad_patterns as $pattern) {
if (stripos($ua, $pattern) !== false) {
$is_threat = 1;
$is_bot = 1;
$bot_name = $pattern;
break;
}
}
$stmt = $pdo->prepare("
INSERT INTO shield_logs
(event_time, event_type, ip_address, user_agent, request_uri, request_method, event_data,
is_bot, bot_name, is_threat, country, country_code, city, isp, org, hostname,
is_datacenter, is_vpn_proxy, is_search_engine, search_engine_name)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$stmt->execute([
$entry['time'],
$entry['event'],
$entry['ip'],
$entry['user_agent'] ?? null,
$entry['request_uri'] ?? null,
$entry['method'] ?? null,
json_encode($entry['data']),
$is_bot,
$bot_name,
$is_threat,
$ip_info['country'] ?? null,
$ip_info['countryCode'] ?? null,
$ip_info['city'] ?? null,
$ip_info['isp'] ?? null,
$ip_info['org'] ?? null,
$ip_info['reverse'] ?? null,
$ip_info['is_datacenter'] ?? 0,
$ip_info['is_vpn_proxy'] ?? 0,
$ip_info['is_search_engine'] ?? 0,
$ip_info['search_engine'] ?? null
]);
} catch (Exception $e) {
// Silently fail - don't break the site if logging fails
error_log("Shield DB log error: " . $e->getMessage());
}
}
/**
* Get Shield statistics
*/
function shield_get_stats() {
$log_dir = __DIR__ . '/../logs/';
$stats = [
'challenges_today' => 0,
'verified_today' => 0,
'blocked_today' => 0,
'bots_allowed' => 0,
'bots_blocked' => 0
];
$log_file = $log_dir . 'shield_' . date('Y-m-d') . '.log';
if (file_exists($log_file)) {
$lines = file($log_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
$entry = json_decode($line, true);
if (!$entry) continue;
switch ($entry['event'] ?? '') {
case 'challenge_served':
$stats['challenges_today']++;
break;
case 'verified':
$stats['verified_today']++;
break;
case 'blocked':
case 'rate_limited':
$stats['blocked_today']++;
break;
case 'bot_allowed':
$stats['bots_allowed']++;
break;
case 'bot_unverified':
$stats['bots_blocked']++;
break;
}
}
}
return $stats;
}