Headless sounds like frontend freedom - and at the same time it moves the entire business logic behind a publicly reachable interface. In practice, that interface is rarely examined as closely as a classic storefront. Anyone who wants to secure the Store API therefore has to start where authorization, field rights and rate limits are actually decided. The pressure is measurable: Broken Access Control tops the OWASP Top 10 2025 with an average incidence rate of 3.74% and a peak of 20.15% across roughly 2.8 million analysed applications (OWASP Top 10 2025). In the API-specific list, three of the five highest-ranked risks relate directly to authorization (OWASP API Security Top 10). This guide walks through the typical failure patterns - from other people's order IDs to writable price fields and missing limits - and ends with a pre-launch review plan. If the architectural decision is still open, start with the overview on headless commerce with Shopware.

The blind spot of headless architecture

In a classic shop, the storefront does part of the protective work almost by accident: server-rendered templates only show what the controller released beforehand, forms carry CSRF tokens, sessions live on the same origin. Decouple the frontend and that silent safeguard disappears. What remains is an HTTP interface that anyone can call from a terminal - and that has to make every decision about who may see and change what entirely on its own.

The OWASP Foundation's data shows how widespread gaps in exactly this decision layer are. For the Top 10 2025, data from 13 contributing organisations and around 220,000 CVEs was evaluated, from which 248 CWEs were sorted into ten categories (OWASP Top 10 2025). A01 Broken Access Control alone accounts for 40 CWEs, 1,839,701 documented occurrences and 32,654 CVEs, at an average weighted exploit score of 7.04 (OWASP Top 10 2025). The category is therefore not only common but also comparatively easy to exploit.

The general attack surface is growing in parallel. Germany's Federal Office for Information Security counted an average of 119 new vulnerabilities per day in the current reporting period - an increase of around 24% over the previous period - as well as roughly 280,000 new malware variants per day (BSI, The State of IT Security in Germany 2025). In this environment, a publicly reachable shop API is not a side issue but a permanently observed target.

Scope: security, not performance

This article covers the security layer of the Store API only. How to make the same interface fast is described in Store API caching for headless shops; the underlying HTTP cache mechanics are covered in the article on the cache rework and HTTP cache in Shopware. Caching and authorization interlock - a personalised response cached incorrectly is a security incident, not a performance problem.

The order matters: first comes the question of which route may release which data at all, then the question of how fast it does so. If you are planning a modular shop architecture, treat the authorization logic as an architectural topic in its own right - not as a detail of individual controllers.

Object level authorization: other people's IDs, open data

The most common failure pattern carries number one in the OWASP API list: API1:2023 Broken Object Level Authorization. In short: APIs expose endpoints that accept object identifiers and then check whether someone is signed in - but not whether that someone belongs to this particular object (OWASP API Security Top 10). Authentication answers the question of identity. Authorization answers the question of ownership. Both are regularly conflated in headless projects.

In concrete terms: a customer account requests its order history. The endpoint accepts an order ID, reads the record and returns it. As long as the ID comes from the customer's own list, nothing stands out. If it is replaced manually, only the server-side check decides whether other people's billing addresses, line items and prices become visible.

Typical BOLA pattern
# The signed-in customer requests their own order
GET /store-api/order?filter[id]=0f3a...own-id
sw-access-key: SWSCXXXXXXXXXXXX
sw-context-token: 9c2b...

# Same route, someone else's ID - the response must not be 200 here
GET /store-api/order?filter[id]=71bd...foreign-id
sw-access-key: SWSCXXXXXXXXXXXX
sw-context-token: 9c2b...

# Expected behaviour: 403 or an empty result set,
# decided from the context - not from the submitted ID

