VYPR
High severityNVD Advisory· Published Mar 6, 2024· Updated Aug 5, 2024

Shopware's session is persistent in Cache for 404 pages

CVE-2024-27917

Description

Shopware is an open commerce platform based on Symfony Framework and Vue. The Symfony Session Handler pops the Session Cookie and assigns it to the Response. Since Shopware 6.5.8.0, the 404 pages are cached to improve the performance of 404 pages. So the cached Response which contains a Session Cookie when the Browser accessing the 404 page, has no cookies yet. The Symfony Session Handler is in use, when no explicit Session configuration has been done. When Redis is in use for Sessions using the PHP Redis extension, this exploiting code is not used. Shopware version 6.5.8.7 contains a patch for this issue. As a workaround, use Redis for Sessions, as this does not trigger the exploit code.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
shopware/storefrontPackagist
>= 6.5.8.0, < 6.5.8.76.5.8.7
shopware/platformPackagist
>= 6.5.8.0, < 6.5.8.76.5.8.7

Affected products

1

Patches

2
7d9cb03225ef

NEXT-34113 - Clear cookies on 404 pages

https://github.com/shopware/shopwareSoner SayakciMar 4, 2024via ghsa
6 files changed · +84 4
  • changelog/_unreleased/2024-03-04-clear-cookies-on-404-pages.md+9 0 added
    @@ -0,0 +1,9 @@
    +---
    +title: Clear cookies on 404 pages
    +issue: NEXT-34113
    +---
    +
    +# Core
    +
    +* Changed `\Shopware\Storefront\Framework\Routing\NotFound\NotFoundSubscriber` to remove all sessions cookies, added by `\Symfony\Component\HttpFoundation\Session\Storage\Handler\AbstractSessionHandler`
    +
    
  • src/Storefront/DependencyInjection/services.xml+1 0 modified
    @@ -182,6 +182,7 @@
                 <argument type="service" id="Shopware\Core\Framework\DataAbstractionLayer\Cache\EntityCacheKeyGenerator"/>
                 <argument type="service" id="Shopware\Core\Framework\Adapter\Cache\CacheInvalidator"/>
                 <argument type="service" id="event_dispatcher"/>
    +            <argument>%session.storage.options%</argument>
     
                 <tag name="kernel.event_subscriber"/>
                 <tag name="kernel.reset" method="reset"/>
    
  • src/Storefront/Framework/Routing/NotFound/NotFoundSubscriber.php+13 1 modified
    @@ -42,10 +42,13 @@ class NotFoundSubscriber implements EventSubscriberInterface, ResetInterface
          */
         private bool $handled = false;
     
    +    private string $sessionName;
    +
         /**
          * @internal
          *
          * @param AbstractCacheTracer<Response> $cacheTracer
    +     * @param array{name?: string} $sessionOptions
          */
         public function __construct(
             private readonly HttpKernelInterface $httpKernel,
    @@ -55,8 +58,10 @@ public function __construct(
             private readonly AbstractCacheTracer $cacheTracer,
             private readonly EntityCacheKeyGenerator $generator,
             private readonly CacheInvalidator $cacheInvalidator,
    -        private readonly EventDispatcherInterface $eventDispatcher
    +        private readonly EventDispatcherInterface $eventDispatcher,
    +        array $sessionOptions = []
         ) {
    +        $this->sessionName = $sessionOptions['name'] ?? 'session-';
         }
     
         public static function getSubscribedEvents(): array
    @@ -123,6 +128,13 @@ public function onError(ExceptionEvent $event): void
                     $response->setContext(null);
                 }
     
    +            // Remove session cookie from 404 pages, injected by the Symfony session listener
    +            foreach ($response->headers->getCookies() as $cookie) {
    +                if ($cookie->getName() === $this->sessionName) {
    +                    $response->headers->removeCookie($cookie->getName(), $cookie->getPath(), $cookie->getDomain());
    +                }
    +            }
    +
                 return $response;
             });
     
    
  • tests/unit/Storefront/Framework/Routing/NotFound/NotFoundPageCacheKeyEventTest.php+1 1 renamed
    @@ -1,6 +1,6 @@
     <?php declare(strict_types=1);
     
    -namespace Shopware\Storefront\Test\Framework\Routing\NotFound;
    +namespace Shopware\Tests\Unit\Storefront\Framework\Routing\NotFound;
     
     use PHPUnit\Framework\TestCase;
     use Shopware\Core\Framework\Context;
    
  • tests/unit/Storefront/Framework/Routing/NotFound/NotFoundPageTagsEventTest.php+1 1 renamed
    @@ -1,6 +1,6 @@
     <?php declare(strict_types=1);
     
    -namespace Shopware\Storefront\Test\Framework\Routing\NotFound;
    +namespace Shopware\Tests\Unit\Storefront\Framework\Routing\NotFound;
     
     use PHPUnit\Framework\TestCase;
     use Shopware\Core\Framework\Context;
    
  • tests/unit/Storefront/Framework/Routing/NotFound/NotFoundSubscriberTest.php+59 1 renamed
    @@ -1,6 +1,6 @@
     <?php declare(strict_types=1);
     
    -namespace Shopware\Storefront\Test\Framework\Routing\NotFound;
    +namespace Shopware\Tests\Unit\Storefront\Framework\Routing\NotFound;
     
     use PHPUnit\Framework\TestCase;
     use Shopware\Core\Framework\Adapter\Cache\AbstractCacheTracer;
    @@ -14,6 +14,7 @@
     use Symfony\Component\Cache\Adapter\ArrayAdapter;
     use Symfony\Component\Cache\Adapter\TagAwareAdapter;
     use Symfony\Component\EventDispatcher\EventDispatcher;
    +use Symfony\Component\HttpFoundation\Cookie;
     use Symfony\Component\HttpFoundation\Request;
     use Symfony\Component\HttpFoundation\RequestStack;
     use Symfony\Component\HttpFoundation\Response;
    @@ -97,6 +98,63 @@ public function testErrorHandled(): void
             static::assertInstanceOf(Response::class, $response);
         }
     
    +    public function testCookiesAreNotPersistedToNotFoundPages(): void
    +    {
    +        $httpKernel = $this->createMock(HttpKernelInterface::class);
    +        $response = new Response();
    +        $response->headers->setCookie(new Cookie('extension-cookie', '1'));
    +        $response->headers->setCookie(new Cookie('session-', '1'));
    +        $httpKernel
    +            ->expects(static::once())
    +            ->method('handle')
    +            ->willReturn($response);
    +
    +        $cacheTracer = $this->createMock(AbstractCacheTracer::class);
    +        $cacheTracer
    +            ->expects(static::once())
    +            ->method('trace')
    +            ->willReturnCallback(fn (string $name, \Closure $closure) => $closure());
    +
    +        $requestStack = $this->createMock(RequestStack::class);
    +        $requestStack->method('getMainRequest')->willReturn(new Request());
    +
    +        $arrayAdapter = new ArrayAdapter();
    +        $subscriber = new NotFoundSubscriber(
    +            $httpKernel,
    +            $this->createMock(SalesChannelContextServiceInterface::class),
    +            false,
    +            new TagAwareAdapter($arrayAdapter, $arrayAdapter),
    +            $cacheTracer,
    +            $this->createMock(EntityCacheKeyGenerator::class),
    +            $this->createMock(CacheInvalidator::class),
    +            new EventDispatcher(),
    +            []
    +        );
    +
    +        $request = new Request();
    +
    +        $event = new ExceptionEvent(
    +            $this->createMock(Kernel::class),
    +            $request,
    +            0,
    +            new HttpException(Response::HTTP_NOT_FOUND)
    +        );
    +
    +        $subscriber->onError($event);
    +
    +        $writtenCaches = array_values($arrayAdapter->getValues());
    +
    +        static::assertArrayHasKey(0, $writtenCaches);
    +
    +        $cacheItem = unserialize($writtenCaches[0]);
    +        static::assertInstanceOf(Response::class, $cacheItem);
    +
    +        $cookies = $cacheItem->headers->getCookies();
    +        static::assertCount(1, $cookies);
    +
    +        static::assertSame('extension-cookie', $cookies[0]->getName());
    +    }
    +
         public function testOtherExceptionsDoesNotGetCached(): void
         {
             $httpKernel = $this->createMock(HttpKernelInterface::class);
    
3477e4a425d3

NEXT-34113 - Clear cookies on 404 pages

https://github.com/shopware/storefrontSoner SayakciMar 4, 2024via ghsa
5 files changed · +14 257
  • DependencyInjection/services.xml+1 0 modified
    @@ -182,6 +182,7 @@
                 <argument type="service" id="Shopware\Core\Framework\DataAbstractionLayer\Cache\EntityCacheKeyGenerator"/>
                 <argument type="service" id="Shopware\Core\Framework\Adapter\Cache\CacheInvalidator"/>
                 <argument type="service" id="event_dispatcher"/>
    +            <argument>%session.storage.options%</argument>
     
                 <tag name="kernel.event_subscriber"/>
                 <tag name="kernel.reset" method="reset"/>
    
  • Framework/Routing/NotFound/NotFoundSubscriber.php+13 1 modified
    @@ -42,10 +42,13 @@ class NotFoundSubscriber implements EventSubscriberInterface, ResetInterface
          */
         private bool $handled = false;
     
    +    private string $sessionName;
    +
         /**
          * @internal
          *
          * @param AbstractCacheTracer<Response> $cacheTracer
    +     * @param array{name?: string} $sessionOptions
          */
         public function __construct(
             private readonly HttpKernelInterface $httpKernel,
    @@ -55,8 +58,10 @@ public function __construct(
             private readonly AbstractCacheTracer $cacheTracer,
             private readonly EntityCacheKeyGenerator $generator,
             private readonly CacheInvalidator $cacheInvalidator,
    -        private readonly EventDispatcherInterface $eventDispatcher
    +        private readonly EventDispatcherInterface $eventDispatcher,
    +        array $sessionOptions = []
         ) {
    +        $this->sessionName = $sessionOptions['name'] ?? 'session-';
         }
     
         public static function getSubscribedEvents(): array
    @@ -123,6 +128,13 @@ public function onError(ExceptionEvent $event): void
                     $response->setContext(null);
                 }
     
    +            // Remove session cookie from 404 pages, injected by the Symfony session listener
    +            foreach ($response->headers->getCookies() as $cookie) {
    +                if ($cookie->getName() === $this->sessionName) {
    +                    $response->headers->removeCookie($cookie->getName(), $cookie->getPath(), $cookie->getDomain());
    +                }
    +            }
    +
                 return $response;
             });
     
    
  • Test/Framework/Routing/NotFound/NotFoundPageCacheKeyEventTest.php+0 34 removed
    @@ -1,34 +0,0 @@
    -<?php declare(strict_types=1);
    -
    -namespace Shopware\Storefront\Test\Framework\Routing\NotFound;
    -
    -use PHPUnit\Framework\TestCase;
    -use Shopware\Core\Framework\Context;
    -use Shopware\Core\System\SalesChannel\SalesChannelContext;
    -use Shopware\Storefront\Framework\Routing\NotFound\NotFoundPageCacheKeyEvent;
    -use Symfony\Component\HttpFoundation\Request;
    -
    -/**
    - * @internal
    - *
    - * @covers \Shopware\Storefront\Framework\Routing\NotFound\NotFoundPageCacheKeyEvent
    - */
    -class NotFoundPageCacheKeyEventTest extends TestCase
    -{
    -    public function testEvent(): void
    -    {
    -        $request = new Request();
    -        $context = $this->createMock(SalesChannelContext::class);
    -        $context->method('getContext')->willReturn(Context::createDefaultContext());
    -
    -        $event = new NotFoundPageCacheKeyEvent('test', $request, $context);
    -
    -        static::assertSame('test', $event->getKey());
    -        static::assertSame($context->getContext(), $event->getContext());
    -        static::assertSame($context, $event->getSalesChannelContext());
    -        static::assertSame($request, $event->getRequest());
    -
    -        $event->setKey('test2');
    -        static::assertSame('test2', $event->getKey());
    -    }
    -}
    
  • Test/Framework/Routing/NotFound/NotFoundPageTagsEventTest.php+0 34 removed
    @@ -1,34 +0,0 @@
    -<?php declare(strict_types=1);
    -
    -namespace Shopware\Storefront\Test\Framework\Routing\NotFound;
    -
    -use PHPUnit\Framework\TestCase;
    -use Shopware\Core\Framework\Context;
    -use Shopware\Core\System\SalesChannel\SalesChannelContext;
    -use Shopware\Storefront\Framework\Routing\NotFound\NotFoundPageTagsEvent;
    -use Symfony\Component\HttpFoundation\Request;
    -
    -/**
    - * @internal
    - *
    - * @covers \Shopware\Storefront\Framework\Routing\NotFound\NotFoundPageTagsEvent
    - */
    -class NotFoundPageTagsEventTest extends TestCase
    -{
    -    public function testEvent(): void
    -    {
    -        $request = new Request();
    -        $context = $this->createMock(SalesChannelContext::class);
    -        $context->method('getContext')->willReturn(Context::createDefaultContext());
    -
    -        $event = new NotFoundPageTagsEvent(['test'], $request, $context);
    -
    -        static::assertSame(['test'], $event->getTags());
    -        static::assertSame($context->getContext(), $event->getContext());
    -        static::assertSame($context, $event->getSalesChannelContext());
    -        static::assertSame($request, $event->getRequest());
    -
    -        $event->addTags(['test2']);
    -        static::assertSame(['test', 'test2'], $event->getTags());
    -    }
    -}
    
  • Test/Framework/Routing/NotFound/NotFoundSubscriberTest.php+0 188 removed
    @@ -1,188 +0,0 @@
    -<?php declare(strict_types=1);
    -
    -namespace Shopware\Storefront\Test\Framework\Routing\NotFound;
    -
    -use PHPUnit\Framework\TestCase;
    -use Shopware\Core\Framework\Adapter\Cache\AbstractCacheTracer;
    -use Shopware\Core\Framework\Adapter\Cache\CacheInvalidator;
    -use Shopware\Core\Framework\DataAbstractionLayer\Cache\EntityCacheKeyGenerator;
    -use Shopware\Core\Kernel;
    -use Shopware\Core\System\SalesChannel\Context\SalesChannelContextServiceInterface;
    -use Shopware\Core\System\SystemConfig\Event\SystemConfigChangedEvent;
    -use Shopware\Storefront\Framework\Routing\NotFound\NotFoundSubscriber;
    -use Shopware\Storefront\Framework\Routing\StorefrontResponse;
    -use Symfony\Component\Cache\Adapter\ArrayAdapter;
    -use Symfony\Component\Cache\Adapter\TagAwareAdapter;
    -use Symfony\Component\EventDispatcher\EventDispatcher;
    -use Symfony\Component\HttpFoundation\Request;
    -use Symfony\Component\HttpFoundation\RequestStack;
    -use Symfony\Component\HttpFoundation\Response;
    -use Symfony\Component\HttpKernel\Event\ExceptionEvent;
    -use Symfony\Component\HttpKernel\Exception\HttpException;
    -use Symfony\Component\HttpKernel\HttpKernelInterface;
    -use Symfony\Component\HttpKernel\KernelEvents;
    -use Symfony\Contracts\Cache\CacheInterface;
    -
    -/**
    - * @internal
    - *
    - * @covers \Shopware\Storefront\Framework\Routing\NotFound\NotFoundSubscriber
    - */
    -class NotFoundSubscriberTest extends TestCase
    -{
    -    public function testDebugIsOnDoesNothing(): void
    -    {
    -        $subscriber = new NotFoundSubscriber(
    -            $this->createMock(HttpKernelInterface::class),
    -            $this->createMock(SalesChannelContextServiceInterface::class),
    -            true,
    -            $this->createMock(CacheInterface::class),
    -            $this->createMock(AbstractCacheTracer::class),
    -            $this->createMock(EntityCacheKeyGenerator::class),
    -            $this->createMock(CacheInvalidator::class),
    -            new EventDispatcher()
    -        );
    -
    -        $event = new ExceptionEvent(
    -            $this->createMock(Kernel::class),
    -            new Request(),
    -            0,
    -            new \Exception()
    -        );
    -        $subscriber->onError($event);
    -
    -        static::assertNull($event->getResponse());
    -    }
    -
    -    public function testErrorHandled(): void
    -    {
    -        $httpKernel = $this->createMock(HttpKernelInterface::class);
    -        $httpKernel
    -            ->expects(static::once())
    -            ->method('handle')
    -            ->willReturn(new StorefrontResponse());
    -
    -        $cacheTracer = $this->createMock(AbstractCacheTracer::class);
    -        $cacheTracer
    -            ->expects(static::once())
    -            ->method('trace')
    -            ->willReturnCallback(fn (string $name, \Closure $closure) => $closure());
    -
    -        $requestStack = $this->createMock(RequestStack::class);
    -        $requestStack->method('getMainRequest')->willReturn(new Request());
    -
    -        $subscriber = new NotFoundSubscriber(
    -            $httpKernel,
    -            $this->createMock(SalesChannelContextServiceInterface::class),
    -            false,
    -            new TagAwareAdapter(new ArrayAdapter(), new ArrayAdapter()),
    -            $cacheTracer,
    -            $this->createMock(EntityCacheKeyGenerator::class),
    -            $this->createMock(CacheInvalidator::class),
    -            new EventDispatcher()
    -        );
    -
    -        $request = new Request();
    -
    -        $event = new ExceptionEvent(
    -            $this->createMock(Kernel::class),
    -            $request,
    -            0,
    -            new HttpException(Response::HTTP_NOT_FOUND)
    -        );
    -        $subscriber->onError($event);
    -
    -        $response = $event->getResponse();
    -
    -        static::assertInstanceOf(Response::class, $response);
    -    }
    -
    -    public function testOtherExceptionsDoesNotGetCached(): void
    -    {
    -        $httpKernel = $this->createMock(HttpKernelInterface::class);
    -        $httpKernel
    -            ->expects(static::once())
    -            ->method('handle')
    -            ->willReturn(new StorefrontResponse());
    -
    -        $cacheTracer = $this->createMock(AbstractCacheTracer::class);
    -        $cacheTracer
    -            ->expects(static::never())
    -            ->method('trace');
    -
    -        $requestStack = $this->createMock(RequestStack::class);
    -        $requestStack->method('getMainRequest')->willReturn(new Request());
    -
    -        $subscriber = new NotFoundSubscriber(
    -            $httpKernel,
    -            $this->createMock(SalesChannelContextServiceInterface::class),
    -            false,
    -            new TagAwareAdapter(new ArrayAdapter(), new ArrayAdapter()),
    -            $cacheTracer,
    -            $this->createMock(EntityCacheKeyGenerator::class),
    -            $this->createMock(CacheInvalidator::class),
    -            new EventDispatcher()
    -        );
    -
    -        $request = new Request();
    -
    -        $event = new ExceptionEvent(
    -            $this->createMock(Kernel::class),
    -            $request,
    -            0,
    -            new \Exception()
    -        );
    -        $subscriber->onError($event);
    -
    -        static::assertInstanceOf(Response::class, $event->getResponse());
    -
    -        $subscriber->reset();
    -    }
    -
    -    /**
    -     * @dataProvider providerSystemConfigKeys
    -     */
    -    public function testInvalidationHappensOnSystemConfigChange(string $key, bool $shouldInvalidate): void
    -    {
    -        $cacheInvalidator = $this->createMock(CacheInvalidator::class);
    -        $cacheInvalidator
    -            ->expects($shouldInvalidate ? static::once() : static::never())
    -            ->method('invalidate');
    -
    -        $subscriber = new NotFoundSubscriber(
    -            $this->createMock(HttpKernelInterface::class),
    -            $this->createMock(SalesChannelContextServiceInterface::class),
    -            true,
    -            $this->createMock(CacheInterface::class),
    -            $this->createMock(AbstractCacheTracer::class),
    -            $this->createMock(EntityCacheKeyGenerator::class),
    -            $cacheInvalidator,
    -            new EventDispatcher()
    -        );
    -
    -        $subscriber->onSystemConfigChanged(new SystemConfigChangedEvent($key, 'foo', null));
    -    }
    -
    -    /**
    -     * @return iterable<string, array<mixed>>
    -     */
    -    public static function providerSystemConfigKeys(): iterable
    -    {
    -        yield 'key matches' => [
    -            'core.basicInformation.http404Page',
    -            true,
    -        ];
    -
    -        yield 'key not matches' => [
    -            'core.http404Page',
    -            false,
    -        ];
    -    }
    -
    -    public function testSubscribedEvents(): void
    -    {
    -        static::assertArrayHasKey(SystemConfigChangedEvent::class, NotFoundSubscriber::getSubscribedEvents());
    -
    -        static::assertArrayHasKey(KernelEvents::EXCEPTION, NotFoundSubscriber::getSubscribedEvents());
    -    }
    -}
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.