Shopware's session is persistent in Cache for 404 pages
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.
| Package | Affected versions | Patched versions |
|---|---|---|
shopware/storefrontPackagist | >= 6.5.8.0, < 6.5.8.7 | 6.5.8.7 |
shopware/platformPackagist | >= 6.5.8.0, < 6.5.8.7 | 6.5.8.7 |
Affected products
1Patches
27d9cb03225efNEXT-34113 - Clear cookies on 404 pages
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);
3477e4a425d3NEXT-34113 - Clear cookies on 404 pages
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- github.com/advisories/GHSA-c2f9-4jmm-v45mghsaADVISORY
- github.com/shopware/shopware/commit/7d9cb03225efca5f97e69b800d8747598dd15ce3ghsax_refsource_MISCWEB
- github.com/shopware/shopware/releases/tag/v6.5.8.7ghsax_refsource_MISCWEB
- github.com/shopware/shopware/security/advisories/GHSA-c2f9-4jmm-v45mghsax_refsource_CONFIRMWEB
- github.com/shopware/storefront/commit/3477e4a425d3c54b4bfae82d703fe3838dc21d3eghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.