Skip to main content
Back to Blog
Cloud SecurityAPI SecurityZero TrustDevSecOpsCyberSecurityInfoSecCloudSecurityVulnerabilitymanagement

Securing the API Composition Layer in Microservice Architectures

Eldar Aydayev· CEO, Aydahwa Enterprise August 14, 2026 11 min read
Securing the API Composition Layer in Microservice Architectures

The place where microservice data gets merged is where your security model actually lives

Picture a single account screen in a banking or delivery app. It shows the user's profile, their five most recent orders or transactions, the live status of each one, and a short list of recommendations. That is four separate services behind the scenes, each holding only its own slice of data. Something has to call all four and stitch the responses into the shape the screen expects. That stitching step is API composition, and it exists in every system where data is split across more than one service.

Most architecture discussions treat composition as a performance and ownership question: where do you put the merge so the screen loads fast and the right team can ship changes. Those are real concerns. But in the engagements we run, the composition layer is also the single point where authorization decisions, data exposure, blast radius, and cacheable content all concentrate. Get it wrong and you have not built one insecure endpoint. You have built a fan-out machine that multiplies a single mistake across every downstream service it touches.

This is the part of a service architecture that rarely gets a dedicated threat model, precisely because it looks like plumbing. It is not plumbing. It is the trust boundary. Here is how we approach it.

Four places to merge, four different blast radii

The code that merges those four responses can run in several places, and the security consequences differ for each.

Client-side composition puts the merge in the mobile app or browser. The device calls all four services directly. That means every service is exposed to the public internet, every service has to authorize every request on its own, and your access-control logic is duplicated across platforms you do not fully control. It is the hardest posture to secure because the attacker sits on the same side of the wire as your composition logic.

An API gateway moves the merge to a server you own, in front of the services. One public entry point, four private ones. This is usually the right call, but it concentrates risk: the gateway now holds credentials or trust that reach every backend, and a flaw there is a flaw everywhere.

A Backend for Frontend (BFF) is a gateway specialised per client, so the mobile app and the web app each get their own composition service. Smaller blast radius per BFF, but more surfaces to keep patched and consistent.

A GraphQL layer turns composition into a query engine the client drives. Powerful, and a category of risk on its own, which we will get to.

Edge composition pushes the merge to a CDN point of presence for latency. Fast, and the most dangerous place to cache anything tied to a specific user.

Whichever you pick, the security question is the same: when this layer calls four services on a user's behalf, does each downstream call carry that user's identity and authority, or does it ride on the composition layer's own privileges? That single distinction drives most of what follows.

Broken object-level authorization scales with fan-out

Broken Object Level Authorization sits at the top of the OWASP API Security Top 10 (API1:2023) for a reason, and composition layers are where we find it most often. The pattern is simple to describe and easy to ship by accident. The gateway authenticates the user at the front door, confirms they hold a valid token, then fans out to the order service, the delivery service, and the rest, asking each for the requested records. The downstream services see a call from a trusted internal client and return the data. Nobody re-checks that this particular user is allowed to see this particular order.

Change the order ID in the request and the gateway will happily fetch someone else's transaction, because authentication at the edge was mistaken for authorization at the object. We have seen this exact gap in production APIs during assessments, and it is rarely visible in a code review of any single service. Each service looks correct on its own. The flaw only appears when you follow one request through the whole fan-out.

The fix is to make object-level authorization a property of every hop, not just the entrance. The composition layer must pass the end user's identity and claims to each downstream service, and each service must enforce ownership on the objects it returns. Centralising the decision in a policy engine, using something like Open Policy Agent, keeps that logic consistent instead of reimplemented five times with five subtly different bugs.

Token propagation and the confused deputy

The moment a composition layer calls downstream services with its own high-privilege credentials, it becomes a confused deputy: a trusted component tricked into using its authority on an attacker's behalf. If the order service trusts any call from the gateway, then anyone who can reach the gateway inherits the gateway's reach.

The pattern we design toward is end-to-end identity. The user's token arrives at the composition layer, and instead of dropping it, the layer exchanges it for a narrowly scoped token per downstream call using OAuth 2.0 Token Exchange (RFC 8693). Each service receives a token that names the actual user, carries only the scopes that call needs, and expires quickly. Service-to-service traffic runs over mutual TLS so a service can prove which caller it is talking to, not just assume. This is the practical shape of a zero trust architecture as described in NIST SP 800-207: no implicit trust granted by network position, every request authenticated and authorized on its own merits.

It costs more to build than a shared internal API key, and it is the difference between one compromised component and a full lateral-movement path through your estate.

Over-fetching quietly turns into data exposure

Composition creates a second, subtler leak. To assemble a screen, the aggregator often pulls whole objects from each service and then hands the merged result to the client, trusting the frontend to display only the relevant fields. The unused fields still travelled across the wire. Open the developer tools or intercept the response and the full customer record is sitting there: internal flags, other people's data joined in, PII that never needed to leave the backend.

OWASP tracks this as API3:2023, and in regulated environments it is not an abstract risk. Under PCI-DSS, an aggregated response that carries primary account numbers to a client that only renders the last four digits has just widened your cardholder data environment. Under privacy regimes, over-returning personal data is a reportable exposure waiting to happen.

The discipline is to shape data at the composition layer, not at the screen. The aggregator should request or filter down to exactly the fields the response contract defines, strip everything else before it leaves the server boundary, and treat the response schema as an allow-list rather than a convenience. What the client cannot see, it also cannot leak.

GraphQL makes composition programmable, including for attackers

GraphQL is an elegant answer to over-fetching, because the client asks for exactly the fields it wants. It also hands the client a query engine that runs against your composed data graph, and that changes the threat model.

A single crafted query can nest relationships deeply enough to force thousands of resolver calls, each fanning out to backend services, turning one HTTP request into a denial-of-service amplifier. OWASP flags this as API4:2023, Unrestricted Resource Consumption. Field aliasing lets an attacker request the same expensive operation dozens of times in one query to slip past naive rate limits. Introspection, if left on in production, hands over a complete map of your schema, including fields you never meant to advertise.

The controls are well understood and worth stating plainly. Disable introspection in production. Enforce query depth and complexity limits, and reject queries whose cost exceeds a budget before they execute. Move to persisted queries so only pre-approved operations run. Apply rate limiting that counts resolver cost, not just request count. A GraphQL composition layer without these is a load generator pointed at your own backends.

Caching at the edge can serve one user another user's data

Edge composition is tempting because it is fast, and it is where we most often find the ugliest bugs. A composed response for an account screen is, by definition, specific to one person. Cache it at a CDN with a key that ignores identity and the next user to hit that path gets served the first user's data. It is a cross-account leak created entirely by a caching config, with no application code flaw at all.

Any personalised composed response needs a cache key that includes the authenticated identity, or it needs to be marked private and not cached at shared layers at all. The rule we hold to is straightforward: public, non-personalised fragments can be cached aggressively and shared; anything shaped by who is asking is either keyed to that identity or never stored where another identity can reach it.

Composition patterns and their security tradeoffs

PatternWhere the merge runsPrimary security riskControl that matters most

Client-side composition

Mobile app / browser

Every service exposed publicly; authz logic duplicated on untrusted clients

Per-service authorization; never trust the client

API gateway

Server you own, in front of services

Confused deputy; single point that reaches every backend

Token exchange + mTLS; harden and monitor the gateway

Backend for Frontend

Per-client composition service

More surfaces to keep patched and consistent

Consistent policy engine across every BFF

GraphQL layer

Query engine over the data graph

Query-cost DoS, aliasing, schema disclosure

Depth/complexity limits, persisted queries, introspection off

Edge composition

CDN point of presence

Personalised data cached to a shared key

Identity-aware cache keys; private for anything user-specific

Why availability belongs in the composition-layer threat model

Fan-out ties the health of one screen to the health of four services. If the recommendations service hangs, a naive composition layer holds the connection open and stalls the whole response, and an attacker who notices can turn that into a cheap way to exhaust your capacity. Composition needs per-call timeouts, circuit breakers that fail a slow dependency fast, and graceful degradation so the screen returns the profile and orders even when recommendations are down. Availability engineering and denial-of-service resistance are the same work here.

That same fan-out is also your best detection surface. Because every user request passes through the composition layer, it is the natural place to emit structured, correlated logs and distributed traces. A request that suddenly fans out to ten times the usual number of downstream calls, or walks a sequence of object IDs, is visible here before it is visible anywhere else, provided you are actually collecting and watching those signals. Feeding composition-layer telemetry into a SIEM turns the busiest chokepoint in the architecture into an early-warning system.

A hardening checklist for the composition layer

  1. Threat-model the composition layer as its own trust boundary, not as plumbing between services.
  2. Propagate end-user identity to every downstream call; never let a service trust the gateway blindly.
  3. Enforce object-level authorization at each service, centralised in a policy engine so the rule is written once.
  4. Use OAuth 2.0 Token Exchange for narrowly scoped, short-lived downstream tokens, and mutual TLS between services.
  5. Shape responses to an allow-list schema at the server boundary; strip fields the client does not need.
  6. For GraphQL: disable introspection in production, set depth and cost limits, and prefer persisted queries.
  7. Give personalised composed responses identity-aware cache keys, or mark them private and keep them off shared caches.
  8. Apply rate limiting that accounts for downstream cost, not just inbound request count.
  9. Add timeouts, circuit breakers, and graceful degradation so one slow service cannot stall or exhaust the layer.
  10. Emit correlated logs and traces from the composition layer into your SIEM, and alert on abnormal fan-out.

How Aydahwa Enterprise can help

We build and assess API and cloud architectures for organisations that cannot afford a quiet data leak, including clients in banking, telecom, and critical national infrastructure. That work is grounded in the standards that hold up under audit: ISO 27001, PCI-DSS, SOC 2, the NIST Cybersecurity Framework, and CIS Benchmarks, applied by architects holding credentials such as the Microsoft Cybersecurity Architect Expert certification. When we review a composition layer, we follow real requests through the full fan-out, test object-level authorization the way an attacker would, and check what actually crosses the server boundary rather than what the design says should.

If your microservice or API estate has grown faster than its security model, a good starting point is our free cybersecurity self-assessment and the cybersecurity readiness checklist, which together give you an honest read on where the gaps are. From there, our cybersecurity services cover API and application security testing and DevSecOps hardening, our cloud security and migration practice secures the gateways, service mesh, and edge that composition runs on, and our managed IT and support team keeps the controls in place after the project ends. To talk through a specific architecture, get in touch and we will start with your real request flows, not a generic template.

Share

Need expert guidance?

Our cybersecurity and IT consultants can help you implement the strategies discussed in this article.

Call UsWhatsAppBook