Software Engineering

Shadow APIs: The Legacy Endpoints Nobody Documented

TuniCyberLabs Team
9 min read

Undocumented endpoints from retired apps, ended integrations and half-finished migrations still answer requests, with authorisation logic from before your last rewrite. Here is how attackers find them, and a discovery and decommissioning method that works.

What a shadow API actually is

A shadow API is any endpoint your systems still answer that nobody owns, nobody documents and nobody monitors. It is not a design flaw, it is sediment: the leftovers of a mobile app you retired, a partner integration that ended, a migration that ran in parallel for six months and was never fully switched off.

The dangerous part is not that the endpoint is undocumented. It is that undocumented means unreviewed. It never appears in a security assessment, never gets a rate limit, never gets the authorisation fix the rest of the application received two years ago.

  • Zombie endpoints: old versions still live because one client might still call them.
  • Orphaned endpoints: the team that built them left, and the service kept running.
  • Internal endpoints that became external when a firewall rule, a proxy or a cloud migration quietly changed the boundary.
  • Debug and admin routes shipped for a launch and never removed, usually with the weakest authentication in the estate.

Where shadow endpoints come from

They come from normal engineering under time pressure, not negligence. Every migration, acquisition, mobile release and integration adds endpoints, and very few projects have a funded step that removes them. Over ten years that delta compounds into an attack surface larger than the product itself.

The recurring sources, in rough order of how often we find them:

  • Versioning without retirement. Version three ships, versions one and two stay online indefinitely because turning them off requires knowing who calls them, and nobody does.
  • Mobile app backends. Old app versions must keep working, so the endpoint that serves them keeps working, including for people who are not using the app at all.
  • Partner and B2B integrations. A contract ends, the credentials are never revoked, the endpoint stays reachable.
  • Copy-paste environments. Staging and UAT clones exposed to the internet with production data and weaker controls.
  • Platform plugins. A CMS extension registers routes you never asked for, which is one reason The WordPress Security Reality Check Every Business Owner Avoids is worth reading before assuming your surface is only what you built.
  • Half-finished migrations. The old system is left running "just in case" and stays running for years, a pattern covered in Leaving WordPress: A Migration Playbook That Does Not Lose Your SEO.

How attackers find the endpoints you forgot

They enumerate, they do not guess. Certificate transparency logs reveal hostnames you never advertised. Old mobile app packages are decompiled to extract base URLs and keys. JavaScript bundles are parsed for every path the front end has ever called. Public code repositories and archived pages give up the rest.

The reconnaissance workflow is cheap, automated and continuous:

  • Subdomain and host discovery from certificate logs and passive DNS, which surfaces api-old, legacy, uat and similar names with no linking required.
  • Client-side extraction. Your single page application ships a map of your API in its bundle. Mobile binaries contain base paths, header names and occasionally embedded secrets.
  • Path and parameter fuzzing against known naming conventions, guided by whatever the client-side extraction revealed.
  • Error message mining. Verbose stack traces from an old framework identify the technology and version precisely, which is then matched against the CISA Known Exploited Vulnerabilities catalogue to pick a working exploit.

Assume anything reachable is already catalogued by someone. The question is not whether an unknown endpoint will be found, it is whether you find it first.

Why the old endpoints are worth more than the new ones

Because the security work of the last five years usually landed only on the current version. The shadow endpoint runs the authorisation logic from before the rewrite, the rate limits from before the abuse incident, and the input validation from before anyone knew better. It is the same database with worse doors.

The failure patterns that make them valuable to an attacker:

  • Broken object level authorisation. The endpoint checks that you are logged in but not that the record belongs to you, so changing an identifier in the request returns someone else's data. This is the first entry in the OWASP API Security Top 10 for a reason.
  • No authentication at all. Internal-only endpoints written when the network was the perimeter, later exposed by a routing change.
  • Excessive data exposure. An old endpoint returns the whole user object and lets the client hide the fields, so the raw response contains password hashes, tokens or internal notes.
  • Mass assignment. An update endpoint accepts fields the current one rejects, including role or account status.
  • No rate limiting, which turns a legitimate lookup into a bulk export.

Discovery: build the inventory that is true, not the one that is documented

Start from traffic and code, never from documentation. Your specification describes intent. Your gateway logs, DNS records and source code describe reality. Reconcile all four and the gap between them is your shadow surface, which in a legacy estate is routinely larger than the documented one.

Run these five passes and merge the results into one list:

1. Traffic analysis. Pull every distinct path from load balancer, gateway and web server logs over the longest window you retain. Anything answering that is not in your spec is a candidate. 2. External enumeration. Do to yourself what an attacker does: certificate transparency, DNS records, port scans of your ranges, and a look at what is exposed from cloud accounts nobody claims. 3. Code and route extraction. Enumerate route definitions from every repository and every deployed artifact, including the ones with no recent commits. 4. Client-side extraction. Parse front end bundles and mobile binaries for every path and host they reference. 5. Dependency and component inventory. Generate a software bill of materials so you know what libraries are exposing what, using CISA guidance on SBOM and a machine-readable format such as CycloneDX.

For every endpoint, record four fields: what it does, who owns it, who calls it, and whether it touches personal or financial data. Endpoints with no owner and unknown callers are your priority list.

Triage: keep, fix or kill

