kanopi/firewall

Evaluate the requests for malicious items.

v2.0.0-alpha1 2025-06-25 22:41 UTC

This package is auto-updated.

Last update: 2025-06-30 18:05:13 UTC


README

Simple Firewall is a powerful, extensible request-evaluation library for PHP-based systems. It provides comprehensive protection by analyzing HTTP requests and applying configurable rules to either allow or block access based on IP addresses, geolocation, user agents, URLs, ASN (Autonomous System Numbers), and rate limits. The library is designed to work seamlessly with popular frameworks like Drupal, WordPress, Symfony, or any standalone PHP application.

Table of Contents

Features

  • Flexible Plugin System: Modular architecture allows for easy extension and customization
  • Multiple Storage Backends: Support for in-memory, file-based, database, and Redis storage
  • Comprehensive Request Analysis: Evaluate requests based on IP, location, user agent, URL patterns, and more
  • Vulnerability Scoring: Advanced risk assessment based on multiple factors with configurable thresholds
  • Rate Limiting: Built-in rate limiting with configurable storage backends
  • GeoIP Integration: Full support for MaxMind GeoIP2 databases (both local and web service)
  • Advanced Conditional Logic: Support for simple, complex, and grouped conditional rules
  • PSR-3 Compatible Logging: Integration with Monolog for flexible logging
  • Framework Agnostic: Works with any PHP application or framework

Requirements

  • PHP 8.0 or higher
  • Composer
  • Optional: MaxMind GeoIP2 databases for geolocation features
  • Optional: Redis for distributed rate limiting

Installation

Install via Composer:

composer require kanopi/firewall

For geolocation features, also install:

composer require geoip2/geoip2

Quick Start

Basic Implementation

Place the following code in your application's entry point (e.g., index.php, wp-config.php, or Drupal's settings.php):

<?php
// Include composer autoloader if not already loaded
require_once __DIR__ . '/vendor/autoload.php';

// Initialize and evaluate the firewall
if (class_exists('\Kanopi\Firewall\Firewall')) {
    \Kanopi\Firewall\Firewall::create([__DIR__ . '/config/firewall.yml'])->evaluate();
}

Minimal Configuration Example

Create a config/firewall.yml file:

# Storage configuration - where blocked IPs are stored
storage:
  type: \Kanopi\Firewall\Storage\FileStorage
  config:
    file: /var/log/firewall/blocked.data

# Block malicious IPs
block:
  \Kanopi\Firewall\Plugins\IpAddress:
    enable: true
    config:
      - 192.168.1.100
      - 10.0.0.0/24
  
  # Optional: Enable vulnerability scoring for advanced threat detection
  # \Kanopi\Firewall\Plugins\VulnerabilityScore:
  #   enable: true
  #   config:
  #     scoring:
  #       patterns:
  #         - pattern: "/<script|union.*select/i"
  #           score: 50
  #           type: regex
  #           locations: ["uri", "query_string"]
  #     risk_levels:
  #       high:
  #         threshold: 40
  #         block: true

# Optional: Configure logging
logger:
  - class: Monolog\Handler\StreamHandler
    args:
      - /var/log/firewall/firewall.log
      - Monolog\Level::Info

Configuration Overview

The firewall configuration consists of four main sections:

Section Purpose Required
storage Defines where blocked IP addresses are persisted Yes
bypass Plugins that allow trusted traffic through No
block Plugins that deny harmful or suspicious traffic No
logger Monolog handlers for logging firewall events No

Storage Configuration

Storage defines how the firewall persists blocked IP addresses across requests.

Available Storage Classes

1. In-Memory Storage

Non-persistent storage that resets with each request. Useful for testing.

storage:
  type: \Kanopi\Firewall\Storage\InMemoryStorage

2. File Storage

Persists blocked IPs to the filesystem.

storage:
  type: \Kanopi\Firewall\Storage\FileStorage
  config:
    file: /var/log/firewall/blocked_ips.data

3. Database Storage

Stores blocked IPs in a SQL database using Doctrine DBAL.

storage:
  type: \Kanopi\Firewall\Storage\DatabaseStorage
  config:
    storage-table: firewall_blocked_ips
    connection:
      # Option 1: Using DSN (recommended)
      dsn: "mysql://user:password@localhost:3306/database?serverVersion=8.0"
      
      # Option 2: Individual parameters
      # dbname: 'my_database'
      # user: 'db_user'
      # password: 'db_password'
      # host: 'localhost'
      # port: 3306
      # driver: 'pdo_mysql'

