VYPR
Moderate severityNVD Advisory· Published Apr 8, 2024· Updated Sep 3, 2024

Shopware has Improper Session Handling in store-api

CVE-2024-31447

Description

Shopware 6 is an open commerce platform based on Symfony Framework and Vue. Starting in version 6.3.5.0 and prior to versions 6.6.1.0 and 6.5.8.8, when a authenticated request is made to POST /store-api/account/logout, the cart will be cleared, but the User won't be logged out. This affects only the direct store-api usage, as the PHP Storefront listens additionally on CustomerLogoutEvent and invalidates the session additionally. The problem has been fixed in Shopware 6.6.1.0 and 6.5.8.8. Those who are unable to update can install the latest version of the Shopware Security Plugin as a workaround.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
shopware/corePackagist
>= 6.3.5.0, < 6.5.8.86.5.8.8
shopware/platformPackagist
>= 6.3.5.0, < 6.5.8.86.5.8.8
shopware/corePackagist
>= 6.6.0.0-rc1, < 6.6.1.06.6.1.0
shopware/platformPackagist
>= 6.6.0.0-rc1, < 6.6.1.06.6.1.0

Affected products

1

Patches

2
5cc84ddd817a

NEXT-34608 - Improve account logout

https://github.com/shopware/shopwareSoner SayakciApr 3, 2024via ghsa
4 files changed · +13 5
  • changelog/_unreleased/2024-04-03-improve-account-logout.md+8 0 added
    @@ -0,0 +1,8 @@
    +---
    +title: Improve account logout
    +issue: NEXT-34608
    +---
    +
    +# Core
    +
    +* Changed `LogoutRoute` to always logout the user regardless of the configurations.
    
  • src/Core/Checkout/Customer/SalesChannel/LogoutRoute.php+2 0 modified
    @@ -39,6 +39,8 @@ public function getDecorated(): AbstractLogoutRoute
         #[Route(path: '/store-api/account/logout', name: 'store-api.account.logout', methods: ['POST'], defaults: ['_loginRequired' => true, '_loginRequiredAllowGuest' => true])]
         public function logout(SalesChannelContext $context, RequestDataBag $data): ContextTokenResponse
         {
    +        $this->contextPersister->save($context->getToken(), ['customerId' => null], $context->getSalesChannelId());
    +
             /** @var CustomerEntity $customer */
             $customer = $context->getCustomer();
             if ($this->shouldDelete($context)) {
    
  • tests/integration/Core/Checkout/Customer/SalesChannel/LogoutRouteTest.php+2 4 modified
    @@ -166,7 +166,7 @@ public function testLoggedOutUpdateCustomerContextWithReplaceTokenParameter(): v
     
             $newCustomerContextToken = $this->getContainer()->get(Connection::class)->fetchOne('SELECT token FROM sales_channel_api_context WHERE customer_id = ?', [$currentCustomerId]);
     
    -        static::assertNotEmpty($newCustomerContextToken);
    +        static::assertEmpty($newCustomerContextToken);
             static::assertNotEquals($currentCustomerToken, $newCustomerContextToken);
         }
     
    @@ -193,7 +193,6 @@ public function testLoggedOutKeepCustomerContextWithoutReplaceTokenParameter():
             $response = $this->browser->getResponse();
     
             $currentCustomerToken = $response->headers->get(PlatformRequest::HEADER_CONTEXT_TOKEN) ?: '';
    -        $currentCustomerId = $this->getContainer()->get(Connection::class)->fetchOne('SELECT customer_id FROM sales_channel_api_context WHERE token = ?', [$currentCustomerToken]);
     
             $this->browser->setServerParameter('HTTP_SW_CONTEXT_TOKEN', $currentCustomerToken);
     
    @@ -208,8 +207,7 @@ public function testLoggedOutKeepCustomerContextWithoutReplaceTokenParameter():
                 );
     
             $customerIdWithOldToken = $this->getContainer()->get(Connection::class)->fetchOne('SELECT customer_id FROM sales_channel_api_context WHERE token = ?', [$currentCustomerToken]);
    -
    -        static::assertEquals($currentCustomerId, $customerIdWithOldToken);
    +        static::assertEmpty($customerIdWithOldToken);
         }
     
         public function testLogoutRouteReturnContextTokenResponse(): void
    
  • tests/integration/Storefront/Controller/AuthControllerTest.php+1 1 modified
    @@ -262,7 +262,7 @@ public function testOneUserUseOneContextAcrossSessions(): void
             $secondTimeLoginContextToken = $secondTimeLogin->get(PlatformRequest::HEADER_CONTEXT_TOKEN);
     
             static::assertNotEquals($firstTimeLoginSessionId, $secondTimeLoginSessionId);
    -        static::assertEquals($firstTimeLoginContextToken, $secondTimeLoginContextToken);
    +        static::assertNotEquals($firstTimeLoginContextToken, $secondTimeLoginContextToken);
         }
     
         public function testMergedHintIsAdded(): void
    
