src/EventSubscriber/StartingRequestSubscriber.php line 78

Open in your IDE?
  1. <?php
  2. namespace App\EventSubscriber;
  3. use App\Entity\BannedIp;
  4. use Symfony\Component\HttpKernel\KernelEvents;
  5. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  6. use Twig\Environment;
  7. //Cache
  8. use Symfony\Component\Cache\Adapter\FilesystemAdapter;
  9. use Symfony\Contracts\Cache\ItemInterface;
  10. //Entity
  11. use Doctrine\ORM\EntityManagerInterface;
  12. use App\Entity\Connection;
  13. use App\Repository\BannedIpRepository;
  14. use App\Repository\ConnectionRepository;
  15. // Look like Hooks
  16. class StartingRequestSubscriber implements EventSubscriberInterface
  17. {
  18.     private $twig;
  19.     private $manager;
  20.     private $connectionRepository;
  21.     private $bannedIpRepository;
  22.     private $authorizedIps;
  23.     private $skipLogPrefixes = [
  24.         '/api',
  25.         '/build',
  26.         '/assets',
  27.         '/images',
  28.         '/img',
  29.         '/officials-imgs',
  30.         '/uploads',
  31.         '/fonts',
  32.         '/bundles',
  33.         '/i18n',
  34.         '/_profiler',
  35.         '/_wdt',
  36.         '/favicon.ico',
  37.         '/robots.txt',
  38.         '/sitemap',
  39.         '/user/heart-beat',
  40.         '/hub',
  41.     ];
  42.     private $rateLimitError 30// 10 erreurs
  43.     private $rateLimitErrorDuration 60 10// ... en 10 minutes
  44.     private $bannedDuration 60 60 24// ... donc banni 24h
  45.     public function __construct(
  46.         Environment $twig,
  47.         EntityManagerInterface $manager,
  48.         ConnectionRepository $connectionRepository,
  49.         BannedIpRepository $bannedIpRepository
  50.     ) {
  51.         $this->twig $twig;
  52.         $this->manager $manager;
  53.         $this->connectionRepository $connectionRepository;
  54.         $this->bannedIpRepository $bannedIpRepository;
  55.         $this->authorizedIps call_user_func(function () {
  56.             return [
  57.                 '92.144.142.121',
  58.                 '172.18.0.1'// local docker
  59.                 '172.20.0.1',
  60.             ];
  61.         });
  62.     }
  63.     public static function getSubscribedEvents()
  64.     {
  65.         // return the subscribed events, their methods and priorities
  66.         return [
  67.             KernelEvents::REQUEST => 'checkBannedIp',
  68.             // KernelEvents::TERMINATE : Appelé une seul fois après le traitement de la rêquete, evite les doublons dans la dbb
  69.             KernelEvents::TERMINATE => 'saveClientInfo',
  70.         ];
  71.     }
  72.     public function checkBannedIp(\Symfony\Component\HttpKernel\Event\RequestEvent $event)
  73.     {
  74.         if (!$event->isMainRequest()) {
  75.             return;
  76.         }
  77.         $request $event->getRequest();
  78.         $clientIp $request->getClientIp() ?? ($_SERVER['REMOTE_ADDR'] ?? '127.0.0.1');
  79.         $bannedMessage 'Banned'
  80.             '</br></br>Your IP address has been signaled and marked as potentially unreliable due to suspicious or malicious behavior.'
  81.             '</br>Please refer to this <a href="https://www.abuseipdb.com/">link</a> for more details and reclamations.';
  82.         $limitDate = (new \DateTime('now'))->sub(new \DateInterval('PT' $this->bannedDuration 'S'));
  83.         $isBanned count($this->bannedIpRepository->findAllWithLimitDateAndWithIp($limitDate$clientIp)) > 0;
  84.         if ($isBanned) {
  85.             $connec = new Connection();
  86.             $connec->setCreatedAt(new \DateTime());
  87.             $connec->setClientIp($clientIp);
  88.             $connec->setRequestUri($request->getRequestUri() ?? '');
  89.             $connec->setResponseStatus('BANNED'); // ADDED
  90.             // $this->addToHtaccesFile($_SERVER['REMOTE_ADDR']); // add to .htaccess for fast rejection
  91.             if (array_key_exists('HTTP_REFERER'$_SERVER)) {
  92.                 $connec->setHttpReferer($_SERVER['HTTP_REFERER']);
  93.             } else {
  94.                 $connec->setHttpReferer("No referer");
  95.             }
  96.             $this->manager->persist($connec);
  97.             $this->manager->flush();
  98.             die($bannedMessage);
  99.         }
  100.     }
  101.     public function addToHtaccesFile($ip)
  102.     {
  103.         // \sleep(1);
  104.         $filePath '/home/joeddqhp/public_html/.htaccess';
  105.         \file_put_contents(
  106.             $filePath,
  107.             \preg_replace(
  108.                 '/Require not ip(\s((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)\.?\b){4})+/',
  109.                 '$0 ' $ip,
  110.                 \file_get_contents($filePath)
  111.             )
  112.         );
  113.     }
  114.     public function saveClientInfo($event)
  115.     {
  116.         if (!$event->isMainRequest()) {
  117.             return;
  118.         }
  119.         $connec = new Connection();
  120.         $date = new \DateTime();
  121.         // Tout ceux avant la connection "id: 3312, date: 2021-02-21 06:53:30, ip: 46.193.4.149" sont soutrait de 5h de part la ligne qui suit
  122.         // $date->sub(new \DateInterval('PT5H'));
  123.         $connec->setCreatedAt($date);
  124.         $responseStatus = (string) $event->getResponse()->getStatusCode();
  125.         // dd($event->getRequest()->getContent());
  126.         $clientIp $event->getRequest()->getClientIp() ?? ($_SERVER['REMOTE_ADDR'] ?? '127.0.0.1');
  127.         $connec->setClientIp($clientIp);
  128.         $request $event->getRequest();
  129.         $requestUri $request->getRequestUri() ?? ($_SERVER['REQUEST_URI'] ?? '');
  130.         if ($this->shouldSkipLogging($requestUri)) {
  131.             return;
  132.         }
  133.         $connec->setRequestUri($requestUri);
  134.         $connec->setResponseStatus($responseStatus); // ADDED
  135.         if (array_key_exists('HTTP_REFERER'$_SERVER)) {
  136.             $connec->setHttpReferer($_SERVER['HTTP_REFERER']);
  137.         } else {
  138.             $connec->setHttpReferer("No referer");
  139.         }
  140.         $connec->setRequestData($this->safeContent($request));
  141.         $connec->setResponseData($this->safeResponseContent($event->getResponse()->getContent()));
  142.         // netoyer les logs associés a mon ip public
  143.         // DELETE FROM `connection` WHERE client_ip=
  144.         if (in_array($_SERVER['REMOTE_ADDR'], $this->authorizedIps)) {
  145.             $connec->setClientIp("moi (" mb_substr($_SERVER['REMOTE_ADDR'], 01) . '*.***.***.*' mb_substr($_SERVER['REMOTE_ADDR'], -2) . ')');
  146.         }
  147.         $this->manager->persist($connec);
  148.         $this->manager->flush();
  149.         // Get count of 404 in lastes request
  150.         if ($responseStatus === '404' || $responseStatus === '500') {
  151.             $limitDate = (new \DateTime('now'))->sub(new \DateInterval('PT' $this->rateLimitErrorDuration 'S'));
  152.             $count404 count($this->connectionRepository->findAllWithLimitDateAndWithIpAndWithStatus($limitDate$clientIp'404'));
  153.             $count500 count($this->connectionRepository->findAllWithLimitDateAndWithIpAndWithStatus($limitDate$clientIp'500'));
  154.             if ($count404 $count500 >= $this->rateLimitError) {
  155.                 // Add banned ip
  156.                 $bannedIp = new BannedIp();
  157.                 $bannedIp->setCreatedAt(new \DateTime());
  158.                 $bannedIp->setClientIp($clientIp);
  159.                 $this->manager->persist($bannedIp);
  160.                 $this->manager->flush();
  161.             }
  162.         }
  163.         // die();
  164.     }
  165.     private function shouldSkipLogging(string $requestUri): bool
  166.     {
  167.         foreach ($this->skipLogPrefixes as $prefix) {
  168.             if ($prefix !== '/' && str_starts_with($requestUri$prefix)) {
  169.                 return true;
  170.             }
  171.         }
  172.         return false;
  173.     }
  174.     private function safeContent(\Symfony\Component\HttpFoundation\Request $request): string
  175.     {
  176.         $contentType = (string) $request->headers->get('Content-Type''');
  177.         if (str_contains($contentType'multipart/form-data')) {
  178.             return '';
  179.         }
  180.         $content = (string) $request->getContent();
  181.         if ($content === '') {
  182.             return '';
  183.         }
  184.         if (strlen($content) > 4096) {
  185.             $content substr($content04096) . '...';
  186.         }
  187.         return \json_encode($content);
  188.     }
  189.     private function safeResponseContent(string $content): string
  190.     {
  191.         if ($content === '') {
  192.             return '';
  193.         }
  194.         if (strlen($content) > 4096) {
  195.             $content substr($content04096) . '...';
  196.         }
  197.         return \json_encode($content);
  198.     }
  199. }