platform/src/Storefront/Framework/Csrf/CsrfRouteListener.php line 63

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Storefront\Framework\Csrf;
  3. use Shopware\Core\Framework\Routing\Annotation\RouteScope;
  4. use Shopware\Core\Framework\Routing\KernelListenerPriorities;
  5. use Shopware\Core\SalesChannelRequest;
  6. use Shopware\Storefront\Framework\Csrf\Exception\InvalidCsrfTokenException;
  7. use Shopware\Storefront\Framework\Routing\StorefrontRouteScope;
  8. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  9. use Symfony\Component\HttpFoundation\Request;
  10. use Symfony\Component\HttpKernel\Event\ControllerEvent;
  11. use Symfony\Component\HttpKernel\KernelEvents;
  12. use Symfony\Component\Security\Csrf\CsrfToken;
  13. use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
  14. use Symfony\Contracts\Translation\TranslatorInterface;
  15. class CsrfRouteListener implements EventSubscriberInterface
  16. {
  17.     /**
  18.      * @var bool
  19.      */
  20.     protected $csrfEnabled;
  21.     /**
  22.      * @var string
  23.      */
  24.     protected $csrfMode;
  25.     private CsrfTokenManagerInterface $csrfTokenManager;
  26.     /**
  27.      * Used to track if the csrf token has already been check for the request
  28.      */
  29.     private bool $csrfChecked false;
  30.     /**
  31.      * @var TranslatorInterface
  32.      */
  33.     private $translator;
  34.     public function __construct(
  35.         CsrfTokenManagerInterface $csrfTokenManager,
  36.         bool $csrfEnabled,
  37.         string $csrfMode,
  38.         TranslatorInterface $translator
  39.     ) {
  40.         $this->csrfTokenManager $csrfTokenManager;
  41.         $this->csrfEnabled $csrfEnabled;
  42.         $this->translator $translator;
  43.         $this->csrfMode $csrfMode;
  44.     }
  45.     public static function getSubscribedEvents(): array
  46.     {
  47.         return [
  48.             KernelEvents::CONTROLLER => [
  49.                 ['csrfCheck'KernelListenerPriorities::KERNEL_CONTROLLER_EVENT_CONTEXT_RESOLVE_PRE],
  50.             ],
  51.         ];
  52.     }
  53.     public function csrfCheck(ControllerEvent $event): void
  54.     {
  55.         if (!$this->csrfEnabled || $this->csrfChecked === true) {
  56.             return;
  57.         }
  58.         $request $event->getRequest();
  59.         if ($request->attributes->get(SalesChannelRequest::ATTRIBUTE_CSRF_PROTECTEDtrue) === false) {
  60.             return;
  61.         }
  62.         if ($request->getMethod() !== Request::METHOD_POST) {
  63.             return;
  64.         }
  65.         /** @var RouteScope|null $routeScope */
  66.         $routeScope $request->attributes->get('_routeScope');
  67.         // Only check csrf token on storefront routes
  68.         if ($routeScope === null || !$routeScope->hasScope(StorefrontRouteScope::ID)) {
  69.             return;
  70.         }
  71.         $this->validateCsrfToken($request);
  72.     }
  73.     public function validateCsrfToken(Request $request): void
  74.     {
  75.         $this->csrfChecked true;
  76.         $submittedCSRFToken = (string) $request->request->get('_csrf_token');
  77.         if ($this->csrfMode === CsrfModes::MODE_TWIG) {
  78.             $intent = (string) $request->attributes->get('_route');
  79.         } else {
  80.             $intent 'ajax';
  81.         }
  82.         $csrfCookies = (array) $request->cookies->get('csrf');
  83.         if (
  84.             (!isset($csrfCookies[$intent]) || $csrfCookies[$intent] !== $submittedCSRFToken)
  85.             && !$this->csrfTokenManager->isTokenValid(new CsrfToken($intent$submittedCSRFToken))
  86.         ) {
  87.             $session $request->getSession();
  88.             /* @see https://github.com/symfony/symfony/issues/41765 */
  89.             if (method_exists($session'getFlashBag')) {
  90.                 if ($request->isXmlHttpRequest()) {
  91.                     $session->getFlashBag()->add('danger'$this->translator->trans('error.message-403-ajax'));
  92.                 } else {
  93.                     $session->getFlashBag()->add('danger'$this->translator->trans('error.message-403'));
  94.                 }
  95.             }
  96.             throw new InvalidCsrfTokenException();
  97.         }
  98.     }
  99. }