Plugin Architecture

Plugins are the core components that evaluate incoming requests. They can be configured in two sections:

  • bypass: Plugins here allow requests to pass through without further evaluation
  • block: Plugins here can block requests based on configured rules

Common Plugin Configuration

All plugins share these configuration options:

PluginNamespace:
  enable: true              # Whether the plugin is active
  priority: 0              # Execution order (-100 runs before 100)
  metadata: {}             # Plugin-specific configuration
  config: []               # Rules or conditions for the plugin

Available Plugins

IP Address Plugin

Namespace: \Kanopi\Firewall\Plugins\IpAddress

Evaluates requests based on IP addresses, supporting IPv4, IPv6, CIDR blocks, and IP ranges.

Configuration Example

# In bypass section - whitelist trusted IPs
bypass:
  \Kanopi\Firewall\Plugins\IpAddress:
    enable: true
    priority: -100  # Run early
    config:
      # Single IPv4 address
      - 192.168.1.1
      # Single IPv6 address
      - ::1
      - 2001:db8::1
      # CIDR notation
      - 10.0.0.0/8
      - 172.16.0.0/12
      # IP range (start-end)
      - 192.168.1.100-192.168.1.200

# In block section - blacklist malicious IPs
block:
  \Kanopi\Firewall\Plugins\IpAddress:
    enable: true
    priority: -100
    config:
      - 192.168.1.50
      - 10.10.10.0/24

GeoLocation Plugin

Namespace: \Kanopi\Firewall\Plugins\GeoLocation

Evaluates requests based on geographic location using MaxMind GeoIP2 databases.

Configuration Example

block:
  \Kanopi\Firewall\Plugins\GeoLocation:
    enable: true
    priority: 0
    metadata:
      reader:
        # Option 1: Local database file
        type: reader
        db: /path/to/GeoLite2-City.mmdb
        
        # Option 2: MaxMind web service
        # type: client
        # accountId: 123456
        # licenseKey: your_license_key
        # languages: ['en', 'es']
        # options: []
    config:
      # Block specific countries
      - "country:CN"
      - "country:RU"
      - "country.isoCode:KP"
      
      # Block entire continents
      - "continent:AS"
      - "continent.code:AF"
      
      # Block specific cities
      - "city:Moscow"
      - "city.name@contains:Beijing"
      
      # Complex location rules
      - variable: location.timeZone
        operator: equals
        value: "Asia/Shanghai"

Available Variables

  • country - Returns country ISO code (e.g., "US")
  • country.isoCode - Country ISO code
  • country.name - Full country name
  • continent - Returns continent code (e.g., "NA")
  • continent.code - Continent code
  • continent.name - Full continent name
  • city - Returns city name
  • city.name - City name
  • location.latitude - Latitude coordinate
  • location.longitude - Longitude coordinate
  • location.timeZone - Time zone
  • postal - Returns postal code
  • postal.code - Postal/ZIP code

URL Plugin

Namespace: \Kanopi\Firewall\Plugins\Url

Evaluates requests based on URL components and request parameters.

Configuration Example

block:
  \Kanopi\Firewall\Plugins\Url:
    enable: true
    priority: 0
    config:
      # Block all POST requests
      - "method:POST"
      
      # Block specific paths
      - "path:/wp-admin"
      - "path@starts_with:/admin"
      - "path@contains:phpmyadmin"
      - "path@regex:/\.(sql|bak|old)$/i"
      
      # Block based on host
      - "host:malicious.example.com"
      - "host@ends_with:.suspicious.com"
      
      # Block based on query parameters
      - "query.cmd@exists"
      - "query.action:delete"
      
      # Block based on POST data
      - "post.username:admin"
      - "post.action@in:drop,truncate,delete"
      
      # Block based on headers
      - "header.user-agent@contains:bot"
      - "header.x-forwarded-for@exists"
      
      # Complex URL rules
      - type: AND
        rules:
          - "method:POST"
          - "path@starts_with:/api"
          - "!header.authorization@exists"

Available Variables

  • method - HTTP method (GET, POST, PUT, DELETE, etc.)
  • host - Hostname from the request
  • path - URI path (e.g., /admin/users)
  • scheme - URL scheme (http or https)
  • port - Port number
  • query.* - Query parameters (e.g., query.page, query.id)
  • post.* - POST body parameters
  • header.* - HTTP headers (e.g., header.user-agent)
  • cookie.* - Cookie values