Three assumptions typically lead into this pattern. First: the ID is a UUID, nobody will guess it. Unpredictability is not access control, because IDs appear in redirects, exports, logs, support tickets and browser histories. Second: the frontend only displays the customer's own orders. The frontend is a display layer, not a control instance. Third: the endpoint is internal anyway. Anything a browser can call is public.

  • Context instead of parameter: ownership is derived from the authenticated context, not from a value in the request. A submitted ID may only narrow the result set, not widen it.
  • Central enforcement: one shared check layer instead of individual checks per controller. Otherwise, whatever is forgotten in one place only surfaces during an incident.
  • Treat object types individually: orders, addresses, wishlists, documents, returns and quotes each have their own ownership rules - especially in B2B scenarios with several users per company account.
  • Automate negative tests: at least one test per read route that expects an error status for a foreign ID. These tests belong in the pipeline, not in a one-off audit.
  • Least privilege throughout: the principle of minimal rights transfers directly to route level from a zero trust approach for online shops.
One rule for every read route

An ID in the request is a filter, not a proof of entitlement. Applying that sentence consistently closes most object level gaps before they appear.

Property level authorization: fields no route may accept

The second category sits one level deeper. API3:2023 Broken Object Property Level Authorization merges two older risks - excessive data exposure and mass assignment - and names the shared root cause: missing or inadequate authorization validation at object property level (OWASP API Security Top 10). The question is no longer which object someone may touch, but which of its properties.

On reads this shows up as over-delivery: the route serialises the complete entity and leaves the selection to the frontend. Internal calculation fields, supplier notes, gross margins or customer group assignments end up in a response that anyone can read in the network tab. On writes it shows up as mass assignment: the request body is mapped onto the entity, and fields nobody thought about are written along with it.

Dangerous write payload
{
  "quantity": 2,
  "productId": "a17c...",

  "price": 0.01,
  "discountPercentage": 90,
  "customerGroupId": "b2b-special-terms",
  "taxFree": true,
  "orderState": "paid"
}

// Only quantity and productId are permitted here.
// All other fields must be discarded server-side -
// via an allowlist, not via a blocklist.
Field groupReadableWritableTypical consequence of a mistake
Quantity, product IDyesyesnone
Price, discount, tax exemptionyes (calculated)noorder at an invented price
Customer group, price listnonosomeone else's special terms
Order and payment statusyesnounpaid goods get shipped
Internal notes, marginnonoleakage of calculation data
Email, customer numberown recordrestrictedaccount takeover via detour

The robust approach is the same in both directions: allowlists. On reads, an explicit output object defines which fields leave the route - regardless of what else the data model contains. On writes, an assignment list defines which fields are taken from the request at all; everything else is discarded instead of silently stored. Blocklists regularly fail because a new field in the data model does not force anyone to maintain the list.

Extensions are the most common entry point

Custom routes and plugins often bring their own serialisation - and do not automatically inherit the field rights of the core routes. For every custom endpoint, check separately which fields it returns and which it accepts. For third-party extensions, that check belongs in the acceptance process, just as described for supply chain risks in the shop stack.

Unrestricted resource consumption: rate limits are business logic

API4:2023 Unrestricted Resource Consumption describes endpoints that operate without quantitative limits. Affected are not only compute time and memory but everything that costs money per call - emails, SMS, external lookups (OWASP API Security Top 10). The OWASP Foundation's example scenarios are uncomfortably concrete: an attacker triggering thousands of chargeable SMS through an unprotected password reset; 999 upload mutations batched into a single request, rendering a per-request limit ineffective; and a file that grew from 13 GB to 18 GB and drove the monthly bill from 13 to 8,000 US dollars (OWASP API Security Top 10).

In a shop context three endpoint groups are particularly exposed - and in a headless setup all three are directly callable, without the detour via a rendered page.

Login and password reset

Without a limit, the sign-in endpoint becomes an invitation for credential stuffing: credentials from third-party leaks are tried in bulk. Password reset and contact forms additionally incur delivery costs per attempt.

Search and listing

Freely combinable filter and sort parameters invite price scraping. Competitors pull the entire assortment and pricing while the database carries the load - often during the very hours with the most genuine traffic.

Cart and availability

Automated line item creation ties up stock, distorts metrics and creates downstream load. For scarce articles this quickly turns into an availability problem for real customers.

