CodePress CMS v1.8.0: BotGuard security engine, HAProxy docs & ARIA fix

- Implement BotGuard security engine (Bot, AI, Scraper & Empty UA blocking)
- Add Admin Security page (/admin/security) with toggles, rate limiter & block/allowlists
- Add per-IP RateLimiter handoff in index.php with HTTP 429 response
- Add dynamic /robots.txt generation and noai/noimageai meta tags
- Add RequestLogger status column and blocked badges in admin request logs
- Fix ARIAComponents.php syntax errors on lines 67, 137, 262
- Add HAProxy / PFSense bot blocking & IP forwarding guide (docs/haproxy-bot-blocking.md)
- Update version to 1.8.0 with release notes in version.php and guides
This commit is contained in:
2026-07-29 14:26:48 +02:00
parent 5884877f18
commit 375a39c458
18 changed files with 618 additions and 117 deletions
+68 -9
View File
@@ -82,6 +82,13 @@ if (strpos($path, '/-assets/') === 0) {
exit;
}
// Serve dynamic robots.txt
if ($path === '/robots.txt') {
header('Content-Type: text/plain; charset=utf-8');
echo BotGuard::generateRobotsTxt($config['security'] ?? []);
exit;
}
// Block direct access to content files
if (strpos($path, '/content/') === 0) {
http_response_code(403);
@@ -89,16 +96,54 @@ if (strpos($path, '/content/') === 0) {
exit;
}
// Load RequestLogger for bot detection and request logging
// Load RequestLogger for IP & request logging
require_once __DIR__ . '/../cms/core/class/RequestLogger.php';
// Bot detection — block known bots/AI scrapers early
if (RequestLogger::detectBot() !== null) {
http_response_code(403);
echo '<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>403 Forbidden</title><meta name="robots" content="noindex,nofollow"></head><body><h1>403 Forbidden</h1><p>Access denied.</p></body></html>';
exit;
// Execute security checks & rate limiting
$clientIp = RequestLogger::getClientIp();
$secSettings = $config['security'] ?? [
'block_ai_bots' => true,
'block_scrapers' => true,
'block_search_engines' => false,
'block_empty_user_agent' => true,
'rate_limit_enabled' => true,
'rate_limit_max' => 60,
'rate_limit_window' => 60,
];
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
$isAllowedIp = in_array($clientIp, $secSettings['allowed_ips'] ?? [], true);
$requestStatus = 'ok';
if (!$isAllowedIp) {
// 1. IP Blocklist
if (in_array($clientIp, $secSettings['blocked_ips'] ?? [], true)) {
$requestStatus = 'blocked:ip';
}
// 2. BotGuard User-Agent check
if ($requestStatus === 'ok') {
$botBlockReason = BotGuard::shouldBlock($userAgent, $secSettings);
if ($botBlockReason !== null) {
$requestStatus = $botBlockReason;
}
}
// 3. Rate Limiting per IP
if ($requestStatus === 'ok' && !empty($secSettings['rate_limit_enabled'])) {
$cacheDir = dirname(__DIR__) . '/admin/storage/cache';
$rateLimiter = new RateLimiter(
(int)($secSettings['rate_limit_max'] ?? 60),
(int)($secSettings['rate_limit_window'] ?? 60),
new FileCache($cacheDir)
);
if (!$rateLimiter->isAllowed($clientIp)) {
$requestStatus = 'blocked:ratelimit';
}
}
}
// Instantiate CMS instance
$cms = new CodePressCMS($config);
// Log page view (not for media/assets)
@@ -112,12 +157,26 @@ if (!str_starts_with($path, '/-media/') && !str_starts_with($path, '/-assets/'))
$logger = new RequestLogger($requestLogFile);
$logger->log(
$_GET['page'] ?? $cms->getEffectiveDefaultPage(),
RequestLogger::getClientIp(),
$_SERVER['HTTP_USER_AGENT'] ?? '',
$clientIp,
$userAgent,
$_SERVER['HTTP_REFERER'] ?? '',
$loggedInUser,
$_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? ''
$_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '',
$requestStatus
);
}
// Block request if status is not ok
if ($requestStatus !== 'ok') {
if ($requestStatus === 'blocked:ratelimit') {
http_response_code(429);
header('Retry-After: ' . (int)($secSettings['rate_limit_window'] ?? 60));
echo '<!DOCTYPE html><html lang="nl"><head><meta charset="UTF-8"><title>429 Te Veel Verzoeken</title><meta name="robots" content="noindex,nofollow"></head><body><h1>429 Te Veel Verzoeken</h1><p>Probeer het over een minuut opnieuw.</p></body></html>';
} else {
http_response_code(403);
echo '<!DOCTYPE html><html lang="nl"><head><meta charset="UTF-8"><title>403 Toegang Geweigerd</title><meta name="robots" content="noindex,nofollow"></head><body><h1>403 Toegang Geweigerd</h1><p>Toegang geweigerd.</p></body></html>';
}
exit;
}
$cms->render();