Sort by exposure and data sensitivity, not by how old the code looks. An unauthenticated endpoint returning customer records outranks everything, even if it serves ten requests a day. Then give each entry one of three fates, recorded with a name and a date beside it.

  • Keep and document. It has a real caller and a real owner. Bring it into the specification, the gateway, the authentication scheme and the monitoring, then treat it as a supported product surface.
  • Fix and keep. The function is needed but the implementation predates your current controls. Add authorisation checks per object, response filtering, rate limits and logging before anything else.
  • Kill. No known caller, no owner, or duplicated by a newer endpoint. This is the majority in most legacy estates, and every one you remove is permanently removed risk rather than managed risk.

Anything unauthenticated that reads or writes personal data goes to the front of the queue regardless of traffic volume, and anything on a host you cannot name should be assumed hostile until proven otherwise.

Decommissioning without breaking a customer you forgot about

The fear that stops decommissioning is real: somewhere a client integration might depend on that endpoint. Handle it with evidence instead of courage. Instrument first, warn second, degrade third, remove fourth. Every step is reversible until the last one, and each produces the data the next step needs.

The sequence that works:

1. Instrument. Log every call with caller identity, source address, user agent and timestamp. Run it for a full business cycle, so quarterly and annual jobs appear. 2. Notify. Contact every identified caller with a date. Silence from a caller you can name is a decision they have made, not a reason to delay. 3. Deprecate loudly. Return a deprecation header and add latency, so integrations that ignore email still notice. 4. Brownout. Return errors for scheduled windows that grow over time. This surfaces the callers who never answered, safely and on your schedule. 5. Disable, do not delete. Turn the route off but keep the code and the ability to restore it for a defined period. 6. Remove and revoke. Delete the code, revoke the credentials, close the firewall rule, retire the DNS record and remove the host so it cannot be resurrected by an old deployment.

Keep the credential revocation step. An endpoint that is gone but whose API keys still work elsewhere has moved the problem, not solved it.

Keeping the inventory honest after the cleanup

A one-off audit decays within a quarter. The durable fix is making the inventory a build-time artifact rather than a document. If a route can reach production without appearing in the specification, the gateway and the monitoring, you will repeat this exercise in eighteen months.

  • Route registration as a gate. No endpoint reaches production unless it is declared, owned and authenticated. Fail the pipeline when the code exposes something the specification does not.
  • Continuous external enumeration. Run attacker-style discovery on a schedule and alert on anything new, including hosts from cloud accounts nobody remembers creating.
  • Ownership metadata that expires. An endpoint with no confirmed owner after a set period is automatically flagged for the kill list.
  • An expiry date on every version. Ship version three with the retirement date of version two already agreed.
  • Regulatory pressure is now on your side. Vulnerability handling and product security obligations under the EU Cyber Resilience Act make an accurate component and interface inventory a compliance asset rather than an engineering nicety.

This is exactly the work that a maintenance retainer should include and usually does not, which is the point of What Website Maintenance Should Actually Include (and What You Are Probably Paying For).

How TuniCyberLabs helps

We run the discovery for you, from traffic, code, clients and external enumeration, then produce one inventory with owners, callers and data classification. We fix what must stay, decommission the rest with a brownout schedule that does not break real integrations, and wire route registration into your pipeline so the surface stops growing.

Send us your domains and repositories and we will show you what is answering today: see how we work.

TAGS
shadow APIsAPI securitylegacy systemsattack surfaceAPI inventorydecommissioningtechnical debt

Frequently Asked Questions

What exactly counts as a shadow API?

+

Any endpoint your infrastructure still answers that has no owner, no documentation and no monitoring. Typical examples are old API versions kept alive for a client nobody can name, backends for retired mobile apps, partner integrations that outlived the contract, and debug or admin routes shipped for a launch. If it responds and nobody owns it, treat it as hostile.

How would an attacker discover an endpoint we never published?

+

Enumeration, not guesswork. Certificate transparency logs expose hostnames, passive DNS reveals subdomains, front end bundles list every path the application calls, and old mobile packages can be decompiled for base URLs and header names. Error messages then confirm the framework and its version. All of it is automated, continuous and cheap, so assume your surface is already mapped.

Where do we start if we have no API inventory at all?

+

Start with gateway and web server logs. Extract every distinct path answered over the longest retention window you have, then compare that list against your documentation and the route definitions in your code. Add external enumeration of your own domains. The endpoints that appear in traffic but nowhere else are your shadow surface and your first priority.

How do we switch off an old endpoint without breaking a client?

+

Instrument before you decide. Log every caller with identity, address and timestamp for a full business cycle so quarterly and annual jobs appear, notify the callers you can identify with a firm date, then add deprecation headers and scheduled brownout windows that grow over time. Disable the route before deleting the code, and revoke the associated credentials when you remove it.

Are shadow APIs a compliance problem as well as a security one?

+

Yes. Obligations around vulnerability handling, incident reporting and product security all assume you know what your systems expose. An undocumented endpoint holding personal data is a data protection issue before anyone exploits it, and an accurate interface and component inventory, backed by a software bill of materials, is what turns that assumption into evidence you can show.

How do we stop the problem coming back after a cleanup?

+

Make the inventory a build artifact instead of a document. Fail the pipeline when code exposes a route that is not declared, owned and authenticated. Run attacker-style external discovery on a schedule and alert on anything new. Give every API version a retirement date at launch, and flag any endpoint whose owner has not been confirmed recently.

Need help with
this topic
?

Our team specializes in the technologies and strategies discussed in this article. Let’s talk about how we can help your business.

Get in Touch