Shopware ships a configurable rate limiter for these cases. Out of the box, login, guest_login and oauth use a staggered backoff strategy: 10 attempts in 10 seconds, then 15 in 30 seconds, then 20 in 60 seconds, resetting after 24 hours. For reset_password, user_recovery, contact_form and newsletter_form, stricter values of 3 attempts in 30 seconds, 5 in 60 seconds and 10 in 90 seconds apply; app_shop_verify uses a sliding window of 60 calls per 60 minutes (Shopware Developer Documentation). Adding cart line items runs through cart_add_line_item and can be limited in the system configuration.

Since Shopware 6.7.10.0, two additional limiters close the blind spot of classic IP limits: login_user limits per email address regardless of IP, login_client limits per IP regardless of email address (Shopware Developer Documentation). Exactly that combination is effective against distributed sign-in attempts where each individual IP stays inconspicuous.

  • Derive limits from the business: how many sign-in attempts, search queries or cart changes are plausible for genuine customers within a minute? The value follows from the business, not from a default.
  • Cap response size: maximum limit values and an upper bound for nested associations prevent a single call from producing half the catalogue.
  • Count batched operations: counting requests alone misses bundled operations. What should be counted is the number of operations, not the number of HTTP requests.
  • Respond cleanly: a 429 Too Many Requests with Retry-After is actionable for legitimate clients - a timeout is not.
  • Limit background load: endpoints that write messages into the queue need their own limits. How workers behave under load is described in the article on message queue and workers in production.
Limits are no substitute for fraud detection

Rate limits slow down volume. Against few, targeted abuse patterns - voucher or returns abuse, for instance - business-level controls help, as described under fraud detection in e-commerce. The two layers complement each other but do not replace one another.

Access key hygiene: what may live in the client and what may not

The store access key, sent in Shopware as the sw-access-key header, sits in the frontend code or the build artefact - and is therefore public by definition. It identifies the sales channel a call runs against. It does not prove who is calling, and it grants no rights. Treating it as a secret means building protection on a value that any visitor can read from the developer console.

The actual user identity hangs on the context token, created at sign-in and carried along with every request. All ownership checks from the previous sections have to hang on that context - not on the access key. And what applies to the store access key applies even more strictly to Admin API credentials: client ID and secret belong exclusively in server-side environments. A frontend calling admin endpoints directly moves administrative rights into the browser.

A server-side intermediate layer

Tasks requiring elevated rights run through a dedicated server-side layer - the SSR part of the frontend or a lean backend-for-frontend, for example. The browser only sees the narrow, purpose-built route, not the credentials behind it.

Strict separation by environment

Separate sales channels and separate keys per environment. A key from staging should not work in production - and vice versa. That limits the damage when a test system is compromised.

Rotation as routine

Keys and secrets get a fixed rotation rhythm and a documented emergency swap. What matters is that rotation works without a deployment freeze - otherwise it gets postponed when it counts.

Keep secrets out of the repository

Automated secret scanning in the repository and build pipeline finds accidentally committed credentials early. Searching the history only after an incident is regularly too late. Fitting alongside: tailored development with a clean separation of code and configuration.

A key in the browser is a name, not a lock. Everything it can open has to be safe without it, too.

XICTRON development team

CORS, origin checks and WAF rules instead of a wildcard

A02:2025 Security Misconfiguration ranks second in the current OWASP list: 16 mapped CWEs, an average incidence rate of 3.00% with a peak of 27.70%, 719,084 occurrences and an average weighted exploit score of 7.96 (OWASP Top 10 2025). The category includes CWE-942, a permissive cross-domain policy with untrusted domains (OWASP Top 10 2025) - in everyday terms, the wildcard release that was set once for a test and then forgotten.

One clarification is worth making: CORS is a browser rule, not access control. A script outside the browser ignores it entirely. A generous CORS configuration therefore does not make an already insecure route less secure - it merely makes it more conveniently attackable from third-party web pages. Conversely, a strict CORS configuration replaces none of the authorization checks from the previous sections.

Configuration pointRiskyRobust
Allowed originwildcard for all domainsfixed list of your own frontend domains
Credentials in the browserwildcard plus credentialscredentials only with an exact origin
Allowed methodsall methods blanket-allowedonly those actually required
Allowed headersall headers blanket-allowedallowlist including context headers
Preflight cacheunboundedshort, deliberately chosen duration
Error responsesstack trace and internal pathsgeneric message, details to the log

A filtering layer belongs in front of the application, catching patterns before they reach PHP. A web application firewall is not a replacement for correct authorization but an additional layer: it blocks known attack signatures, caps request sizes and absorbs part of the automated traffic. Where that layer sits - in the reverse proxy, at the edge or both - depends on the operating model and belongs in the planning of hosting and maintenance.

  • Allowed origins maintained explicitly, no wildcard in production
  • Debug, profiler and development routes disabled in production
  • Error output without internal paths, version numbers or stack traces
  • Security headers set and regularly verified against the actual state
  • API versions and published endpoints inventoried - a risk of its own per OWASP (API9:2023 Improper Inventory Management)
  • Deprecated endpoints actually switched off, not merely removed from the documentation

Detect instead of hope: logging, alerting and incident flow

The OWASP Foundation renamed and sharpened its logging category for 2025: A09:2025 Security Logging and Alerting Failures stresses that recording without alerting stays worthless. Without logging and monitoring, attacks cannot be detected, and without alerting a fast response is barely possible (OWASP Top 10 2025). The category's figures: 5 mapped CWEs, average incidence rate 3.91%, peak 11.33%, 260,288 occurrences and an average weighted exploit score of 7.19 (OWASP Top 10 2025).

In practice, detection rarely fails for lack of logs but for lack of evaluation. A headless setup produces access data in several places - reverse proxy, frontend server, application, database. Anyone who does not bring these traces together sees a distributed attack pattern as three inconspicuous individual snapshots.

  • Failed authorization: every 403 response with route, object type and context identifier - clusters on one route are the clearest early signal of object level probing.
  • Sign-in behaviour: the ratio of success to failure per time window, spread across IP ranges and accounts. A drop in that ratio points to automated attempts.
  • Rate limit hits: every 429 response with endpoint and trigger. A sudden increase reveals scraping or bot load before response times rise.
  • Discarded fields: when an allowlist discards a field, that is an event - usually an integration bug, occasionally an attempt.
  • Response volume per account: unusually large or unusually many responses per context suggest systematic extraction.
  • Without personal content: what gets logged is metadata and identifiers, not plain-text data. Retention periods and a deletion concept are part of the logging configuration.

Alerting requires thresholds someone actually answers. An alert that fires daily without consequence is ignored within a few weeks. What works is a small number of clearly owned signals with a defined response time - plus a documented flow for the case that one of them applies.

  1. Scope it: determine the affected route, time frame and contexts, secure raw data before rotation overwrites it.
  2. Contain it: throttle or temporarily disable the affected endpoint, rotate keys and tokens of the affected channels.
  3. Assess it: check which records were actually delivered - response sizes in the log are often more telling here than request counts.
  4. Report it: review and document notification duties under the General Data Protection Regulation and, depending on exposure, further regulations within the applicable deadlines.
  5. Close it: add the missing check, write a regression test, roll out the fix.
  6. Follow up: fix the detection gap - if the incident was not surfaced by your own alerting, that is a finding in its own right.
The most common finding in hindsight

In many cases the decisive traces were already there before the incident - they simply were not evaluated by anyone (project experience). Auditable logs without a defined alert threshold are an archive, not an early warning system. The fundamentals are covered in the article on IT security in e-commerce.

The pre-launch review plan

The individual measures only take effect if they are systematically ticked off before release - and again after every larger release. The following list can be adopted as an acceptance criterion in a project and covers the OWASP categories named in this article.

  • Complete inventory of all reachable endpoints, including custom routes from extensions
  • One negative test per read route using a foreign object ID, expecting an error status
  • One allowlist of permitted fields per write route, documented and tested
  • Response objects defined explicitly - no automatic serialisation of entire entities
  • Rate limits for sign-in, password reset, contact form, search and cart set and measured
  • Separate limits per account and per origin so that distributed attempts stand out
  • Store access keys separated per environment, admin credentials used server-side only
  • Key rotation procedure documented and rehearsed once
  • CORS without a wildcard, security headers set, debug routes disabled
  • Logging of 403, 429 and discarded-field events active, with defined alert thresholds
  • Cache rules verified: no personalised response in a shared cache
  • Incident flow written down, responsibilities and response times named

For many operators, this review plan doubles as preparation for regulatory requirements. The Cyber Resilience Act and its obligations for online shops requires traceable vulnerability handling, among other things - a documented check chain and a practised reporting path feed straight into that. Where several systems are connected via integrations, the same care applies to every additional connection: each further integration is another path into the business logic.

Security as part of the headless architecture

Headless shifts responsibility. What the storefront used to handle implicitly has to be decided and verified explicitly in a decoupled setup - route by route, field by field. That is not an argument against the architecture but a consequence of it: the same decoupling that enables fast frontends and flexible channels makes authorization logic a load-bearing component.

The commercial leverage is tangible. A single object level flaw can trigger a data protection notification, loss of trust and operational emergencies; a missing rate limit costs compute time, distorts metrics and hands your own assortment to the competition. Both can be reduced considerably with comparatively short measures - provided someone actually looks at the endpoints.

That is exactly what we do: XICTRON reviews Store API endpoints, the authorization logic behind every route and the rate limits in place, maps findings to the OWASP categories and hardens existing headless setups - without rebuilding the frontend. Whether that happens within ongoing e-commerce projects, as standalone consulting or alongside Shopware development depends on your setup.

This is how your decoupled shop could look:

ElektronikDemo

Elektronik-Fachhandel

This design example shows how a decoupled online shop with clear product navigation and fast response times can look. We build individual headless solutions in which frontend, Store API and authorization logic are cleanly separated.
HeadlessStore APISecurityAuthorization
Discuss your project
Demo
Sources and studies

This article is based on data and documentation from: OWASP Foundation - Top 10 Web Application Security Risks 2025 (A01 Broken Access Control, A02 Security Misconfiguration, A09 Security Logging and Alerting Failures, data basis and methodology), OWASP Foundation - API Security Top 10 2023 (API1, API3, API4, API8, API9), Shopware Developer Documentation (Rate Limiter, Store API, Add Rate Limiter to API Route) as well as BSI - The State of IT Security in Germany 2025 and the BSI recommendations on securing web applications. Own project experience is included in addition. The figures cited may vary depending on the survey date, data basis and measurement method.

As a rule, no. Authentication clarifies identity, not ownership. Without an additional check on whether the requested object belongs to the authenticated context, the route stays open for object level access - which is exactly what API1:2023 Broken Object Level Authorization describes (OWASP API Security Top 10). It is therefore advisable to derive ownership server-side from the context rather than from a value in the request.

It is delivered with the frontend and is therefore typically publicly visible. It identifies the sales channel but does not replace authorization. What should be treated as a secret are the Admin API credentials - these should be used server-side only, for example in an intermediate layer that the frontend talks to.

For sign-in, guest sign-in and OAuth, a staggered backoff strategy typically applies with 10 attempts in 10 seconds, 15 in 30 seconds and 20 in 60 seconds; for password reset, contact form and newsletter, stricter values of 3 in 30 seconds, 5 in 60 seconds and 10 in 90 seconds apply (Shopware Developer Documentation). From version 6.7.10.0 onwards, additional limiters per email address and per IP are available. The values can be adjusted in the configuration to suit your own business.

Only to a limited extent. CORS is a rule enforced by browsers; calls made outside a browser are unaffected. A tight configuration typically reduces the attack surface from third-party web pages but does not replace server-side authorization. A permanent wildcard release falls under security misconfiguration at OWASP (OWASP Top 10 2025).

Experience suggests before every larger release plus on a fixed rhythm during ongoing operations. Changes to custom routes, extensions and field serialisation are particularly relevant, because new fields often slip unnoticed into responses or write payloads there. Automated negative tests in the pipeline reduce the manual effort considerably.

Typical indicators are clusters of 403 and 429 responses on individual routes, a conspicuous ratio of failed to successful sign-ins, and unusually large or unusually many responses per context. Without consolidating the logs from proxy, frontend and application, such patterns usually stay invisible - which is why OWASP assigns missing logging and alerting a risk category of its own (OWASP Top 10 2025).

Tags:#Security#Headless#Store API#OWASP#Development