Skip to main navigation Skip to main content Skip to page footer

Community Budget Report: A PHP Firewall for TYPO3

Sascha Egerer provides an update on his Community Budget Idea to add a PHP-based firewall to TYPO3, helping site owners block common attacks even when they can’t rely on server-level security.

In Q4/2025, the TYPO3 Association funded my community budget idea to improve protection for TYPO3 websites against automated attacks — without requiring root access, special hosting features, or external services.

Background and Goals

Many TYPO3 installations run on managed hosting. This often prevents the use of server-level security solutions such as ModSecurity or custom web server rules.

The goal of this project was to provide a rule-based firewall inside TYPO3 itself. The firewall runs early in the request lifecycle, is fully configurable in PHP, and works without infrastructure-level access.

Two Building Blocks

The Firewall Engine: Phirewall

Phirewall (flowd/phirewall) is a PHP application firewall implemented as a PSR-15 middleware. It provides:

  • Safelists, blocklists, rate limiting, and Fail2Ban-style temporary bans
  • Optional tracking and PSR-14 events
  • Cache-based storage (PSR-16) using APCu or Redis for rate limiting

TYPO3 Integration: Firewall Extension

The Firewall for TYPO3 (firewall) extension integrates Phirewall into TYPO3 and adds support for the OWASP ModSecurity Core Rule Set (CRS) v4.20.0, plus TYPO3-specific tooling.

It includes:

  • Custom firewall rules managed in a PHP file
  • OWASP CRS loading and enforcement
  • TYPO3 backend module for managing simple block patterns
  • Diagnostics and observability features

What Was Implemented in TYPO3

TYPO3 installations can now apply request filtering and rate controls inside the application.

Unwanted requests are rejected before expensive TYPO3 processing starts.

Simple Examples (TYPO3 Context)

Phirewall is configured in a phirewall.php file and executed for every request.

Blocking Common Scanner Requests and Known Bot IPs

<?php

use Flowd\Phirewall\Config;
use Flowd\Phirewall\KeyExtractors;
use Flowd\Phirewall\Store\InMemoryCache;
use Predis\Client;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Http\Message\ServerRequestInterface;

return fn (EventDispatcherInterface $eventDispatcher) =>
(new Config(
    new InMemoryCache(),
    $eventDispatcher
))
    ->blocklist(
        name: 'evil-bot-ips',
        callback: function (ServerRequestInterface $request): bool {
            return in_array(KeyExtractors::ip()($request), [
                '176.65.149.61', // IP is only an example
                '45.13.214.201', // IP is only an example
            ], true);
        }
    )
    ->blocklist(
        name: 'blocked-uri-paths',
        callback: function (ServerRequestInterface $request): bool {
            $path = strtolower($request->getUri()->getPath());
            return str_contains($path, '/admin/');
        }
    )
    ->blocklist(
        name: 'blocked-uri-query-strings',
        callback: function (ServerRequestInterface $request): bool {
            $path = strtolower($request->getUri()->getQuery());
            return str_contains($path, 'xdebug')
                || str_contains($path, 'option=com_');
        }
    );

Temporary Blocking After Repeated Abuse (Fail2Ban)

<?php

use Flowd\Phirewall\Config;
use Flowd\Phirewall\KeyExtractors;
use Flowd\Phirewall\Store\RedisCache;
use Predis\Client;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Http\Message\ServerRequestInterface;

return fn (EventDispatcherInterface $eventDispatcher) =>
(new Config(
    new RedisCache(new Client('redis://redis:6379')),
    $eventDispatcher
))
    ->fail2ban(
        name: 'search-page-scrapers',
        threshold: 5,
        period: 10,
        ban: 60,
        filter: fn (ServerRequestInterface $request) => str_starts_with($request->getUri()->getPath(), '/search'),
        key: KeyExtractors::ip()
    );

Rate limiting with clear client feedback
<?php

use Flowd\Phirewall\Config;
use Flowd\Phirewall\KeyExtractors;
use Flowd\Phirewall\Store\RedisCache;
use Predis\Client;
use Psr\EventDispatcher\EventDispatcherInterface;

return fn (EventDispatcherInterface $eventDispatcher) =>
    (new Config(
        new RedisCache(new Client('redis://localhost:6379')),
        $eventDispatcher
    ))
    ->throttle(
        name: 'slow-down-to-10-requests-in-10-seconds',
        limit: 10,
        period: 10,
        key: KeyExtractors::ip()
    )
    ->enableRateLimitHeaders();

Cache Backend Requirement

Rate limiting and temporary bans require request counters that persist between requests.

Currently supported cache backends:

  • APCu — recommended for single-server setups
  • Redis — recommended for multi-server environments

Without APCu or Redis (Using the InMemoryCache backend):

  • Block rules still work
  • Throttling and Fail2Ban do not work as request counts cannot be stored

OWASP CRS Support

The Phirewall package can load and enforce the OWASP ModSecurity Core Rule Set v4.21.0, combined with custom TYPO3 rules.

How to Get It

Take a look at the TYPO3 Firewall extension documentation.

The Firewall for TYPO3 extension and its underlying firewall engine are available via Composer and can be accessed here:

Next Steps

This project delivers a foundation for application-level protection in TYPO3:

  • Reusable firewall engine
  • TYPO3 integration with OWASP CRS
  • Initial backend management tools

Future work will focus on rule presets, diagnostics, and further TYPO3-specific improvements.

Feedback and Discussions

I would be very happy to get your feedback, possible bug reports, and features requests. 

Feel free to reach out in the #ext-firewall channel on TYPO3 Slack or via GitHub Issues.