VYPR
Low severityNVD Advisory· Published Jan 17, 2023· Updated Mar 10, 2025

Insufficient Session Expiration in Administration in shopware

CVE-2023-22732

Description

Shopware is an open source commerce platform based on Symfony Framework and Vue js. The Administration session expiration was set to one week, when an attacker has stolen the session cookie they could use it for a long period of time. In version 6.4.18.1 an automatic logout into the Administration session has been added. As a result the user will be logged out when they are inactive. Users are advised to upgrade. There are no known workarounds for this issue.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Shopware 6 Admin session cookies lacked inactivity timeout, allowing a stolen cookie to be reused for up to one week.

Vulnerability

CVE-2023-22732 describes an insufficient session expiration vulnerability in the Shopware 6 Administration component. The Administration session cookie had a fixed lifetime of one week and was not invalidated by user inactivity, unlike typical web applications that enforce a shorter idle timeout [1][4]. This design oversight meant that if an attacker managed to steal a valid session cookie (e.g., through cross-site scripting, a compromised endpoint, or a man-in-the-middle attack), they could reuse that cookie to impersonate the legitimate administrator for the entire week, without any activity-based session termination [1][4].

Exploitation

Exploitation of CVE-2023-22732 requires the attacker to first obtain a valid, active administration session cookie from a logged-in Shopware administrator. The cookie could be stolen via phishing, malicious browser extensions, or by exploiting other vulnerabilities that expose session tokens. Once the cookie is in hand, no further authentication is needed; the attacker can simply replay the cookie in requests to the /admin interface and gain persistent access as the victim administrator for the cookie's remaining lifetime [1][4].

Impact

A successful attack grants the adversary full administrative access to the Shopware commerce platform. Depending on the privileges of the compromised account, the attacker could modify products, customer data, payment configurations, or even execute code through the plugin system. The UI reference describes the patch as adding a "30-minute logout for inactive users" [2], indicating that before the fix, an attacker could maintain a hijacked session indefinitely within the one-week window, without the user being automatically logged out.

Mitigation

Shopware addressed this issue in version 6.4.18.1 by implementing automatic logout for inactive users in the Administration, with the session now expiring after 30 minutes of inactivity [2][4]. The vendor advises all users to upgrade to version 6.4.18.1 or later; no workarounds are available [1][4]. This CVE is not currently listed in CISA's Known Exploited Vulnerabilities (KEV) catalog and does not require immediate emergency patching beyond the standard security update process.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
shopware/platformPackagist
< 6.4.18.16.4.18.1
shopware/corePackagist
< 6.4.18.16.4.18.1

Affected products

3

Patches

1
cd7a89cbcd3a

NEXT-24677 - Limit admin session time