User Agent Plugin

Namespace: \Kanopi\Firewall\Plugins\UserAgent

Analyzes user agent strings to identify bots, devices, browsers, and operating systems.

Configuration Example

block:
  \Kanopi\Firewall\Plugins\UserAgent:
    enable: true
    priority: 0
    config:
      # Block all bots
      - "bot:true"
      
      # Block specific device types
      - "device.type:desktop"
      - "device.type@in:smartphone,tablet"
      
      # Block specific browsers
      - "client.name:Internet Explorer"
      - "client.type:browser"
      - "client.version@less_than:10"
      
      # Block specific operating systems
      - "os.name:Windows XP"
      - "os.short_name:WIN"
      - "os.version@less_than:10"
      
      # Block specific brands or models
      - "brand:Huawei"
      - "model@contains:Galaxy"
      
      # Complex user agent rules
      - type: AND
        rules:
          - "bot:false"
          - "client.name:Chrome"
          - "client.version@less_than:80"

Available Variables

  • bot - Whether the user agent is a bot ("true" or "false")
  • device.type - Device type (desktop, smartphone, tablet, etc.)
  • client.name - Browser or client name
  • client.type - Client type (browser, mobile app, etc.)
  • client.version - Client version number
  • os.name - Operating system name
  • os.short_name - OS short name (WIN, MAC, LIN, etc.)
  • os.version - OS version number
  • brand - Device brand (Apple, Samsung, etc.)
  • model - Device model

ASN Plugin

Namespace: \Kanopi\Firewall\Plugins\Asn

Evaluates requests based on Autonomous System Numbers (ASN) using MaxMind's GeoIP2 ASN database.

Configuration Example

block:
  \Kanopi\Firewall\Plugins\Asn:
    enable: true
    priority: 0
    metadata:
      reader:
        type: reader
        db: /path/to/GeoLite2-ASN.mmdb
    config:
      # Block specific ASN numbers
      - "asn:13335"  # Cloudflare
      - "asn:15169"  # Google
      
      # Block by organization name
      - "asn_org:CLOUDFLARENET"
      - "asn_org@contains:AMAZON"
      - "asn_org@starts_with:DIGITAL"

Available Variables

  • asn - Autonomous System Number
  • asn_org - Organization name associated with the ASN

Rate Limit Plugin

Namespace: \Kanopi\Firewall\Plugins\RateLimit

Implements rate limiting to prevent abuse and DDoS attacks.

Configuration Example

block:
  \Kanopi\Firewall\Plugins\RateLimit:
    enable: true
    priority: 100  # Run after other plugins
    metadata:
      # Default settings for all paths
      default_rate: 60        # Requests allowed
      default_sample: 60      # Time window in seconds
      default_expiration_time: 300  # Block duration in seconds
      
      # Storage backend for rate limit data
      storage:
        # Option 1: Redis (recommended for production)
        type: \Kanopi\Firewall\RateLimitStorage\RedisRateLimitStorage
        config:
          redis:
            host: localhost
            port: 6379
            # Authentication options:
            # auth: "password"
            # auth: ["password"]
            # auth: ["username", "password"]
        
        # Option 2: File storage
        # type: \Kanopi\Firewall\RateLimitStorage\FileRateLimitStorage
        # config:
        #   file: /var/log/firewall/ratelimit.data
        
        # Option 3: Database storage
        # type: \Kanopi\Firewall\RateLimitStorage\DatabaseRateLimitStorage
        # config:
        #   storage-table: firewall_ratelimit
        #   connection:
        #     dsn: "mysql://user:pass@localhost/db"
        
        # Option 4: In-memory (testing only)
        # type: \Kanopi\Firewall\RateLimitStorage\InMemoryRateLimitStorage
    
    config:
      # Strict rate limit for homepage
      - path: "/"
        rate: 10
        sample: 60
      
      # API endpoints with higher limits
      - path: "/api/*"
        rate: 100
        sample: 60
      
      # Admin area with moderate limits
      - path: "/admin/*"
        rate: 30
        sample: 60
      
      # Login endpoint with strict limits
      - path: "/login"
        rate: 5
        sample: 300  # 5 attempts per 5 minutes
      
      # Use regex for complex patterns
      - path: "/\.(php|asp|aspx)$/i"
        rate: 1
        sample: 3600  # Block direct script access

