Auditing and abuse handling
Place authentication, object authorization and resource limits at explicit application boundaries.
THE IDEA
Auditing and abuse handling
Record who did what to which resource and with what outcome: identity, action, resource, outcome. Never record credentials that can be replayed. Plan how you will revoke access (a revoked token must fail at once, 401) and how you will investigate, both for honest mistakes and for deliberate abuse: filter the trail by identity or by resource.
Predict first. Should an audit log contain raw reusable API secrets?
Chapter context
A distributed application must identify callers and control which resources and effects they may access. Tenant boundaries and resource budgets must be enforced by trusted components rather than inferred from client-supplied identifiers.
TRY IT YOURSELF
Part 1. Do it hands-on
Design an audit record by hand, then write redacting events, revocation and an investigation filter.
- To do: An audit record should say who (identity), what (action), on what (resource) and what happened (outcome). How many fields is that?
- To do: A token is revoked. A request with it then arrives. Status code?
- To do: Events: alice read, bob delete, alice delete, alice export. Events by alice during an investigation?
- To do: Should an audit log contain raw reusable API secrets? Type yes or no
Part 2. Do it in code
- audit_event(identity, action, resource, outcome, secrets=()): a dict with exactly the keys identity, action, resource, outcome. Any string in `secrets` appearing in any value is replaced by "[redacted]".
- contains_secret(event, secrets): True if any value still contains one of the secret strings.
- class TokenStore: issue(user) → a new token string "tok1", "tok2", …; verify(token) → the user or None; revoke(token) → True if it existed and was active; revoked tokens never verify again.
- handle(store, token) → 200 if the token verifies, else 401.
- events_by(events, identity): the events whose identity matches, in order.
- actions_on(events, resource): the sorted unique actions performed on a resource.
Stuck, or something not working? You can come back to it any time.
CHECK YOUR UNDERSTANDING
Should an audit log contain raw reusable API secrets?
Chapter reference: math, worked example & code
This section belongs to the whole chapter, so it is the same on every lesson in it and may cover a different lesson's topic. The code for the experiment you just ran is inside the experiment itself.
Math and notation
For a token bucket with capacity C, refill rate r, previous tokens b and elapsed time dt:
Accept cost k only when , then subtract k atomically. Clock behavior, concurrency and shared enforcement are additional implementation requirements.
Worked example
A bucket holds at most 10 tokens and refills 2/s. An 8-token request at time zero leaves 2. Two seconds later it has 6. A request costing 7 is denied and leaves 6; a request costing 6 is accepted and leaves zero. Rejected operations should not accidentally create negative credit under this contract.
Reference code
The following example uses the Python standard library and does not call a remote service.
capacity,rate,tokens=10,2,10
tokens-=8
assert tokens == 2
tokens=min(capacity,tokens+rate*2)
assert tokens == 6
assert tokens < 7
assert tokens >= 6
tokens-=6
assert tokens == 0
Python examples are provided for study and local execution; this page does not execute Python.
Common mistakes
Authentication is not object-level authorization. Cache and worker paths can cross tenant boundaries too. A global rate limit requires a stated distributed enforcement contract.