src/Event/ForceBruteListener.php line 117

Open in your IDE?
  1. <?php
  2. namespace App\Event;
  3. use Symfony\Component\HttpFoundation\Response;
  4. use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
  5. use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
  6. use Symfony\Component\Security\Core\Security;
  7. // use Symfony\Bundle\FrameworkBundle\Routing\Router;
  8. // use Symfony\Component\HttpFoundation\RedirectResponse;
  9. use App\PaaBundle\Entity\adressesIP ;
  10. use App\PaaBundle\Entity\adressesIP_Evenements ;
  11. use App\PaaBundle\Component\Logs;
  12. use DateTime;
  13. // Gestionnaire de page not found, pour blacklistage des attaques par force brute
  14. // Erreurs NotFoundHttpException : test par un robot de toutes les URLS classiques
  15. // Erreurs d'authentification (A VENIR)
  16. class ForceBruteListener {
  17.     /**
  18.      * @var string
  19.      */
  20.     private $project_dir;
  21.     private $entityManager;
  22.     private $security;
  23.     public function __construct($project_dir$doctrineSecurity $security) {
  24.         $this->project_dir $project_dir;
  25.         $this->entityManager $doctrine->getManager() ;
  26.         $this->security $security ;
  27.     }
  28.     // Capture des exceptions "page not found"
  29.     public function onKernelException(GetResponseForExceptionEvent $event) {
  30.         if (
  31. //            $_ENV['APP_ENV'] != 'prod' || 
  32.             !$event->isMasterRequest() || 
  33.             !$event->getException() instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
  34.         ) {
  35.             // Nous ne sommes pas en prod, ou ce n'est pas la requête ppale, ou ce n'est pas une exception "File not found"
  36.             return;
  37.         }
  38.         // A partir de là, on traite les exceptions "Page non trouvée"
  39.         $request $event->getRequest() ;
  40.         
  41.         if ($request->getRequestUri() == "/favicon.ico") {
  42.             // Pas à pendre en compte : arrive toujours en prod
  43.             return ;
  44.         }
  45.         if ($this->security->getToken()) {
  46.             // Cette condition ne semble jamais respectée et c'est dommage car on ne peut pas savoir si le user est authentifié
  47.             // et que dans ce cas on ne devrait pas bannir cette adresse IP car l'erreur "Route not found" est plus probablement due à un bug de l'application (route bosolète/mal configurée/...)
  48.             // Du coup, on est obligés de passer par la vérification que cette IP a été authentifiée au moins une fois dans le passé ci-dessous ($loAdresseIP->estAuthentifiéeAuMoinsUneFois())
  49.             // C'est src/PaaBundle/Component/Authentication/Handler/LoginSuccessHandler.php qui pose le flag en question
  50.             $user $this->security->getUser();
  51.             // L'objet sécurité est à jour avec le token d'authentification, on peut donc vérifier si l'utilisateur en cours est bien authentifié
  52.             // $user = $this->security->getUser();
  53.             $lbAuthentifié $this->security->isGranted('IS_AUTHENTICATED_FULLY') ;
  54.             if ($lbAuthentifié) {
  55.                 // Puisque l'utilisateur est authentifié, on ne devrait pas le bannir s'il s'agit d'un bug de l'application et non d'une tentative de force brute
  56.                 return ;
  57.             }
  58.         }
  59.         $ip $request->getClientIp() ;
  60.         $loRepo $this->entityManager->getRepository("PaaBundle:adressesIP");
  61.         if (!$loRepo->estUtilisable()) {
  62.             // La table AdressesIP n'est pas encore créée
  63.             return ;
  64.         }
  65.         $loAdresseIP $loRepo->findByIP($ip) ;
  66.         if (!$loAdresseIP) {
  67.             // Adresse IP pas encore référencée : l'ajouter maintenant
  68.             $loAdresseIP = new adressesIP() ;
  69.             $loAdresseIP->setCIP($ip) ;
  70.         } else if ($loAdresseIP->estAuthentifiéeAuMoinsUneFois()) {
  71.             // Cette adresse a déja été authentifiée au moins une fois (un utilisateur s'est authentifié avec succès à partir de cette adresse)
  72.             // On ne va pas bannir cette adresse IP car l'erreur "Route not found" est plus probablement due à un bug de l'application (route bosolète/mal configurée/...)
  73.             return ;
  74.         }
  75.         // Ajouter l'événement à l'historique de cette adresse IP
  76.         $loEvenement = new adressesIP_Evenements() ;
  77.         $loEvenement->setAdresseIP($loAdresseIP) ;
  78.         $loEvenement->setTEvenement(new DateTime()) ;
  79.         $loEvenement->setITypeEvenement($loRepo::TYPE_ROUTENOTFOUND) ;
  80.         $loEvenement->setCDescEvenement($request->getRequestUri()) ;
  81.         $this->entityManager->persist($loEvenement) ;
  82.         $this->entityManager->flush() ;
  83.         $lbBanni $loRepo->updateABannir($loAdresseIP) ;
  84.         if ($lbBanni) {
  85.             // Cette adresse est bannie
  86.             // Logger le banissement
  87.             $message "L'adresse IP $ip vient d'être bannie pour dépassement du nombre max d'événements.";
  88.             $this->log($message) ;
  89.             // Renvoyer une réponse ici évite de passer dans les gestionnaires d'erreur suivants : c'est donc cette réponse qui sera renvoyée au client
  90.             $response = new Response($this->getMsg($ip), Response::HTTP_FORBIDDEN);
  91.             $event->setResponse($response);
  92.             return ;
  93.         }
  94.         // Si on arrive jusque là, le traitement normal de l'exception va se poursuivre
  95.         return ;
  96.     }
  97.     // Vérification sur chaque requête que l'adresse n'est pas bannie
  98.     public function onKernelController(FilterControllerEvent $event) {
  99.         if (!$event->isMasterRequest()) {
  100.             // Cette requête n'est pas la requête principale
  101.             return ;
  102.         }
  103.         // if ($event->getController() && isset($event->getController()[1]) && isset($event->getController()[1]) == "toolbarAction") {
  104.         //     // Rien à faire dans ce contexte
  105.         //     return ;
  106.         // }
  107.         if ($this->security->getToken()) {
  108.             // L'objet sécurité est à jour avec le token d'authentification, on peut donc vérifier si l'utilisateur en cours est bien authentifié
  109.             // $user = $this->security->getUser();
  110.             $lbAuthentifié $this->security->isGranted('IS_AUTHENTICATED_FULLY') ;
  111.             if ($lbAuthentifié) {
  112.                 // Puisque l'utilisateur est authentifié, pas besoin de vérifier s'il est banni
  113.                 return ;
  114.             }
  115.         }
  116.         $request $event->getRequest() ;
  117.         $ip $request->getClientIp() ;
  118.         $loRepo $this->entityManager->getRepository("PaaBundle:adressesIP");
  119.         if (!$loRepo->estUtilisable()) {
  120.             // La table AdressesIP n'est pas encore créée
  121.             return ;
  122.         }
  123.         $loAdresseIP $loRepo->findByIP($ip) ;
  124.         if ($loAdresseIP && $loAdresseIP->estBannie()) {
  125.             // Cette adresse IP est bannie
  126.             // Logger la tentative
  127.             $message "L'adresse IP $ip est bannie mais fait une tentative d'accès.";
  128.             $this->log($message) ;
  129.             // Terminer en erreur
  130.             throw new \Exception($this->getMsg($ip)) ;
  131.         }
  132.         // Si on arrive jusque là, le traitement normal va se poursuivre
  133.         return ;
  134.     }
  135.     // Renvoyer le message d'erreur
  136.     private function getMsg($ip) {
  137.         return "Désolé, l'adresse IP que vous utilisez ($ip) a été bannie pour cette application en raison d'un trop grand nombre de tentatives infructueuses. Merci de demander à un administrateur de vous aider à rétablir la situation." ;
  138.     }
  139.     // Logguer un message
  140.     private function log($msg) {
  141.         Logs::setProjectDir($this->project_dir) ;
  142.         Logs::setStackTrace("") ;
  143.         Logs::critical($msg, [], true) ;
  144.     }
  145. }