Path Patterns

  • Exact match: /login
  • Wildcard: /api/* (matches /api/users, /api/posts/123, etc.)
  • Regex: /^\/api\/v[0-9]+\// (matches /api/v1/, /api/v2/, etc.)

Vulnerability Score Plugin

Namespace: \Kanopi\Firewall\Plugins\VulnerabilityScore

Evaluates requests based on a comprehensive scoring system that combines multiple risk factors to determine if a request should be blocked. This plugin provides fine-grained control over security policies by assigning scores to various request characteristics and blocking based on cumulative risk levels.

Key Features

  • Multi-Factor Scoring: Evaluates HTTP methods, geographic origin, ASN, patterns, and user agents
  • Configurable Risk Levels: Define custom thresholds with different blocking behaviors
  • Pattern Detection: Built-in detection for SQL injection, XSS, command injection, and custom patterns
  • Geographic Intelligence: Optional integration with GeoIP databases for country and ASN scoring
  • Dynamic Response: Different status codes and expiration times based on risk level

Configuration Example

block:
  \Kanopi\Firewall\Plugins\VulnerabilityScore:
    enable: true
    priority: -50  # Run after basic filters but before rate limiting
    metadata:
      # Default response settings
      default_expiration_time: 3600
      status_code: 403
      
      # Optional: GeoIP database for country scoring
      country_reader:
        type: reader
        db: /path/to/GeoLite2-Country.mmdb
      
      # Optional: ASN database for network scoring
      asn_reader:
        type: reader
        db: /path/to/GeoLite2-ASN.mmdb
      
      # Load scoring rules from external file
      config:
        - vulnerability-score-rules.yml
    
    config:
      scoring:
        # HTTP Method Scoring
        methods:
          GET: 0          # Safe read operations
          HEAD: 0
          OPTIONS: 1      # CORS probing
          POST: 10        # Write operations
          PUT: 15         # Full replacements
          PATCH: 15       # Partial updates
          DELETE: 20      # Destructive operations
          TRACE: 50       # Security risk
          CONNECT: 50     # Proxy tunneling
        
        # Country-based Scoring
        countries:
          # Low risk countries
          US: 1
          CA: 1
          GB: 1
          DE: 1
          
          # Medium risk countries
          BR: 10
          IN: 10
          
          # High risk countries
          CN: 30
          RU: 30
          KP: 50
          IR: 40
        
        # ASN (Network) Scoring
        asn:
          # Trusted networks
          "15169": 1      # Google
          "13335": 1      # Cloudflare
          "16509": 1      # Amazon AWS
          
          # Suspicious networks
          "4134": 30      # Chinanet
          "45102": 25     # Alibaba Cloud
        
        # ASN Organization Pattern Matching
        asn_patterns:
          "vpn": 20
          "proxy": 20
          "hosting": 15
          "datacenter": 10
          "residential": 5
        
        # Malicious Pattern Detection
        patterns:
          # SQL Injection
          - pattern: "/(union.*select|select.*from|drop.*table)/i"
            score: 40
            type: regex
            locations: ["uri", "query_string", "body"]
          
          # XSS Attacks
          - pattern: "/<script[^>]*>.*?<\/script>/i"
            score: 35
            type: regex
            locations: ["uri", "query_string", "body"]
          
          - pattern: "javascript:"
            score: 30
            type: contains
            locations: ["uri", "query_string", "body"]
          
          # Command Injection
          - pattern: "/(;|\||&&|`|\$\()/i"
            score: 25
            type: regex
            locations: ["uri", "query_string"]
          
          # Path Traversal
          - pattern: "/(\.\.[\/\\]){2,}/i"
            score: 30
            type: regex
            locations: ["uri", "query_string"]
          
          # Sensitive Files
          - pattern: ".git"
            score: 20
            type: contains
            locations: ["uri"]
          
          - pattern: ".env"
            score: 25
            type: contains
            locations: ["uri"]
          
          # Admin Access
          - pattern: "admin"
            score: 10
            type: contains
            locations: ["uri"]
        
        # User Agent Scoring
        user_agents:
          # Known attack tools
          - pattern: "sqlmap"
            score: 50
            type: contains
          
          - pattern: "nikto"
            score: 45
            type: contains
          
          - pattern: "nmap"
            score: 40
            type: contains
          
          # Suspicious agents
          - pattern: "python-requests"
            score: 15
            type: contains
          
          - pattern: "curl"
            score: 10
            type: contains
          
          # Empty user agent
          - pattern: "^$"
            score: 20
            type: regex
      
      # Risk Level Configuration
      risk_levels:
        low:
          threshold: 0
          block: false      # Monitor only
          
        medium:
          threshold: 25
          block: false      # Still monitoring
          
        high:
          threshold: 50
          block: true
          status_code: 403
          expiration_time: 3600    # 1 hour
          
        critical:
          threshold: 75
          block: true
          status_code: 403
          expiration_time: 86400   # 24 hours
          
        extreme:
          threshold: 100
          block: true
          status_code: 403
          expiration_time: 604800  # 7 days

Scoring Components

1. Method Scoring

Assigns scores based on HTTP methods, with higher scores for potentially dangerous operations.

2. Country Scoring

Uses GeoIP database to identify request origin and assign scores based on geographic risk assessment.

3. ASN Scoring

Evaluates the Autonomous System Number of the request origin, identifying datacenter, VPN, or residential connections.

4. Pattern Detection

Searches for malicious patterns in various parts of the request:

  • Locations: uri, query_string, body, headers
  • Types: regex, contains, exact
  • Patterns: SQL injection, XSS, command injection, path traversal, etc.
5. User Agent Analysis

Identifies and scores suspicious or malicious user agents, including security tools and bots.

Risk Levels

Each risk level can be configured with:

  • threshold: Minimum score to trigger this level
  • block: Whether to block requests at this level
  • status_code: HTTP status code to return when blocking
  • expiration_time: How long to block the IP address (in seconds)

Advanced Usage Examples

Example 1: E-commerce Site Protection
config:
  scoring:
    methods:
      GET: 0
      POST: 5        # Allow normal form submissions
      DELETE: 50     # High risk for e-commerce
    
    patterns:
      # Credit card testing
      - pattern: "/4[0-9]{12}(?:[0-9]{3})?/"
        score: 60
        type: regex
        locations: ["body", "query_string"]
      
      # Price manipulation attempts
      - pattern: "price="
        score: 30
        type: contains
        locations: ["query_string", "body"]
      
      # Admin panel access
      - pattern: "/admin|/backend|/dashboard/i"
        score: 20
        type: regex
        locations: ["uri"]
    
    user_agents:
      # Block automated scanners
      - pattern: "bot|crawler|spider"
        score: 15
        type: regex
  
  risk_levels:
    high:
      threshold: 40
      block: true
      status_code: 403
      expiration_time: 7200
Example 2: API Protection
config:
  scoring:
    methods:
      GET: 0
      POST: 5
      PUT: 10
      DELETE: 30
    
    patterns:
      # GraphQL introspection
      - pattern: "__schema"
        score: 40
        type: contains
        locations: ["body", "query_string"]
      
      # Mass assignment attempts
      - pattern: "/(role|admin|permission)=/i"
        score: 35
        type: regex
        locations: ["body"]
    
    user_agents:
      # Require proper user agents for API access
      - pattern: "^$"
        score: 50  # No user agent = suspicious
        type: regex
  
  risk_levels:
    medium:
      threshold: 30
      block: true
      status_code: 429  # Too Many Requests
      expiration_time: 300
Example 3: Geographic Restrictions with Exceptions
config:
  scoring:
    countries:
      # Blocked regions
      CN: 50
      RU: 50
      KP: 100
      
      # Allowed regions
      US: 0
      CA: 0
      GB: 0
    
    # But allow known good ASNs from blocked countries
    asn:
      "45102": -40  # Alibaba Cloud (reduces China score)
      "13335": -40  # Cloudflare (reduces any country score)
    
  risk_levels:
    high:
      threshold: 40
      block: true

Integration with Other Plugins

The VulnerabilityScore plugin works well with other firewall plugins:

# Use IP whitelist to bypass scoring
bypass:
  \Kanopi\Firewall\Plugins\IpAddress:
    enable: true
    priority: -200
    config:
      - 192.168.1.0/24  # Internal network

# Apply vulnerability scoring
block:
  \Kanopi\Firewall\Plugins\VulnerabilityScore:
    enable: true
    priority: -50
    config: # ... scoring configuration ...
  
  # Then apply rate limiting to scored requests
  \Kanopi\Firewall\Plugins\RateLimit:
    enable: true
    priority: 100
    config:
      - path: "/*"
        rate: 60
        sample: 60

Performance Considerations

  • The plugin evaluates all scoring factors for each request
  • Pattern matching can be CPU intensive with many patterns
  • Consider using Redis or database storage for better performance at scale
  • Place the plugin after basic filters (like IP blocking) for efficiency

Debugging and Monitoring

The plugin logs detailed information about scoring decisions:

logger:
  - class: Monolog\Handler\StreamHandler
    args:
      - /var/log/firewall/vulnerability-scores.log
      - Monolog\Level::Debug
    formatter:
      class: Monolog\Formatter\JsonFormatter

Log entries include:

  • Total score calculated
  • Individual component scores
  • Risk level determined
  • Blocking decision

Conditional Logic

The firewall supports three formats for defining conditions:

1. Simple Format

Quick and readable syntax for common conditions:

# Basic equality
- "variable:value"

# With operator
- "variable@operator:value"

# Negation
- "!variable:value"
- "!variable@operator:value"

# Numeric comparisons
- "rate > 100"
- "client.version <= 10"

# Array matching
- "tags@contains:spam,malware#all"  # Must contain all
- "tags@contains:bot,crawler#any"   # Must contain at least one

Supported Operators

  • equals (default)
  • not_equals
  • contains
  • starts_with
  • ends_with
  • regex
  • in
  • greater_than (>)
  • less_than (<)
  • greater_than_or_equal (>=)
  • less_than_or_equal (<=)
  • exists

2. Complex Format

Detailed configuration with full control:

- variable: method
  operator: in
  value: [GET, POST]
  negate: false
  case_sensitive: true
  matches: any  # For array values: any, all, none, some

3. Grouped Format

Combine multiple conditions with logical operators:

- type: AND
  rules:
    - "method:POST"
    - "path@starts_with:/api"
    - type: OR
      rules:
        - "header.authorization@exists"
        - "query.api_key@exists"

Logging Configuration

The firewall uses Monolog for flexible logging. Multiple handlers can be configured:

logger:
  # File logging
  - class: Monolog\Handler\StreamHandler
    args:
      - /var/log/firewall/firewall.log
      - Monolog\Level::Info
    formatter:
      class: Monolog\Formatter\LineFormatter
      args:
        - "[%datetime%] [%level_name%] [%context.plugin%] %message% %context% %extra%\n"
        - "Y-m-d H:i:s"
  
  # Syslog
  - class: Monolog\Handler\SyslogHandler
    args:
      - firewall
      - LOG_USER
      - Monolog\Level::Warning
  
  # Email alerts for critical events
  - class: Monolog\Handler\NativeMailerHandler
    args:
      - security@example.com
      - "Firewall Alert"
      - noreply@example.com
      - Monolog\Level::Critical

Dynamic Configuration Overrides

For dynamic environments (Docker, multi-site installations), you can override YAML configuration with PHP arrays:

<?php
$overrides = [
    // Override storage location
    '[storage][config][file]' => $_ENV['FIREWALL_STORAGE_PATH'] ?? '/tmp/firewall.data',
    
    // Override GeoIP database path
    '[block][\Kanopi\Firewall\Plugins\GeoLocation][metadata][reader][db]' => $_ENV['GEOIP_DB_PATH'],
    
    // Override Redis connection
    '[block][\Kanopi\Firewall\Plugins\RateLimit][metadata][storage][config][redis][host]' => $_ENV['REDIS_HOST'] ?? 'localhost',
    
    // Disable a plugin
    '[block][\Kanopi\Firewall\Plugins\UserAgent][enable]' => false,
];

\Kanopi\Firewall\Firewall::create([__DIR__ . '/config.yml'], $overrides)->evaluate();

Platform Integration

Drupal

Add to settings.php before the container configuration:

// Load composer autoloader if not already loaded
if (file_exists(__DIR__ . '/../vendor/autoload.php')) {
    require_once __DIR__ . '/../vendor/autoload.php';
}

// Initialize firewall
if (class_exists('\Kanopi\Firewall\Firewall')) {
    $firewall_config = __DIR__ . '/firewall.yml';
    if (file_exists($firewall_config)) {
        \Kanopi\Firewall\Firewall::create([$firewall_config])->evaluate();
    }
}

WordPress

Add to wp-config.php after ABSPATH is defined but before wp-settings.php:

// Firewall integration
if (file_exists(__DIR__ . '/vendor/autoload.php')) {
    require_once __DIR__ . '/vendor/autoload.php';
    
    if (class_exists('\Kanopi\Firewall\Firewall')) {
        $firewall_config = __DIR__ . '/firewall/config.yml';
        if (file_exists($firewall_config)) {
            \Kanopi\Firewall\Firewall::create([$firewall_config])->evaluate();
        }
    }
}

Symfony

Add to public/index.php before the kernel boot:

use App\Kernel;
use Kanopi\Firewall\Firewall;

require_once dirname(__DIR__).'/vendor/autoload_runtime.php';

return function (array $context) {
    // Initialize firewall
    if (class_exists(Firewall::class)) {
        $configPath = dirname(__DIR__) . '/config/firewall.yml';
        if (file_exists($configPath)) {
            Firewall::create([$configPath])->evaluate();
        }
    }
    
    return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};

Laravel

Add to public/index.php after the autoloader:

require __DIR__.'/../vendor/autoload.php';

// Firewall integration
if (class_exists('\Kanopi\Firewall\Firewall')) {
    $firewall_config = __DIR__ . '/../config/firewall.yml';
    if (file_exists($firewall_config)) {
        \Kanopi\Firewall\Firewall::create([$firewall_config])->evaluate();
    }
}

$app = require_once __DIR__.'/../bootstrap/app.php';

Advanced Examples

Multi-layered Security Configuration

# High-performance storage
storage:
  type: \Kanopi\Firewall\Storage\DatabaseStorage
  config:
    storage-table: firewall_blocked
    connection:
      dsn: "mysql://firewall:secure@localhost/security"

# Whitelist trusted sources
bypass:
  # Office IPs
  \Kanopi\Firewall\Plugins\IpAddress:
    enable: true
    priority: -200
    config:
      - 203.0.113.0/24  # Office network
      - 198.51.100.50   # VPN endpoint

# Comprehensive blocking rules
block:
  # Geographic restrictions
  \Kanopi\Firewall\Plugins\GeoLocation:
    enable: true
    priority: -100
    metadata:
      reader:
        type: reader
        db: /usr/share/GeoIP/GeoLite2-City.mmdb
    config:
      # Block high-risk countries
      - type: OR
        rules:
          - "country@in:CN,RU,KP,IR"
          - "continent:AF"
  
  # Block suspicious user agents
  \Kanopi\Firewall\Plugins\UserAgent:
    enable: true
    priority: -50
    config:
      # Block all bots except Google and Bing
      - type: AND
        rules:
          - "bot:true"
          - "!client.name@in:Googlebot,Bingbot"
      
      # Block outdated browsers
      - type: OR
        rules:
          - variable: client.name
            operator: equals
            value: "Internet Explorer"
          - type: AND
            rules:
              - "client.name:Chrome"
              - "client.version < 80"
  
  # Vulnerability scoring for comprehensive threat assessment
  \Kanopi\Firewall\Plugins\VulnerabilityScore:
    enable: true
    priority: -25
    metadata:
      country_reader:
        type: reader
        db: /usr/share/GeoIP/GeoLite2-Country.mmdb
      asn_reader:
        type: reader
        db: /usr/share/GeoIP/GeoLite2-ASN.mmdb
    config:
      scoring:
        methods:
          DELETE: 30
          PUT: 20
          POST: 10
        countries:
          CN: 25
          RU: 25
          KP: 50
        patterns:
          - pattern: "/(union.*select|drop.*table)/i"
            score: 50
            type: regex
            locations: ["uri", "query_string", "body"]
          - pattern: "/<script|javascript:/i"
            score: 40
            type: regex
            locations: ["uri", "query_string", "body"]
      risk_levels:
        high:
          threshold: 50
          block: true
          status_code: 403
          expiration_time: 7200
  
  # URL-based protection
  \Kanopi\Firewall\Plugins\Url:
    enable: true
    priority: 0
    config:
      # Protect admin areas
      - type: AND
        rules:
          - "path@starts_with:/admin"
          - "!header.authorization@exists"
      
      # Block vulnerability scanners
      - "path@regex:/(\.git|\.env|\.htaccess|wp-config\.php|phpmyadmin)/i"
      
      # Block SQL injection attempts
      - "query@regex:/(union.*select|select.*from|insert.*into|drop.*table)/i"
  
  # Aggressive rate limiting
  \Kanopi\Firewall\Plugins\RateLimit:
    enable: true
    priority: 100
    metadata:
      default_rate: 120
      default_sample: 60
      storage:
        type: \Kanopi\Firewall\RateLimitStorage\RedisRateLimitStorage
        config:
          redis:
            host: redis.internal
            port: 6379
            auth: ["default", "redis_password"]
    config:
      # API rate limits by endpoint
      - path: "/api/v1/auth/*"
        rate: 5
        sample: 300
      
      - path: "/api/v1/public/*"
        rate: 100
        sample: 60
      
      - path: "/api/v1/private/*"
        rate: 30
        sample: 60

# Comprehensive logging
logger:
  # General log file
  - class: Monolog\Handler\RotatingFileHandler
    args:
      - /var/log/firewall/firewall.log
      - 7  # Keep 7 days
      - Monolog\Level::Info
    formatter:
      class: Monolog\Formatter\JsonFormatter
  
  # Security alerts
  - class: Monolog\Handler\StreamHandler
    args:
      - /var/log/firewall/security-alerts.log
      - Monolog\Level::Warning
    formatter:
      class: Monolog\Formatter\LineFormatter
      args:
        - "[%datetime%] %level_name%: %message% %context%\n"

Custom Plugin Implementation

Create a custom plugin to implement specific business logic:

<?php

namespace App\Security\Firewall\Plugins;

use Kanopi\Firewall\Plugins\AbstractPluginBase;
use Symfony\Component\HttpFoundation\Request;

class ApiKeyValidator extends AbstractPluginBase
{
    private array $validApiKeys;
    
    public function __construct(array $metadata = [], array $config = [])
    {
        parent::__construct($metadata, $config);
        
        // Load API keys from configuration or database
        $this->validApiKeys = $metadata['api_keys'] ?? [];
    }
    
    public function getName(): string
    {
        return 'API Key Validator';
    }
    
    public function getDescription(): string
    {
        return 'Validates API keys for authenticated endpoints';
    }
    
    public function evaluate(Request $request): bool
    {
        // Only check API endpoints
        if (!str_starts_with($request->getPathInfo(), '/api/')) {
            return false;
        }
        
        // Check for API key in header or query
        $apiKey = $request->headers->get('X-API-Key') 
                  ?? $request->query->get('api_key');
        
        if (!$apiKey) {
            $this->logger?->warning('Missing API key', [
                'ip' => $request->getClientIp(),
                'path' => $request->getPathInfo(),
            ]);
            return true; // Block request
        }
        
        if (!in_array($apiKey, $this->validApiKeys, true)) {
            $this->logger?->warning('Invalid API key', [
                'ip' => $request->getClientIp(),
                'api_key' => substr($apiKey, 0, 8) . '...',
            ]);
            return true; // Block request
        }
        
        return false; // Allow request
    }
    
    public function getStatusCode(): int
    {
        return 401; // Unauthorized
    }
}

Register the custom plugin in your configuration:

block:
  \App\Security\Firewall\Plugins\ApiKeyValidator:
    enable: true
    priority: -150  # Run before rate limiting
    metadata:
      api_keys:
        - "sk_live_abcd1234567890"
        - "sk_live_efgh0987654321"

Testing

The firewall includes a comprehensive test suite. Run tests with:

# Run all tests
composer test

# Run with coverage
composer test:coverage

# Run specific test suite
./vendor/bin/phpunit tests/Unit/Plugins/

# Run integration tests
./vendor/bin/phpunit tests/Integration/

Example Test Case

<?php

use PHPUnit\Framework\TestCase;
use Kanopi\Firewall\Firewall;
use Symfony\Component\HttpFoundation\Request;

class FirewallTest extends TestCase
{
    public function testBlocksMaliciousIp(): void
    {
        $config = [
            'storage' => [
                'type' => 'Kanopi\Firewall\Storage\InMemoryStorage'
            ],
            'block' => [
                'Kanopi\Firewall\Plugins\IpAddress' => [
                    'enable' => true,
                    'config' => ['192.168.1.100']
                ]
            ]
        ];
        
        $firewall = Firewall::create([$config]);
        
        // Create a request from the blocked IP
        $request = Request::create('/', 'GET', [], [], [], [
            'REMOTE_ADDR' => '192.168.1.100'
        ]);
        
        // The firewall should block this request
        $this->expectException(\Exception::class);
        $firewall->evaluate($request);
    }
}

Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Setup

  1. Clone the repository
  2. Install dependencies: composer install
  3. Run tests: composer test
  4. Check code style: composer cs
  5. Run static analysis: composer stan

License

This project is licensed under the MIT License. See the LICENSE file for details.

Support

Credits

Simple Firewall is developed and maintained by Kanopi Studios.

Special thanks to:

  • The Symfony team for the excellent HttpFoundation component
  • MaxMind for the GeoIP2 databases
  • The Monolog team for the flexible logging library
  • All our contributors and users