Skip to content

API1/API5: Authorization guidance assumes a single hop - end-user identity is lost when APIs call other APIs #180

Description

@alienatix

API1 and API5 both frame authorization as a check against the caller. API1 says the checks "should validate that the logged-in user has permissions to perform the requested action on the requested object," and API5 asks whether "a regular user" can reach administrative endpoints. Every scenario in both categories is a single hop from client to API.

In service-to-service architectures, the API that performs the action is often not the one the user called. A public-facing API authenticates the user, then calls internal APIs to fulfil the request. By that point, the downstream API often has no "logged-in user" to check against. It sees the upstream service's credential, a forwarded token whose audience is not the downstream API, or identity asserted in a plain header. Each API assumes the other one authorized the request, so, in practice, neither does.

Proposed changes

1. API1 and API5, "Is the API Vulnerable?" Add a paragraph:

When an API fulfils a request by calling other APIs, each API that acts on an object or function must be able to authorize the action against the end user on whose behalf it is performed. The API is vulnerable if a downstream API authorizes only the calling service rather than the end user, trusts identity or role information asserted by an upstream component without verification, or assumes that authorization was performed upstream while the upstream API assumes it is performed downstream.

2. "How To Prevent" Add to both categories:

  • Propagate end-user identity to downstream APIs in a verifiable form (for example, OAuth 2.0 Token Exchange / on-behalf-of flows) rather than calling downstream APIs with a service credential alone.
  • Issue each hop a token restricted to that downstream API (audience/resource-bound). Do not forward the client's original token as-is to other services.
  • Do not trust identity or role information in plain request headers (e.g. X-User-Id, X-User-Role) unless the header is set by a trusted component, stripped from all incoming client requests, and the downstream service is unreachable except through that component.
  • Assign explicit responsibility for each authorization check to a specific API in the call chain, and test that downstream APIs reject requests lacking valid end-user context.
  • Do not grant service identities broad privileges on the assumption that upstream checks are sufficient; downstream authorization should still be scoped to the end user.

3. New scenario for API1:

An e-commerce platform's public Orders API authenticates the user and exposes GET /orders/{orderId}/invoice. To build the response, it calls an internal Billing API at GET /internal/invoices/{invoiceId} using the Orders service's own credential. The Billing API returns any invoice to a request carrying that credential, on the assumption that the Orders API has already checked ownership. The Orders API passes the client-supplied invoiceId through without checking it, on the assumption that the Billing API does. An attacker who changes the invoice ID in the request receives other customers' invoices. Each service individually "implements authorization," but no service checks whether this user may access this invoice.

4. New scenario for API5:

An API gateway validates users' access tokens and forwards requests to internal services, adding X-User-Id and X-User-Role headers that the internal services trust for authorization decisions. The gateway does not remove these headers when a client sends them itself. An attacker adds X-User-Role: admin to a request, and an internal service grants access to its administrative functions.

5. References: Add CWE-441: Unintended Proxy or Intermediary ('Confused Deputy') and RFC 8693: OAuth 2.0 Token Exchange.

This proposes new guidance and scenarios, so I understand it may land in the next edition. Happy to open a PR after there's an agreement on placement (scenarios in API1/API5 vs. a dedicated section).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions