API Authorization, State Machines and Distributed Abuse Cases — DedSec Project
API Authorization, State Machines and Distributed Abuse Cases Purpose: Study API security at the level where serious logic bugs occur: object identity, function authority, workflow state…
Purpose: Study API security at the level where serious logic bugs occur: object identity, function authority, workflow state, concurrency, tenancy, delegation, and distributed consistency.
Learning objectives
- Model object-level and function-level authorization separately.
- Build an authorization matrix from subjects, actions, resources, and context.
- Analyze state-machine and race-condition failures.
- Understand tenancy boundaries, delegation, replay, idempotency, and event-driven authorization.
- Create negative tests that prove a policy rather than merely exercise happy paths.
Authentication is only the first gate
An API can strongly authenticate a caller and still be insecure if it authorizes the wrong object or action. The decision should conceptually answer:
May subject S perform action A on resource R under context C?
Context can include tenant, ownership, group membership, transaction state, device posture, time, delegation, approval state, and policy version. If code checks only “is the caller logged in?”, the policy is incomplete.
Object-level authorization
Object-level authorization determines whether the caller can access a particular record, document, account, project, invoice, message, or other object. Identifiers should be treated as locators, not proof of authority.
A strong test suite creates at least two users and multiple objects in different ownership/tenant states, then verifies read/update/delete behavior for every relevant combination.
Function-level authorization
Function-level authorization determines whether a caller can invoke an administrative or privileged operation at all. Routes such as user management, billing adjustment, export, key rotation, configuration, moderation, or support impersonation need explicit policy independent of whether their URLs are hidden from ordinary clients.
Backend routing should not infer privilege from UI visibility or client-supplied role fields.
Property-level authorization
A caller may be authorized to update an object but not every field on it. Mass-assignment and over-posting bugs happen when a framework binds all supplied properties to a model, including fields the caller should not control.
Define writable fields by operation and caller class. Treat server-owned fields—role, tenant ID, approval state, balance, ownership, verification state—as protected unless a specific workflow grants change authority.
Authorization matrix
Before writing tests, build a matrix:
| Subject | Action | Resource relationship | Expected |
|---|---|---|---|
| owner | read | own object | allow |
| owner | update | own object | allow |
| user | read | another user's object | deny |
| tenant admin | read | same-tenant object | policy-dependent |
| tenant admin | read | other tenant | deny |
| service A | write | service B namespace | deny unless delegated |
This makes missing cases obvious.
Multi-tenancy
Tenant isolation is a high-value invariant. Tenant context should come from trusted identity/session state, not a request field the caller can arbitrarily choose. Database queries, cache keys, object storage paths, search indexes, background jobs, exports, and logs all need tenant-aware design.
A common failure is enforcing tenant filters in the web handler but forgetting them in a background worker or secondary query path.
State-machine authorization
An action may be legal only in one state. An order can be canceled before shipment but not after. A recovery token can be used before expiry but only once. An invitation can be accepted by the intended recipient while pending, not after revocation.
Test transitions, not just endpoints. Attempt skip, repeat, reverse, and concurrent execution in your local lab.
Replay and idempotency
Distributed systems retry. A network timeout does not reveal whether the server processed a request. Idempotency keys or transaction identifiers can prevent duplicate effects for operations such as payments or provisioning.
Security-sensitive one-time actions should bind tokens to purpose, subject, audience, object, and lifetime and mark successful consumption atomically.
Race conditions and TOCTOU
Two requests can both pass a check before either commits the change. This can violate quotas, balances, one-time token semantics, role changes, or ownership rules.
Mitigations include transactions, row/version locks, unique constraints, atomic compare-and-swap, serializable operations where appropriate, and designing invariants into the data layer rather than only application code.
Event-driven systems
Authorization becomes harder when a frontend writes an event and a worker later performs the action. The worker must know which principal authorized the request, what policy applied, whether authority is still valid, and whether the event can be replayed.
Do not blindly trust fields inside a message merely because it came from an internal queue. Internal components can be buggy or compromised.
Delegation
Delegation allows one principal to act on behalf of another under bounded authority. OAuth scopes, service-to-service tokens, signed job capabilities, and support workflows are examples.
Good delegation is explicit about subject, actor, audience, scope, lifetime, and revocation. Logging should preserve both the human/original subject and the acting service when possible.
Confused deputy in APIs
A privileged service becomes a confused deputy when an unprivileged caller can cause it to use its authority on a caller-chosen target. Server-side URL fetchers, file converters, cloud management helpers, and integration brokers are common shapes.
Prevent this by binding target choices to policy and minimizing the service's own ambient authority.
Pagination, filters, and exports
Authorization must survive alternative data access paths. Bulk exports, search endpoints, GraphQL resolvers, analytics views, pagination cursors, filters, and autocomplete can leak objects even when the main record endpoint is correct.
Treat every representation as a separate policy enforcement point unless authorization is centralized below them.
GraphQL considerations
GraphQL exposes a flexible query graph. Security review should cover resolver authorization, field-level policy, query complexity/depth, batching, introspection policy, object identifiers, subscriptions, and data-loader caching across users/tenants.
The schema's flexibility does not remove the need for server-side per-object decisions.
Rate controls and abuse economics
Some API weaknesses are valid functionality used at harmful scale: enumeration, scraping, expensive search, bulk invitation, or high-cost AI operations. Combine authorization with quotas, anomaly detection, bounded pagination, per-identity limits, and business rules.
Rate limiting is not a substitute for access control; it reduces abuse volume.
Safe local authorization lab
Implement an in-memory API model with two tenants, two users per tenant, objects, and roles. You do not need a framework; Python functions are enough. Define authorize(subject, action, resource, context) and generate all subject/action/resource combinations.
Add a transfer workflow with states pending → approved → completed and assert that skipping or repeating a state fails. Then simulate two concurrent “use once” operations using threads and fix the race with an atomic/locked operation.
Guided study workflow
Before you begin
Complete Modules 21, 22, 39, 40, 41, 61, 68, and 69.
Practice task
Build an authorization matrix containing at least 30 cases and automate it. Include two tenants, property-level restrictions, and one stateful workflow.
Evidence to keep
Policy matrix, test output, state diagram, one intentionally introduced local failure, fix, and regression test.
Common mistakes to avoid
- treating object IDs as authorization;
- trusting tenant IDs from request bodies;
- checking policy only in the UI;
- ignoring workers/export/search paths;
- testing only successful workflows.
Mastery check
Explain object-, function-, and property-level authorization, then show how concurrency can violate a correct-looking check.
Continue with
Modules 72, 76, 84, and 85.