d29775aa758f

NEXT-34608 - Improve account logout

https://github.com/shopware/shopwareSoner SayakciMar 28, 2024via ghsa
3 files changed · +10 92
  • src/Core/Checkout/Customer/SalesChannel/LogoutRoute.php+5 10 modified
    @@ -36,23 +36,18 @@ public function getDecorated(): AbstractLogoutRoute
             throw new DecorationPatternException(self::class);
         }
     
    -    #[Route(path: '/store-api/account/logout', name: 'store-api.account.logout', methods: ['POST'], defaults: ['_loginRequired' => true, '_loginRequiredAllowGuest' => true])]
    +    #[Route(path: '/store-api/account/logout', name: 'store-api.account.logout', defaults: ['_loginRequired' => true, '_loginRequiredAllowGuest' => true], methods: ['POST'])]
         public function logout(SalesChannelContext $context, RequestDataBag $data): ContextTokenResponse
         {
    +        $newToken = Random::getAlphanumericString(32);
    +
             /** @var CustomerEntity $customer */
             $customer = $context->getCustomer();
             if ($this->shouldDelete($context)) {
                 $this->cartService->deleteCart($context);
                 $this->contextPersister->delete($context->getToken(), $context->getSalesChannelId());
    -
    -            $event = new CustomerLogoutEvent($context, $customer);
    -            $this->eventDispatcher->dispatch($event);
    -
    -            return new ContextTokenResponse($context->getToken());
    -        }
    -
    -        $newToken = Random::getAlphanumericString(32);
    -        if ((bool) $data->get('replace-token')) {
    +        } else {
    +            $this->contextPersister->save($context->getToken(), ['customerId' => null], $context->getSalesChannelId());
                 $newToken = $this->contextPersister->replace($context->getToken(), $context);
             }
     
    
  • tests/integration/Core/Checkout/Customer/SalesChannel/LogoutRouteTest.php+4 81 modified
    @@ -55,8 +55,6 @@ public function testNotLoggedin(): void
                 ->request(
                     'POST',
                     '/store-api/account/logout',
    -                [
    -                ]
                 );
     
             static::assertIsString($this->browser->getResponse()->getContent());
    @@ -95,24 +93,14 @@ public function testValidLogout(): void
                 ->request(
                     'POST',
                     '/store-api/account/logout',
    -                [
    -                    'replace-token' => true,
    -                ],
    -                [],
    -                [
    -                ]
                 );
     
             static::assertSame(200, $this->browser->getResponse()->getStatusCode());
     
             $this->browser
                 ->request(
                     'POST',
    -                '/store-api/account/customer',
    -                [],
    -                [],
    -                [
    -                ]
    +                '/store-api/account/customer'
                 );
     
             static::assertIsString($this->browser->getResponse()->getContent());
    @@ -121,55 +109,6 @@ public function testValidLogout(): void
             static::assertArrayHasKey('errors', $response);
         }
     
    -    public function testLoggedOutUpdateCustomerContextWithReplaceTokenParameter(): void
    -    {
    -        $systemConfig = $this->getContainer()->get(SystemConfigService::class);
    -        $systemConfig->set('core.loginRegistration.invalidateSessionOnLogOut', false);
    -
    -        $email = Uuid::randomHex() . '@example.com';
    -        $this->createCustomer($email);
    -
    -        $this->browser
    -            ->request(
    -                'POST',
    -                '/store-api/account/login',
    -                [
    -                    'email' => $email,
    -                    'password' => 'shopware',
    -                ]
    -            );
    -
    -        static::assertIsString($this->browser->getResponse()->getContent());
    -
    -        $response = $this->browser->getResponse();
    -
    -        $currentCustomerToken = $response->headers->get(PlatformRequest::HEADER_CONTEXT_TOKEN) ?: '';
    -        $currentCustomerId = $this->getContainer()->get(Connection::class)->fetchOne('SELECT customer_id FROM sales_channel_api_context WHERE token = ?', [$currentCustomerToken]);
    -
    -        $this->browser->setServerParameter('HTTP_SW_CONTEXT_TOKEN', $currentCustomerToken);
    -
    -        $this->browser
    -            ->request(
    -                'POST',
    -                '/store-api/account/logout',
    -                [
    -                    'replace-token' => true,
    -                ],
    -                [],
    -                [
    -                ]
    -            );
    -
    -        $customerIdWithOldToken = $this->getContainer()->get(Connection::class)->fetchOne('SELECT customer_id FROM sales_channel_api_context WHERE token = ?', [$currentCustomerToken]);
    -
    -        static::assertFalse($customerIdWithOldToken);
    -
    -        $newCustomerContextToken = $this->getContainer()->get(Connection::class)->fetchOne('SELECT token FROM sales_channel_api_context WHERE customer_id = ?', [$currentCustomerId]);
    -
    -        static::assertNotEmpty($newCustomerContextToken);
    -        static::assertNotEquals($currentCustomerToken, $newCustomerContextToken);
    -    }
    -
         public function testLoggedOutKeepCustomerContextWithoutReplaceTokenParameter(): void
         {
             $systemConfig = $this->getContainer()->get(SystemConfigService::class);
    @@ -193,23 +132,17 @@ public function testLoggedOutKeepCustomerContextWithoutReplaceTokenParameter():
             $response = $this->browser->getResponse();
     
             $currentCustomerToken = $response->headers->get(PlatformRequest::HEADER_CONTEXT_TOKEN) ?: '';
    -        $currentCustomerId = $this->getContainer()->get(Connection::class)->fetchOne('SELECT customer_id FROM sales_channel_api_context WHERE token = ?', [$currentCustomerToken]);
     
             $this->browser->setServerParameter('HTTP_SW_CONTEXT_TOKEN', $currentCustomerToken);
     
             $this->browser
                 ->request(
                     'POST',
                     '/store-api/account/logout',
    -                [],
    -                [],
    -                [
    -                ]
                 );
     
             $customerIdWithOldToken = $this->getContainer()->get(Connection::class)->fetchOne('SELECT customer_id FROM sales_channel_api_context WHERE token = ?', [$currentCustomerToken]);
    -
    -        static::assertEquals($currentCustomerId, $customerIdWithOldToken);
    +        static::assertFalse($customerIdWithOldToken, 'The old token should be gone');
         }
     
         public function testLogoutRouteReturnContextTokenResponse(): void
    @@ -286,7 +219,7 @@ public function testLogoutForcedForGuestAccounts(): void
                 ->logout($context, $request);
     
             static::assertInstanceOf(ContextTokenResponse::class, $logout);
    -        static::assertEquals($login->getToken(), $logout->getToken());
    +        static::assertNotEquals($login->getToken(), $logout->getToken());
     
             $exists = $this->getContainer()->get(Connection::class)
                 ->fetchAllAssociative('SELECT * FROM sales_channel_api_context WHERE token = :token', ['token' => $login->getToken()]);
    @@ -307,12 +240,6 @@ public function testValidLogoutAsGuest(): void
                 ->request(
                     'POST',
                     '/store-api/account/logout',
    -                [
    -                    'replace-token' => true,
    -                ],
    -                [],
    -                [
    -                ]
                 );
     
             static::assertIsString($this->browser->getResponse()->getContent());
    @@ -325,11 +252,7 @@ public function testValidLogoutAsGuest(): void
             $this->browser
                 ->request(
                     'POST',
    -                '/store-api/account/customer',
    -                [],
    -                [],
    -                [
    -                ]
    +                '/store-api/account/customer'
                 );
     
             static::assertIsString($this->browser->getResponse()->getContent());
    
  • tests/integration/Storefront/Controller/AuthControllerTest.php+1 1 modified
    @@ -262,7 +262,7 @@ public function testOneUserUseOneContextAcrossSessions(): void
             $secondTimeLoginContextToken = $secondTimeLogin->get(PlatformRequest::HEADER_CONTEXT_TOKEN);
     
             static::assertNotEquals($firstTimeLoginSessionId, $secondTimeLoginSessionId);
    -        static::assertEquals($firstTimeLoginContextToken, $secondTimeLoginContextToken);
    +        static::assertNotEquals($firstTimeLoginContextToken, $secondTimeLoginContextToken);
         }
     
         public function testMergedHintIsAdded(): void
    

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.