CVE-2024-27917 (published 2024-03-06T20:15Z)

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.


Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/shopware/storefront
commit_details: repo_url: https://github.com/shopware/storefront
author: Soner Sayakci
author_date: 2024-03-04 10:30:46+01:00
msg: NEXT-34113 - Clear cookies on 404 pages
repository: repo_url: https://github.com/shopware/storefront
repo_name: shopware/storefront
repo_language: Twig
owner: shopware
file_changes: filename: services.xml
change_type: ModificationType.MODIFY
diff: @@ -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"/>

programming_language: PHP
filename: NotFoundSubscriber.php
change_type: ModificationType.MODIFY
diff: @@ -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 @@ class NotFoundSubscriber implements EventSubscriberInterface, ResetInterface
         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 @@ class NotFoundSubscriber implements EventSubscriberInterface, ResetInterface
                 $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;
         });
 

programming_language: PHP
filename: NotFoundPageCacheKeyEventTest.php
change_type: ModificationType.DELETE
diff: @@ -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());
-    }
-}

programming_language: unknown
filename: NotFoundPageTagsEventTest.php
change_type: ModificationType.DELETE
diff: @@ -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());
-    }
-}

programming_language: unknown
filename: NotFoundSubscriberTest.php
change_type: ModificationType.DELETE
diff: @@ -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());
-    }
-}

programming_language: unknown