APIs

A Security Pass for a New Public Endpoint

The HTTP request arrives before anyone has decided whether the endpoint should exist. A new public URL goes live on a Tuesday, and by Wednesday morning the logs show a few thousand requests from addresses nobody recognizes. That is the moment the work actually begins, because a public endpoint is less a feature than a standing invitation, and the pass that controls it has to be designed before the first stranger walks up.

The phrase "security pass" gets used loosely in engineering. Sometimes it means an API key. Sometimes it means a signed token, a mutual TLS certificate, an OAuth scope, or a rate limit bucket. The differences matter, but the underlying question is the same for all of them: when a request with no prior relationship to the system shows up at a new door, what does the system check, and what does it hand back?

Most of the public endpoints that leak data, get scraped into oblivion, or turn into a denial-of-service headline fail this question in the same boring way. They were designed for the happy path. A developer wired up a route, tested it from a laptop, watched the JSON come back, and shipped. The pass was an afterthought added in a follow-up ticket, which is a polite way of saying it was never added at all.

What a Public Endpoint Actually Exposes

A private endpoint sits behind a network boundary. The firewall does the first round of filtering, the internal service mesh does the second, and the attacker has to already be inside to reach the door. A public endpoint has no such luxury. The entire internet is the network boundary, and every address in it is a potential caller with a fresh browser, a script, and an unlimited appetite for retries.

This changes the threat model in ways that are easy to underestimate. An internal endpoint can afford to assume the caller is cooperative. A public one cannot. The caller might be a scraper running through a list of known API paths, a botnet probing for unauthenticated admin routes, or a competitor's crawler that has already indexed the product catalog. None of them will read the documentation. All of them will find the endpoint within hours of it going live, because public DNS is discoverable and certificate transparency logs are public records.

The first real decision is therefore about identity. Does the endpoint require a caller to prove who it is before it does any work? If the answer is no, the endpoint is anonymous by design, and the pass has to come from somewhere else: a rate limit, an origin check, a signed request body. If the answer is yes, the endpoint needs a credential issuance system, and that system is a product in its own right.

The Three Jobs of a Pass

A security pass on a new public endpoint has to do three jobs, and conflating them is where most implementations get into trouble. The first job is authentication: proving that the caller is a specific entity and not an impersonator. The second is authorization: deciding what that entity is allowed to do once identified. The third is accounting: keeping a record of what happened so that misuse can be detected and abuse can be billed or blocked.

Plenty of systems collapse all three into a single opaque string. A long random token gets minted, handed to the caller, and checked against a database row that says "valid" or "invalid." This works until the token leaks, at which point the only remedy is revocation, and revocation breaks every legitimate caller who happens to share the token. A single shared key for an entire partner integration is a pass in name only, because it cannot distinguish one caller from another.

A better shape gives each caller its own credential, scopes that limit what that credential can reach, and a log line that records which credential was used on which request. The credential can be rotated without disrupting anyone else. The scopes mean a read-only integration cannot accidentally delete records. The log line means an anomaly shows up as a specific caller behaving oddly, which is far easier to act on than a vague spike in traffic.

Rate Limits Are Part of the Pass

The most common omission on a new public endpoint is the rate limit, and it is usually omitted because it feels like a performance concern rather than a security one. That framing is backwards. A rate limit is the cheapest form of access control available. It caps the damage any single caller can do, it protects the database from being hammered by a retry storm, and it turns a would-be scraper into a slow one that eventually gives up or gets caught.

The limit should be tied to the credential, not just the source IP. IP-based limits are trivially defeated by a caller with a pool of addresses, and they punish legitimate users who happen to share a corporate NAT. A per-credential limit follows the caller wherever it goes. The numbers should be set from observed usage, with a generous ceiling for normal traffic and a hard floor that stops anything pathological. The exact figures matter less than the fact that a limit exists and is enforced at the edge before the request touches the database.

Rate limiting also needs a response that tells the caller what happened. A bare 429 with no headers is a dead end for an integrator trying to build a backoff strategy. The standard headers that report the remaining quota and the reset time turn the limit into a contract rather than a mystery, and integrations that respect the contract cause far fewer support tickets.

The Difference Between a Key and a Session

Server-to-server integrations usually want a long-lived credential, and that is fine as long as the credential is scoped and rotatable. Browser-based callers are a different problem entirely. A key embedded in a client-side application is a key that has been published, because anyone with a browser can open the developer tools and read it. This is not a flaw in the key; it is a flaw in the assumption that a client can keep a secret.

For browser callers, the pass has to be issued per session and tied to the user who authenticated. The endpoint then checks the session, not a static key, and the session expires on a schedule that matches the sensitivity of the data. A short-lived token that is refreshed through a server-side exchange keeps the long-lived secret off the client entirely. The refresh exchange is the place to enforce additional checks, such as device binding or a re-authentication prompt for sensitive operations.

Mixing these two models on one endpoint is where trouble starts. An endpoint that accepts either a server key or a browser session must apply the stricter rules to both, or the looser path becomes the way in. The cleanest approach is separate routes with separate passes, even if they share the same backend logic underneath.

Logging the Pass, Not Just the Request

Request logs that record the URL, the status code, and the latency are useful for operations. They are nearly useless for security, because they do not say who called. A pass-aware log records the credential identifier, the scopes it carried, the decision the authorization layer made, and the reason for any rejection. When a credential starts returning 403s at three in the morning, the on-call engineer needs to see that in one query, not after an hour of grepping.

The log also serves the accounting job. Usage-based billing, quota enforcement, and abuse detection all depend on knowing which credential did what. A public endpoint without this record is a public endpoint that cannot answer the first question any incident review will ask: who was calling, and were they supposed to be?

Revocation Has to Be Fast and Boring

Every pass will eventually need to be taken away. A partner goes out of business, an employee leaves, a key shows up in a public repository, a crawler misbehaves. The speed of revocation is the real test of whether the pass design holds up. If revoking a credential requires a deploy, the credential stays live for the length of a release cycle, which is far too long.

Revocation should be a database write that takes effect on the next request, with a cache TTL short enough that the window of exposure is measured in seconds. The system should also support scoped revocation, so a single permission can be pulled without killing the entire credential. And revocation should be logged as loudly as issuance, because a spike in revocations is itself a signal worth investigating.

The Pass Outlives the Launch

The launch is the easy part. A new public endpoint gets attention, a review, and a checklist before it goes live. The hard part arrives six months later, when the endpoint has accumulated three integrations, two internal callers, a mobile app, and a webhook consumer that nobody remembers approving. The pass design determines whether that growth is manageable or whether the endpoint becomes a liability that has to be retired.

Credentials that are scoped, rotated on a schedule, logged per caller, and revocable in seconds keep the endpoint governable as it ages. Credentials that are shared, permanent, and invisible in the logs turn the same endpoint into a black box. The difference is not the amount of work at launch. It is the decision, made once, about whether the pass is a real object with a lifecycle or a string that happens to be checked.

The endpoint that survives is the one where the pass was designed first, before the route was wired, before the documentation was written, before the first stranger found the URL. That ordering is the whole discipline. Everything after it is maintenance.

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button