Skip to content

A lightweight, file-based PHP 8 firewall — rate limiting, bot detection, payload scanning, and IP banning with zero dependencies.

License

Notifications You must be signed in to change notification settings

webrium/xzeroprotect

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation


██╗  ██╗███████╗███████╗██████╗  ██████╗ ██████╗ ██████╗  ██████╗ ████████╗███████╗ ██████╗████████╗
╚██╗██╔╝╚══███╔╝██╔════╝██╔══██╗██╔═══██╗██╔══██╗██╔══██╗██╔═══██╗╚══██╔══╝██╔════╝██╔════╝╚══██╔══╝
 ╚███╔╝   ███╔╝ █████╗  ██████╔╝██║   ██║██████╔╝██████╔╝██║   ██║   ██║   █████╗  ██║        ██║   
 ██╔██╗  ███╔╝  ██╔══╝  ██╔══██╗██║   ██║██╔═══╝ ██╔══██╗██║   ██║   ██║   ██╔══╝  ██║        ██║   
██╔╝ ██╗███████╗███████╗██║  ██║╚██████╔╝██║      ██║  ██║╚██████╔╝   ██║   ███████╗╚██████╗   ██║   
╚═╝  ╚═╝╚══════╝╚══════╝╚═╝  ╚═╝ ╚═════╝ ╚═╝     ╚═╝  ╚═╝ ╚═════╝    ╚═╝   ╚══════╝ ╚═════╝   ╚═╝   

A lightweight, file-based PHP firewall for the modern web.
No database. No external services. No compromises.


PHP Composer License Zero Dependencies



Why xZeroProtect?

Every day, bots crawl your application looking for exposed .env files, WordPress admin panels, SQL injection vectors, and known CVEs — even if you're not running WordPress. xZeroProtect stops them at the PHP layer with zero external dependencies, no database connection, and a clean API you can tune in minutes.

  • File-based — everything stored on disk; no MySQL, Redis, or memcached required
  • Zero dependencies — pure PHP 8.0+, nothing else
  • Composable — enable, disable, or extend every detection module independently
  • Learning mode — log threats without blocking, perfect for tuning before going live
  • Apache-aware — optionally write permanent bans into .htaccess so Apache rejects them before PHP even starts

Installation

composer require webrium/xzeroprotect

Quick Start

Add these two lines at the very top of your index.php or bootstrap file:

<?php
require 'vendor/autoload.php';

use Webrium\XZeroProtect\XZeroProtect;

XZeroProtect::init()->run();

That's it. Default rules are active immediately.

Note: init() stores the instance internally. You can retrieve it from anywhere in your application using XZeroProtect::getInstance() — no need to pass $firewall around.


Configuration

Every option has a sensible default. Override only what you need:

$firewall = XZeroProtect::init([

    // 'production' → block & log  |  'learning' → log only  |  'off' → disabled
    'mode'         => 'production',

    // Where ban files, rate data, and logs are stored
    'storage_path' => __DIR__ . '/storage/firewall',

    // --- Rate limiting ---
    'rate_limit' => [
        'enabled'      => true,
        'max_requests' => 60,   // requests per window
        'per_seconds'  => 60,   // window size in seconds
    ],

    // --- Automatic banning ---
    'auto_ban' => [
        'enabled'              => true,
        'violations_threshold' => 5,      // violations before a ban is issued
        'ban_duration'         => 86400,  // ban length in seconds (24 h)
        'permanent_after_bans' => 3,      // escalate to permanent after N bans
    ],

    // --- Apache integration ---
    'apache_blocking' => false,
    'htaccess_path'   => __DIR__ . '/.htaccess',

    // --- Always-allow list ---
    'whitelist' => [
        'ips'   => ['127.0.0.1', '10.0.0.0/8'],
        'paths' => ['/health', '/ping'],
    ],

    // --- Response sent to blocked clients ---
    'block_response' => [
        'code'    => 403,
        'message' => 'Access Denied',
    ],

    // --- Toggle individual detection modules ---
    'checks' => [
        'crawler_check' => true,   // exempt verified crawlers from all checks
        'rate_limit'    => true,
        'blocked_path'  => true,
        'user_agent'    => true,
        'payload'       => true,
        'custom_rules'  => true,
    ],

    // --- Log settings ---
    'log' => [
        'enabled'       => true,
        'max_file_size' => 10,   // MB — auto-rotated when exceeded
        'keep_days'     => 30,
    ],

]);

$firewall->run();

Detection Modules

Path Detection

Blocks requests targeting sensitive or non-existent paths. Because a modern routed PHP app has no .php files in the URL, you can add that pattern to immediately reject the flood of index.php?id= scanner probes.

// Add individual patterns
$firewall->patterns->addPath('.php');
$firewall->patterns->addPath('/control-panel');

// Add many at once
$firewall->patterns->addPaths(['.asp', '.jsp', '/backup', '/staging']);

// Remove a default pattern you want to allow
$firewall->patterns->removePath('xmlrpc');
View all default blocked paths
Category Patterns
CMS panels wp-admin, wp-login, wp-config, xmlrpc, administrator, typo3
Config exposure .env, .git, .svn, .htaccess, .htpasswd, web.config
DB tools phpmyadmin, pma, adminer, dbadmin
Dangerous files .sql, .bak, .backup, .old, dump.sql
Path traversal ../, ..%2f, %2e%2e
Web shells shell.php, c99.php, r57.php, webshell
Script extensions .asp, .aspx, .jsp, .cfm, .cgi, .php
Info disclosure phpinfo, server-status, server-info
Install artifacts setup.php, install.php, readme.html

User-Agent Detection

Identifies and blocks known scanner, brute-force, and exploit tool signatures. Empty User-Agent strings are treated as suspicious by default.

$firewall->patterns->addAgent('custom-bad-bot');
$firewall->patterns->removeAgent('curl'); // allow curl if your API clients use it
View default blocked agents

sqlmap · nikto · nessus · acunetix · netsparker · masscan · nmap · zgrab · dirbuster · gobuster · feroxbuster · wfuzz · ffuf · hydra · metasploit · python-requests · go-http-client · libwww-perl · wget · and more


Payload Detection

Scans GET parameters, POST body, raw input, and cookies for attack signatures using compiled regular expressions.

// Add a custom pattern
$firewall->patterns->addPayload('/CUSTOM_EXPLOIT/i', 'my_label');

// Remove a built-in pattern
$firewall->patterns->removePayload('sqli_union');
View default payload rules
Label Detects
sqli_union UNION [ALL] SELECT
sqli_select SELECT ... FROM
sqli_drop DROP TABLE/DATABASE
sqli_sleep SLEEP(n) time-based blind
sqli_benchmark BENCHMARK(...)
sqli_comment --, #, /* */ injection comments
xss_script <script tags
xss_onerror Inline event handlers (onerror=, onclick=, ...)
xss_javascript javascript: protocol
traversal ../ path traversal
php_exec system(), exec(), shell_exec(), ...
php_eval eval(base64_decode(...))
lfi /etc/passwd, /etc/shadow
rfi Remote file inclusion URLs
cmd_injection Shell metacharacters + commands

Rate Limiting

Sliding-window counter stored per-IP on disk. No Redis required.

$firewall = XZeroProtect::init([
    'rate_limit' => [
        'max_requests' => 30,
        'per_seconds'  => 10,
    ],
]);

Crawler Detection

Trusted web crawlers (Googlebot, Bingbot, and others) are identified and exempted from all firewall checks — including rate limiting and auto-ban. This prevents legitimate bots from being accidentally blocked.

For crawlers like Googlebot and Bingbot, identity is confirmed via double-DNS verification before granting trust:

  1. Resolve the visitor IP to a hostname via reverse DNS
  2. Confirm the hostname ends with the expected suffix (e.g. .googlebot.com)
  3. Re-resolve that hostname back to an IP
  4. Confirm the re-resolved IP matches the original visitor IP

This is the verification method recommended by Google and Bing in their official documentation. Anyone can fake a User-Agent — DNS cannot be faked.

Social crawlers (Twitterbot, LinkedInBot, Slackbot, etc.) are trusted by User-Agent only, as they do not publish verifiable IP ranges or rDNS suffixes.

// Add a custom trusted crawler
$firewall->crawlers->addCrawler(
    name: 'MyCrawler',
    uaContains: 'mycrawlerbot',
    verifyRdns: false
);

// Remove a crawler from the trusted list
$firewall->crawlers->removeCrawler('Googlebot');

// Disable crawler detection entirely
$firewall->disableCheck('crawler_check');
View default trusted crawlers
Crawler UA contains DNS verified
Googlebot googlebot .googlebot.com
Google Other google .google.com
Bingbot bingbot .search.msn.com
Yahoo Slurp yahoo! slurp .crawl.yahoo.net
DuckDuckBot duckduckbot
Yandex yandexbot .yandex.com
Baidu baiduspider .baidu.com
Applebot applebot .applebot.apple.com
Facebook facebookexternalhit .facebook.com
Twitterbot twitterbot
LinkedInBot linkedinbot
WhatsApp whatsapp
Telegram telegrambot
Slackbot slackbot
Discordbot discordbot
Uptimerobot uptimerobot
Pingdom pingdom

IP Management

// Temporary ban (24 h default)
$firewall->ip->ban('1.2.3.4');
$firewall->ip->ban('1.2.3.4', reason: 'manual review', duration: 3600);

// Permanent ban
$firewall->ip->banPermanent('1.2.3.4', reason: 'confirmed attacker');

// Remove a ban
$firewall->ip->unban('1.2.3.4');

// Inspect
$firewall->ip->isBanned('1.2.3.4');    // bool
$firewall->ip->getBanInfo('1.2.3.4');  // array|null  { ip, reason, banned_at, expires, bans_count }
$firewall->ip->getAllBans();            // array of all active bans

// Whitelist — supports exact IPs and CIDR notation (IPv4 & IPv6)
$firewall->ip->whitelist('10.0.0.0/8');
$firewall->ip->whitelist('2001:db8::/32');

Custom Rules

