1<?php 2 3declare(strict_types=1); 4 5/* 6 * This file is part of the TYPO3 CMS project. 7 * 8 * It is free software; you can redistribute it and/or modify it under 9 * the terms of the GNU General Public License, either version 2 10 * of the License, or any later version. 11 * 12 * For the full copyright and license information, please read the 13 * LICENSE.txt file that was distributed with this source code. 14 * 15 * The TYPO3 project - inspiring people to share! 16 */ 17 18namespace TYPO3\CMS\Core\RateLimiter; 19 20use Psr\Http\Message\ServerRequestInterface; 21use Symfony\Component\RateLimiter\LimiterInterface; 22use Symfony\Component\RateLimiter\RateLimiterFactory as SymfonyRateLimiterFactory; 23use Symfony\Component\RateLimiter\Storage\InMemoryStorage; 24use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication; 25use TYPO3\CMS\Core\Http\NormalizedParams; 26use TYPO3\CMS\Core\RateLimiter\Storage\CachingFrameworkStorage; 27use TYPO3\CMS\Core\Utility\GeneralUtility; 28 29/** 30 * @internal This is not part of the official TYPO3 Core API due to a limitation of the experimental Symfony Rate Limiter API. 31 */ 32class RateLimiterFactory 33{ 34 public function createLoginRateLimiter(AbstractUserAuthentication $userAuthentication, ServerRequestInterface $request): LimiterInterface 35 { 36 $loginType = $userAuthentication->loginType; 37 $normalizedParams = $request->getAttribute('normalizedParams') ?? NormalizedParams::createFromRequest($request); 38 $remoteIp = $normalizedParams->getRemoteAddress(); 39 $limiterId = sha1('typo3-login-' . $loginType); 40 $limit = (int)($GLOBALS['TYPO3_CONF_VARS'][$loginType]['loginRateLimit'] ?? 5); 41 $interval = $GLOBALS['TYPO3_CONF_VARS'][$loginType]['loginRateLimitInterval'] ?? '15 minutes'; 42 43 // If not enabled, return a null limiter 44 $enabled = !$this->isIpExcluded($loginType, $remoteIp) && $limit > 0; 45 46 $config = [ 47 'id' => $limiterId, 48 'policy' => ($enabled ? 'sliding_window' : 'no_limit'), 49 'limit' => $limit, 50 'interval' => $interval, 51 ]; 52 $storage = ($enabled ? GeneralUtility::makeInstance(CachingFrameworkStorage::class) : new InMemoryStorage()); 53 $limiterFactory = new SymfonyRateLimiterFactory( 54 $config, 55 $storage 56 ); 57 return $limiterFactory->create($remoteIp); 58 } 59 60 protected function isIpExcluded(string $loginType, string $remoteAddress): bool 61 { 62 $ipMask = trim($GLOBALS['TYPO3_CONF_VARS'][$loginType]['loginRateLimitIpExcludeList'] ?? ''); 63 return GeneralUtility::cmpIP($remoteAddress, $ipMask); 64 } 65} 66