custom/plugins/ValkarchFormBotBlocker/src/Subscriber/RequestSubscriber.php line 136

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Valkarch\FormBotBlocker\Subscriber;
  3. use Exception;
  4. use Shopware\Core\PlatformRequest;
  5. use Shopware\Core\Framework\Context;
  6. use Symfony\Component\HttpKernel\KernelEvents;
  7. use Symfony\Component\HttpKernel\Event\KernelEvent;
  8. use Symfony\Component\HttpKernel\Event\RequestEvent;
  9. use Shopware\Storefront\Framework\Routing\RequestTransformer;
  10. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  11. use Valkarch\FormBotBlocker\Service\FormService;
  12. use Valkarch\FormBotBlocker\Service\BlockService;
  13. use Valkarch\FormBotBlocker\Service\PluginService;
  14. use Valkarch\FormBotBlocker\Service\BotLogService;
  15. use Valkarch\FormBotBlocker\Service\CaptchaService;
  16. use Valkarch\FormBotBlocker\Service\HoneypotService;
  17. use Valkarch\FormBotBlocker\Service\ReactionTimeService;
  18. use Valkarch\FormBotBlocker\Service\ExternalPluginsService;
  19. use Valkarch\FormBotBlocker\Content\FormSubmit\FormSubmitDefinition;
  20. use Valkarch\FormBotBlocker\Content\BotLogProtector\BotLogProtectorEntity;
  21. class RequestSubscriber implements EventSubscriberInterface
  22. {
  23.     /**
  24.      * @var FormService
  25.      */
  26.     private FormService $formService;
  27.     /**
  28.      * @var BlockService
  29.      */
  30.     private BlockService $blockService;
  31.     /**
  32.      * @var PluginService
  33.      */
  34.     private PluginService $pluginService;
  35.     /**
  36.      * @var BotLogService
  37.      */
  38.     private BotLogService $botLogService;
  39.     /**
  40.      * @var CaptchaService
  41.      */
  42.     private CaptchaService $captchaService;
  43.     /**
  44.      * @var HoneypotService
  45.      */
  46.     private HoneypotService $honeypotService;
  47.     /**
  48.      * @var ReactionTimeService
  49.      */
  50.     private ReactionTimeService $reactionTimeService;
  51.     /**
  52.      * @var ExternalPluginsService
  53.      */
  54.     private ExternalPluginsService $externalPluginsService;
  55.     /**
  56.      * RequestSubscriber constructor.
  57.      *
  58.      * @param FormService $formService
  59.      * @param BlockService $blockService
  60.      * @param PluginService $pluginService
  61.      * @param BotLogService $botLogService
  62.      * @param CaptchaService $captchaService
  63.      * @param HoneypotService $honeypotService
  64.      * @param ReactionTimeService $reactionTimeService
  65.      * @param ExternalPluginsService $externalPluginsService
  66.      */
  67.     public function __construct(
  68.         FormService $formService,
  69.         BlockService $blockService,
  70.         PluginService $pluginService,
  71.         BotLogService $botLogService,
  72.         CaptchaService $captchaService,
  73.         HoneypotService $honeypotService,
  74.         ReactionTimeService $reactionTimeService,
  75.         ExternalPluginsService $externalPluginsService
  76.     )
  77.     {
  78.         $this->formService $formService;
  79.         $this->blockService $blockService;
  80.         $this->pluginService $pluginService;
  81.         $this->botLogService $botLogService;
  82.         $this->captchaService $captchaService;
  83.         $this->honeypotService $honeypotService;
  84.         $this->reactionTimeService $reactionTimeService;
  85.         $this->externalPluginsService $externalPluginsService;
  86.     }
  87.     /**
  88.      * The event names to listen to
  89.      *
  90.      * @return array<string, string>
  91.      */
  92.     public static function getSubscribedEvents(): array
  93.     {
  94.         return [
  95.             RequestEvent::class => 'onRequest',
  96.             KernelEvents::CONTROLLER => ['onKernelController', -19]
  97.         ];
  98.     }
  99.     /**
  100.      * Deactivate captcha validation if needed
  101.      * Use before KERNEL_CONTROLLER_EVENT_SCOPE_VALIDATE = -20;
  102.      *
  103.      * @param KernelEvent $event
  104.      */
  105.     public function onKernelController(KernelEvent $event): void
  106.     {
  107.         if ($event->getRequest()->hasSession() &&
  108.             ($session $event->getRequest()->getSession())->isStarted() &&
  109.             $session->has(PluginService::SESSION_FORM_BOT_BLOCKER_CAPTCHA_STATUS)
  110.         ) {
  111.             $session->remove(PluginService::SESSION_FORM_BOT_BLOCKER_CAPTCHA_STATUS);
  112.             // Deactivate captcha validation
  113.             $event->getRequest()->attributes->set(PlatformRequest::ATTRIBUTE_CAPTCHAfalse);
  114.         }
  115.     }
  116.     /**
  117.      * Evaluation of the protective measures
  118.      *
  119.      * @param RequestEvent $event
  120.      * @throws Exception
  121.      */
  122.     public function onRequest(RequestEvent $event): void
  123.     {
  124.         $request $event->getRequest();
  125.         $route $request->attributes->get('_route');
  126.         // Unsupported requests
  127.         if (!$route ||
  128.             $request->getMethod() != 'POST' ||
  129.             $request->request->count() === 0
  130.         ) {
  131.             return;
  132.         }
  133.         $routeFormLogName $this->formService->getRouteFormLogName($route);
  134.         // Unsupported forms
  135.         if (!$routeFormLogName) {
  136.             return;
  137.         }
  138.         $session $request->getSession();
  139.         $ip $this->formService->getRealIpAddr();
  140.         $context Context::createDefaultContext();
  141.         // Is ip blocked
  142.         if ($this->blockService->isIpBlocked($ip$context)) {
  143.             $response $this->formService->getResponse($session$request200'invalid_ip''formBotBlocker.errors.ipBlocked');
  144.             $event->setResponse($response);
  145.             return;
  146.         }
  147.         $salesChannelId $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_ID);
  148.         // Not activating forms
  149.         if (!$this->formService->isFormToProtectActive($route$salesChannelId)) {
  150.             return;
  151.         }
  152.         // Not supported external plugin versioned form
  153.         if ($this->externalPluginsService->isVersionedForm($request->request)) {
  154.             if (!$this->externalPluginsService->isSupportedVersionedForm($request->request$context)) {
  155.                 return;
  156.             }
  157.         }
  158.         $requestParams $request->request->all();
  159.         $requestUrl $request->attributes->get(RequestTransformer::ORIGINAL_REQUEST_URI);
  160.         $shopUrl $request->attributes->get(RequestTransformer::SALES_CHANNEL_ABSOLUTE_BASE_URL);
  161.         // Is simple honeypot active
  162.         if ($this->pluginService->isSimpleHoneypotProtectorActive($salesChannelId)) {
  163.             // Simple honeypot protector
  164.             if (!$this->honeypotService->isValidSimpleHoneypot($request->request)) {
  165.                 $this->botLogService->saveLog($ip,
  166.                     $shopUrl,
  167.                     $routeFormLogName,
  168.                     BotLogProtectorEntity::SIMPLE_HONEYPOT_PROTECTOR,
  169.                     $requestUrl,
  170.                     $requestParams,
  171.                     $salesChannelId,
  172.                     $context
  173.                 );
  174.                 $response $this->formService->getResponse($session$request200'invalid_input''formBotBlocker.errors.honeypot');
  175.                 $event->setResponse($response);
  176.                 return;
  177.             }
  178.         }
  179.         // Is dynamic honeypot active
  180.         if ($this->pluginService->isDynamicHoneypotProtectorActive($salesChannelId)) {
  181.             // Dynamic honeypot protector
  182.             if (!$this->honeypotService->isValidDynamicHoneypot($request->request$context)) {
  183.                 $this->botLogService->saveLog($ip,
  184.                     $shopUrl,
  185.                     $routeFormLogName,
  186.                     BotLogProtectorEntity::DYNAMIC_HONEYPOT_PROTECTOR,
  187.                     $requestUrl,
  188.                     $requestParams,
  189.                     $salesChannelId,
  190.                     $context
  191.                 );
  192.                 $response $this->formService->getResponse($session$request200'invalid_input''formBotBlocker.errors.honeypot');
  193.                 $event->setResponse($response);
  194.                 return;
  195.             }
  196.         }
  197.         // Is reaction time active
  198.         if ($this->pluginService->isReactionTimeProtectorActive($salesChannelId)) {
  199.             // Reaction time protector
  200.             if (!$this->reactionTimeService->isValidReactionTime($session$salesChannelId)) {
  201.                 $this->botLogService->saveLog($ip,
  202.                     $shopUrl,
  203.                     $routeFormLogName,
  204.                     BotLogProtectorEntity::REACTION_TIME_PROTECTOR,
  205.                     $requestUrl,
  206.                     $requestParams,
  207.                     $salesChannelId,
  208.                     $context
  209.                 );
  210.                 $response $this->formService->getResponse($session$request200'invalid_reaction_time''formBotBlocker.errors.reactionTime');
  211.                 $event->setResponse($response);
  212.                 return;
  213.             }
  214.         }
  215.         $actionProtectorRouteFormLogName $this->formService->getActionProtectorRouteFormLogName($route);
  216.         if ($actionProtectorRouteFormLogName) {
  217.             // Is form action protector active
  218.             if ($this->pluginService->isFormActionProtectorActive($salesChannelId)) {
  219.                 $this->botLogService->saveLog($ip,
  220.                     $shopUrl,
  221.                     $actionProtectorRouteFormLogName,
  222.                     BotLogProtectorEntity::FORM_ACTION_PROTECTOR,
  223.                     $requestUrl,
  224.                     $requestParams,
  225.                     $salesChannelId,
  226.                     $context
  227.                 );
  228.                 $response $this->formService->getResponse($session$request200'invalid_form''formBotBlocker.errors.formAction');
  229.                 $event->setResponse($response);
  230.                 return;
  231.             }
  232.         }
  233.         // Is captcha defer active
  234.         if ($this->pluginService->isCaptchaDeferActive($salesChannelId)) {
  235.             // Set deactivate captcha validation flag in session
  236.             if (!$this->captchaService->isCaptchaActive($ip$salesChannelId$context)) {
  237.                 $session->set(PluginService::SESSION_FORM_BOT_BLOCKER_CAPTCHA_STATUSfalse);
  238.             }
  239.             // Save form submits
  240.             $writtenEvent $this->captchaService->saveFormSubmit($ip$salesChannelId$context);
  241.             // Automatically delete old form submits of all ip's
  242.             if ($formSubmitEvent $writtenEvent->getEventByEntityName(FormSubmitDefinition::ENTITY_NAME)) {
  243.                 $submits $formSubmitEvent->getWriteResults()[0]->getPayload()['submits'];
  244.                 // Delete when someone has made 3 summits
  245.                 if ($submits === 3) {
  246.                     $this->captchaService->deleteOldFormSubmitsOfAllUsers($salesChannelId$context);
  247.                 }
  248.             }
  249.         }
  250.     }
  251. }