Register your own logic as first-class firewall rules with full access to the request object.

use Webrium\XZeroProtect\RuleResult;

// Block requests with a .php extension (for fully-routed apps)
$firewall->rules->add('no-php-extension', function ($request) {
    if (str_ends_with($request->path(), '.php')) {
        return RuleResult::block('PHP extension not valid on this server');
    }
    return RuleResult::pass();
});

// Log suspicious POST requests without logging a violation
$firewall->rules->add('post-no-referer', function ($request) {
    if ($request->method === 'POST' && empty($request->referer)) {
        return RuleResult::log('POST without Referer header');
    }
    return RuleResult::pass();
}, priority: 10);  // lower = runs first

// Manage rules at runtime
$firewall->rules->disable('no-php-extension');
$firewall->rules->enable('no-php-extension');
$firewall->rules->remove('no-php-extension');

RuleResult options:

Method Effect
RuleResult::pass() Allow the request, continue checking
RuleResult::block(reason: '...') Block immediately and log
RuleResult::log(reason: '...') Log without blocking

Apache Integration

When apache_blocking is enabled, permanently banned IPs are written to .htaccess. Apache drops those connections before PHP starts — zero PHP overhead for known bad actors.

$firewall = XZeroProtect::init([
    'apache_blocking' => true,
    'htaccess_path'   => __DIR__ . '/.htaccess',
]);

// Sync all current permanent bans to .htaccess
$firewall->apache->sync(array_keys($firewall->ip->getAllBans()));

// Block/unblock a single IP in .htaccess
$firewall->apache->block('5.6.7.8');
$firewall->apache->unblock('5.6.7.8');

Generated .htaccess block:

# xZeroProtect:start
# Auto-generated by xZeroProtect — do not edit this block manually
<RequireAll>
    Require all granted
    Require not ip 1.2.3.4
    Require not ip 5.6.7.8
</RequireAll>
# xZeroProtect:end

Logging

// Read the most recent attack entries (newest first)
$logs = $firewall->logger->recent(limit: 100);

// Clean up rotated backup log files older than retention period
$firewall->logger->cleanup();

Accessing the firewall from outside bootstrap

Because init() stores the instance as a singleton, you can retrieve it from any controller, admin panel, or CLI script without passing $firewall as a variable:

use Webrium\XZeroProtect\XZeroProtect;

// In bootstrap / index.php
XZeroProtect::init()->run();

// ---

// In any other file (e.g. admin panel, dashboard controller)
$firewall = XZeroProtect::getInstance();

$logs    = $firewall->logger->recent(limit: 100);
$bans    = $firewall->ip->getAllBans();
$isBanned = $firewall->ip->isBanned('1.2.3.4');

If getInstance() is called before init(), it throws a clear RuntimeException:

xZeroProtect has not been initialized. Call XZeroProtect::init() first.

Log entries are plain-text, one per line:

2024-11-15 14:32:01 | ip=185.220.101.5 | type=sqli_union | uri=/search?q=1+UNION+SELECT | reason=Payload match: sqli_union | ua=sqlmap/1.7

Disabling Individual Checks

// At init time
XZeroProtect::init([
    'checks' => [
        'user_agent' => false,  // disable UA checking for this app
    ],
]);

// Or at runtime
$firewall->disableCheck('user_agent');
$firewall->enableCheck('user_agent');

Available check keys: crawler_check · rate_limit · blocked_path · user_agent · payload · custom_rules


Learning Mode

Deploy in learning mode first. All attacks are logged but nothing is blocked. Review the logs, tune your rules, then switch to production.

// During tuning
XZeroProtect::init(['mode' => 'learning'])->run();

// Once satisfied
XZeroProtect::init(['mode' => 'production'])->run();

Architecture

xzeroprotect/
├── src/
│   ├── XZeroProtect.php      Main class & orchestrator
│   ├── Request.php           HTTP request context
│   ├── Storage.php           File-based persistence (bans, rate, violations, logs)
│   ├── IPManager.php         Ban/whitelist management with CIDR support
│   ├── PatternDetector.php   Path, User-Agent, and payload matching
│   ├── RateLimiter.php       Sliding-window rate limiter
│   ├── RuleEngine.php        Custom rule registration & execution
│   ├── ApacheBlocker.php     .htaccess read/write
│   ├── CrawlerVerifier.php   Trusted crawler detection with double-DNS
│   └── Logger.php            Attack logging with rotation
├── config/
│   └── config.php            Default configuration
├── rules/
│   ├── paths.php             Default blocked path patterns
│   ├── agents.php            Default blocked User-Agent signatures
│   ├── payloads.php          Default attack payload patterns (PCRE)
│   └── crawlers.php          Trusted crawler definitions (UA + rDNS config)
└── tests/
    └── XZeroProtectTest.php  PHPUnit test suite

Requirements

  • PHP 8.0 or higher
  • Write permission on the storage directory

License

Released under the MIT License.
Built by Webrium.

About

A lightweight, file-based PHP 8 firewall — rate limiting, bot detection, payload scanning, and IP banning with zero dependencies.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages