API security / authorization / reliability
API replay and authorization testing: five failure cases
A token can be valid while the operation is still unsafe. Test object access, editable fields, one-time evidence, and concurrent writes separately—and verify the server rejects each broken assumption.
By Jeffrey Monica · Original engineering reference
Use negative tests, not only happy paths
These are design and test boundaries. A signal is useful only when your backend verifies it and applies an independent authorization policy.
| Failure case | Attack or failure you simulate | What the server should enforce |
|---|---|---|
| BOLA / IDOR | Sign in as tenant B and request or update an object that belongs to tenant A using A's identifier. | Check ownership or the appropriate relationship on every accessed object and reject unauthorized reads and writes. |
| Mass assignment | Add role, isAdmin, ownerId or another server-controlled field to a normal update payload. | Allowlist writable fields for the authenticated role and operation; ignore or reject privileged attributes. |
| One-time evidence replay | Submit the same challenge, one-time token, or consumed nonce twice, including concurrently. | Consume proof atomically and reject duplicate use. A client-side disabled button is not a replay defense. |
| Duplicate write on retry | Drop the response after a committed write, then retry the same logical operation with the same idempotency key. | Scope the key to the caller/operation, compare request semantics and return the original outcome without an extra commit. |
| Refresh-token reuse race | Submit two refresh requests for the same token or replay a superseded token after rotation. | Use an atomic rotation protocol, consistent reuse detection, and an explicit family-revocation/recovery policy. |
Set up the test boundary
Use two independent test tenants with their own sessions and objects, plus a low-privilege user. Record which identity owns each object and the expected response for each operation. Do not test accounts you do not control or have permission to assess.
For retry tests, distinguish transport failure from business failure. An HTTP timeout does not prove a server transaction rolled back. A safe idempotency design must account for committed work whose response never reached the caller.
What to capture for a useful bug report
A reproducible failure report is more useful than a broad claim that an API is insecure.
Initial state
Which test tenant owns which resource, the role and its privileges, and the documented operation contract.
Request variants
Method, path and the single changed field: object ID, role field, nonce, token, or idempotency key.
Concurrency
When relevant, issue overlapping requests and record ordering instead of testing only sequential replay.
Observable result
Compare status code, response body shape, durable state changes, audit reason, and number of committed writes.
Regression assertion
Write a failing test first, add the server-side guard, then rerun the identical request against the fixed path.
Threat-model limit
Say whether the result comes from a synthetic reference model, a local test app, or an explicitly authorized production target.
Do not mistake detection for enforcement
Logging a suspicious resource ID or rate-limiting repeated attempts does not replace object authorization. Likewise, an idempotency key is not an authorization token. Check the authenticated caller and business permission before deciding whether to replay a cached result.
The linked ZombieMutex labs intentionally contain vulnerable branches and patched branches. They are safe, synthetic teaching models: they show the contract and a regression, not a completed security assessment of third-party systems.
Try the related labs and articles
Primary references
These vendor and standards references describe actual platform/API behavior. The interactive labs linked above use synthetic evidence and must not be mistaken for production validation.
Public conversation / GitHub Discussions
Discuss this article
Public comments are being set up. There is no live comment feed yet, and this page will not load a third-party widget until the discussion repository is configured.
Send a private correction or question →