https://github.com/shopware/platformSebastian SeggewissJan 3, 2023via ghsa
14 files changed · +134 2
  • changelog/_unreleased/2022-12-29-implement-logout-for-inactive-users.md+9 0 added
    @@ -0,0 +1,9 @@
    +---
    +title: Implement logout for inactive users
    +issue: NEXT-24677
    +author: Sebastian Seggewiss
    +author_email: s.seggewiss@shopware.com
    +author_github: @seggewiss
    +---
    +# Administration
    +* Added 30-minute logout for inactive users
    
  • src/Administration/Resources/app/administration/src/app/component/structure/sw-admin/index.js+8 0 modified
    @@ -8,6 +8,8 @@ const { Component } = Shopware;
     Component.register('sw-admin', {
         template,
     
    +    inject: ['userActivityService'],
    +
         metaInfo() {
             return {
                 title: this.$tc('global.sw-admin-menu.textShopwareAdmin'),
    @@ -19,4 +21,10 @@ Component.register('sw-admin', {
                 return Shopware.Service('loginService').isLoggedIn();
             },
         },
    +
    +    methods: {
    +        onUserActivity: Shopware.Utils.debounce(function updateUserActivity() {
    +            this.userActivityService.updateLastUserActivity();
    +        }, 5000),
    +    },
     });
    
  • src/Administration/Resources/app/administration/src/app/component/structure/sw-admin/sw-admin.html.twig+5 1 modified
    @@ -1,6 +1,10 @@
     <!-- eslint-disable-next-line sw-deprecation-rules/no-twigjs-blocks -->
     {% block sw_admin %}
    -<div id="app">
    +<div
    +    id="app"
    +    @mousemove="onUserActivity"
    +    @keyup="onUserActivity"
    +>
         <sw-notifications ref="notifications" />
         <sw-duplicated-media-v2 v-if="isLoggedIn" />
         <router-view />
    
  • src/Administration/Resources/app/administration/src/app/component/utils/sw-progress-bar/index.js+8 0 modified
    @@ -15,6 +15,8 @@ const { Component } = Shopware;
     Component.register('sw-progress-bar', {
         template,
     
    +    inject: ['userActivityService'],
    +
         props: {
             value: {
                 type: Number,
    @@ -47,4 +49,10 @@ Component.register('sw-progress-bar', {
                 };
             },
         },
    +
    +    watch: {
    +        value() {
    +            this.userActivityService.updateLastUserActivity();
    +        },
    +    },
     });
    
  • src/Administration/Resources/app/administration/src/app/main.ts+4 0 modified
    @@ -38,6 +38,7 @@ import ShopwareDiscountCampaignService from 'src/app/service/discount-campaign.s
     import SearchRankingService from 'src/app/service/search-ranking.service';
     import SearchPreferencesService from 'src/app/service/search-preferences.service';
     import RecentlySearchService from 'src/app/service/recently-search.service';
    +import UserActivityService from 'src/app/service/user-activity.service';
     
     /** Import Feature */
     import Feature from 'src/core/feature';
    @@ -186,4 +187,7 @@ Application
             return new SearchPreferencesService({
                 userConfigRepository: Shopware.Service('repositoryFactory').create('user_config'),
             });
    +    })
    +    .addServiceProvider('userActivityService', () => {
    +        return new UserActivityService();
         });
    
  • src/Administration/Resources/app/administration/src/app/service/user-activity.service.spec.ts+22 0 added
    @@ -0,0 +1,22 @@
    +import UserActivityService from './user-activity.service';
    +
    +describe('src/app/service/user-activity.service.ts', () => {
    +    let service: UserActivityService | undefined;
    +
    +    beforeEach(() => {
    +        service = new UserActivityService();
    +    });
    +
    +    it('should instantiate', () => {
    +        expect(service instanceof UserActivityService).toBe(true);
    +    });
    +
    +    it('should change last user activity', () => {
    +        Shopware.Context.app.lastActivity = 0;
    +        const date = new Date();
    +        const expectedResult = Math.round(+date / 1000);
    +
    +        service.updateLastUserActivity(date);
    +        expect(Shopware.Context.app.lastActivity).toBe(expectedResult);
    +    });
    +});
    
  • src/Administration/Resources/app/administration/src/app/service/user-activity.service.ts+12 0 added
    @@ -0,0 +1,12 @@
    +/**
    + * @private
    + */
    +export default class UserActivityService {
    +    updateLastUserActivity(date?: Date): void {
    +        if (date === undefined) {
    +            date = new Date();
    +        }
    +
    +        Shopware.Context.app.lastActivity = Math.round(+date / 1000);
    +    }
    +}
    
  • src/Administration/Resources/app/administration/src/app/state/context.store.ts+2 0 modified
    @@ -35,6 +35,7 @@ interface ContextState {
             systemCurrencyISOCode: null | string,
             systemCurrencyId: null | string,
             disableExtensions: boolean,
    +        lastActivity: number,
         },
         api: {
             apiPath: null | string,
    @@ -75,6 +76,7 @@ const ContextStore: Module<ContextState, VuexRootState> = {
                 systemCurrencyId: null,
                 systemCurrencyISOCode: null,
                 disableExtensions: false,
    +            lastActivity: 0,
             },
             api: {
                 apiPath: null,
    
  • src/Administration/Resources/app/administration/src/core/factory/app-context.factory.js+6 0 modified
    @@ -14,5 +14,11 @@ export default function createContext(context = {}) {
             Shopware.State.commit('context/addAppValue', { key, value });
         });
     
    +    // set initial last activity to prevent immediate logout
    +    Shopware.State.commit('context/addAppValue', {
    +        key: 'lastActivity',
    +        value: Math.round(+new Date() / 1000),
    +    });
    +
         return Shopware.Context.app;
     }
    
  • src/Administration/Resources/app/administration/src/core/factory/router.factory.js+2 0 modified
    @@ -83,6 +83,8 @@ export default function createRouter(Router, View, moduleFactory, LoginService)
             const assetPath = getAssetPath();
     
             router.beforeEach((to, from, next) => {
    +            Shopware.Context.app.lastActivity = Math.round(+new Date() / 1000);
    +
                 setModuleFavicon(to, assetPath);
                 const loggedIn = LoginService.isLoggedIn();
                 const tokenHandler = new Shopware.Helper.RefreshTokenHelper();
    
  • src/Administration/Resources/app/administration/src/core/service/login.service.spec.js+28 0 modified
    @@ -41,6 +41,7 @@ describe('core/service/login.service.js', () => {
         });
     
         beforeEach(() => {
    +        Shopware.Context.app.lastActivity = Math.round(+new Date() / 1000);
             window.localStorage.removeItem('redirectFromLogin');
             document.cookie = '';
         });
    @@ -331,6 +332,7 @@ describe('core/service/login.service.js', () => {
     
         it('should start auto refresh the token after login', async () => {
             jest.useFakeTimers();
    +        Shopware.Context.app.lastActivity = Math.round(+new Date() / 1000);
     
             const { loginService, clientMock } = loginServiceFactory();
     
    @@ -389,4 +391,30 @@ describe('core/service/login.service.js', () => {
             const storage = loginService.getStorage();
             expect(storage instanceof CookieStorage).toBe(true);
         });
    +
    +    it('should logout inactive user', async () => {
    +        // Current time in Seconds - 1501 to be one 1-second over the threshold
    +        Shopware.Context.app.lastActivity = Math.round(+new Date() / 1000) - 1501;
    +
    +        const { loginService, clientMock } = loginServiceFactory();
    +        const logoutListener = jest.fn();
    +        loginService.addOnLogoutListener(logoutListener);
    +
    +        clientMock.onPost('/oauth/token')
    +            .reply(200, {
    +                token_type: 'Bearer',
    +                expires_in: 600,
    +                access_token: 'aCcEsS_tOkEn_first',
    +                refresh_token: 'rEfReSh_ToKeN_first'
    +            });
    +
    +        await loginService.loginByUsername('admin', 'shopware');
    +
    +        expect(clientMock.history.post[0]).toBeDefined();
    +        expect(clientMock.history.post[1]).toBeUndefined();
    +        expect(JSON.parse(clientMock.history.post[0].data).grant_type).toEqual('password');
    +
    +        expect(clientMock.history.post[1]).toBeUndefined();
    +        expect(logoutListener).toHaveBeenCalledTimes(1);
    +    });
     });
    
  • src/Administration/Resources/app/administration/src/core/service/login.service.ts+20 0 modified
    @@ -242,13 +242,33 @@ export default function createLoginService(
                 clearTimeout(autoRefreshTokenTimeoutId);
             }
     
    +        if (lastActivityOverThreshold()) {
    +            logout();
    +            return;
    +        }
    +
             const timeUntilExpiry = expiryTimestamp * 1000 - Date.now();
     
             autoRefreshTokenTimeoutId = setTimeout(() => {
                 void refreshToken();
             }, timeUntilExpiry / 2);
         }
     
    +    /**
    +     * Returns true if the last user activity is over the 30-minute threshold
    +     *
    +     * @private
    +     */
    +    function lastActivityOverThreshold(): boolean {
    +        const lastActivity = Shopware.Context.app.lastActivity;
    +
    +        // (Current time in seconds) - 25 minutes
    +        // 25 minutes + half the 10-minute expiry = 30 minute threshold
    +        const threshold = Math.round(+new Date() / 1000) - 1500;
    +
    +        return lastActivity <= threshold;
    +    }
    +
         /**
          * Returns saved bearer authentication object. Either you're getting the full object or when you're specifying
          * the `section` argument and getting either the token or the expiry date.
    
  • src/Administration/Resources/app/administration/src/global.types.ts+2 0 modified
    @@ -15,6 +15,7 @@ import type ExtensionSdkService from 'src/core/service/api/extension-sdk.service
     import type CartStoreService from 'src/core/service/api/cart-store-api.api.service';
     import type CustomSnippetApiService from 'src/core/service/api/custom-snippet.api.service';
     import type LocaleFactory from 'src/core/factory/locale.factory';
    +import type UserActivityService from 'src/app/service/user-activity.service';
     import type { ExtensionsState } from './app/state/extensions.store';
     import type { ComponentConfig } from './core/factory/component.factory';
     import type { TabsState } from './app/state/tabs.store';
    @@ -139,6 +140,7 @@ declare global {
             appModulesService: AppModulesService,
             cartStoreService: CartStoreService,
             customSnippetApiService: CustomSnippetApiService,
    +        userActivityService: UserActivityService,
         }
         // eslint-disable-next-line @typescript-eslint/no-empty-interface
         interface InitContainer extends SubContainer<'init'>{
    
  • src/Administration/Resources/app/administration/src/module/sw-extension/component/sw-ratings/sw-extension-ratings-summary/sw-extension-ratings-summary.spec.js+6 1 modified
    @@ -22,7 +22,12 @@ describe('src/module/sw-extension/component/sw-ratings/sw-extension-ratings-summ
                 stubs: {
                     'sw-extension-rating-stars': true,
                     'sw-progress-bar': await Shopware.Component.build('sw-progress-bar')
    -            }
    +            },
    +            provide: {
    +                userActivityService: {
    +                    updateLastUserActivity: () => {},
    +                },
    +            },
             });
         }
     
    

Vulnerability mechanics

Generated 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.