Feature: #109080 - Unified RateLimiterFactory with admin overrides
See forge#109080
Description
TYPO3's
\TYPO3\ has been
refactored to serve as the single entry point for rate limiting across the
system. A new
\TYPO3\
extends Symfony's
Rate with additional
convenience methods for request-based and login rate limiting.
Previously, backend and frontend password recovery features and Extbase rate limiting each created Symfony rate limiter factories, bypassing TYPO3's factory. All consumers now use the central TYPO3 factory, which enables a unified admin override mechanism.
Extension developers should type-hint against
Rate
when injecting the factory.
Admin overrides via TYPO3_CONF_VARS
A new configuration option
$GLOBALS
allows administrators to override any rate limiter's settings by its ID. Each
key is a limiter ID, and each value is an array of settings to override:
$GLOBALS['TYPO3_CONF_VARS']['SYS']['rateLimiter']['login-be'] = [
'limit' => 3,
'interval' => '5 minutes',
];
$GLOBALS['TYPO3_CONF_VARS']['SYS']['rateLimiter']['backend-password-recovery'] = [
'limit' => 1,
'interval' => '1 hour',
];
Known limiter IDs:
login-— backend loginbe login-— frontend loginfe backend-— backend password resetpassword- recovery felogin-— frontend password recoverypassword- recovery extbase-<class— ExtbaseSlug>-<action Method> #actions[Rate Limit]
Example limiter ID for Extbase action
The limiter ID for an Extbase action with the
#
attribute is constructed using the "slugified" class name and the action
method name.
namespace Vendor\MyExtension\Controller;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Attribute\RateLimit;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class MyController extends ActionController
{
#[RateLimit(
limit: 5,
interval: '10 minutes',
message: 'ratelimit.dosomething',
)]
public function doSomethingAction(): ResponseInterface
{
return $this->redirect('index');
}
}
The limiter ID for the action is
extbase-
General-purpose rate limiting
Extension developers can now use the factory for custom rate limiting needs.
The
create method is the recommended entry point
for request-scoped rate limiting. It extracts the client's
remote IP from the PSR-7 request and uses it as the limiter key:
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\RateLimiter\RateLimiterFactoryInterface;
class MyService
{
public function __construct(
private readonly RateLimiterFactoryInterface $rateLimiterFactory,
) {}
public function doSomething(ServerRequestInterface $request): void
{
$limiter = $this->rateLimiterFactory->createRequestBasedLimiter(
$request,
[
'id' => 'my-extension-action',
'policy' => 'sliding_window',
'limit' => 10,
'interval' => '1 hour',
],
);
$limit = $limiter->consume();
if (!$limit->isAccepted()) {
// Handle rate limit exceeded
}
}
}
In cases where a custom key is needed, for example a user ID instead of the
IP address, the
create method accepts an explicit
configuration array and key:
$limiter = $this->rateLimiterFactory->createLimiter(
[
'id' => 'my-extension-action',
'policy' => 'sliding_window',
'limit' => 10,
'interval' => '1 hour',
],
$userId,
);
Preconfigured named services can also be defined in Services..
They are then injectable with the
create method from the
Rate:
myRateLimiter:
class: TYPO3\CMS\Core\RateLimiter\RateLimiterFactory
arguments:
$config:
id: 'my-custom-limiter'
policy: 'sliding_window'
limit: 5
interval: '10 minutes'
Impact
All rate limiting in TYPO3 now flows through a single factory that supports
admin-level overrides. Administrators can tune or restrict rate limits for any
component—login, password recovery, Extbase actions, or custom extensions—
without modifying code, using
$GLOBALS.
The login rate limiter now uses human-readable IDs (login-, login-)
instead of SHA1 hashes. Existing cached rate limit state from previous
versions will expire naturally.