A leaked game-server API key can be revoked quickly, yet the billing dispute remains: which requests belonged to that key before revocation, and which came from another credential under the same account? TL;DR: Treat the credential inventory, including ownership, lifecycle and immutable usage references, as the account's effective security boundary. An account-level total cannot tell an investigator which principal produced a charge.
Why is the API credential inventory the real account security boundary?
An account is an administrative grouping. A credential is what a caller actually presents. If a match-server worker, a tournament scheduler and a developer's test job all share an account, an account identifier alone collapses three distinct actors into one billing bucket. Revoking one key then answers only whether future presentations of that key should be accepted; it cannot retroactively distinguish its historical traffic from the other two workloads. This matters when request usage becomes a billable event, because inaccurate attribution can survive even after access has been contained. The practical failure is easy to miss during an incident: a dashboard showing a clean account total can still leave finance unable to isolate disputed usage, while the security team believes the incident is closed because the exposed secret was disabled.
Containment is not reconciliation.
The inventory must answer, for each active or retired credential, who owns it, which workload uses it, what scope it has, when it was created and disabled, and which stable identifier appears in request and billing records. Store a non-secret credential identifier separately from the secret material. OWASP's Secrets Management Cheat Sheet describes inventory, rotation and auditing as parts of secrets management; none of those activities requires putting raw keys in logs.
A drill starts by identifying the suspected credential ID and the deployment that held it, setting a containment time, and querying usage on both sides of that time. Keep the time boundary explicit. A request admitted just before revocation can finish afterward, so its admission time and later settlement time should not be treated as interchangeable. Likewise, a clock skew between the verifier and the ledger writer can make a naive timestamp filter misleading: reconcile by stable request ID and retain both timestamps when their semantics differ.
Deriving an attribution record from the request path
Authentication resolves the presented secret to a stable credential ID and its authorized principal. The request path carries that ID into an append-only usage record, alongside a request ID, account ID, workload label, admission timestamp and metered units. The billing path reads usage records rather than reconstructing identity from a current credential table; deletion or rotation must not rewrite historical ownership. Audit events for creation, scope change and revocation need an actor and time as well, or a reviewer cannot establish who changed the boundary during the incident.
Consider illustrative drill data: credential match-prod-04 admits 41 metered requests before containment while event-scheduler-02 admits 7. These are example values, not measured production behavior. A single account total of 48 obscures the distinction. An attribution query grouped by credential ID preserves it, provided that retries do not count the same admitted operation twice.
The following Go sketch shows the boundary between authenticated usage and the billing ledger. It omits authentication and storage adapters: the important contract is that a retry of the same request produces the same event key, while a different credential cannot silently claim that event.
type UsageEvent struct {
RequestID string
AccountID string
CredentialID string
Units int64
}
type UsageStore interface {
// InsertOnce atomically rejects duplicate request IDs and identity conflicts.
InsertOnce(ctx context.Context, event UsageEvent) error
}
func RecordUsage(ctx context.Context, store UsageStore, event UsageEvent) error {
if event.RequestID == "" || event.AccountID == "" || event.CredentialID == "" || event.Units < 0 {
return errors.New("invalid usage event")
}
return store.InsertOnce(ctx, event)
}
The store must enforce uniqueness and compare the identity fields on collision. A duplicate with identical fields is a retry; a duplicate request ID with a different credential is an integrity alarm, not an excuse to overwrite the row. This is an exactly-once accounting objective built from idempotent writes and reconciliation, not a claim that networks deliver messages exactly once. A production event also needs a documented meter definition and a timestamp recorded by the admitting service.
What evidence survives revocation?
Revocation stops subsequent authorization only if every verifier consults current credential state within its documented cache window. Independently, the audit trail must retain the non-secret credential ID and lifecycle events so an investigator can join usage, deployment ownership and billing entries after the secret itself is retired. Restrict access to those records, set retention according to applicable obligations, and avoid logging raw secrets or sensitive request bodies. An audit trail is evidence, not permission to retain everything indefinitely.
In a controlled test environment, use a known test credential in one workload, generate a few labeled requests, record the simulated exposure time, revoke it, and verify both denial of later admissions and continued attribution of earlier admitted usage. Reconcile usage-event IDs against ledger-entry IDs and investigate missing, duplicated or conflicting records before calling the exercise complete. Production secrets should not be intentionally leaked for rehearsal.
Choosing the boundary and rolling it out
An account-only design has fewer identifiers to propagate, but it cannot isolate charges between workloads sharing that account. Per-workload credential IDs add lifecycle and audit work, yet permit incident containment and billing reconciliation without guessing from IP addresses or current deployment names. The limitation of per-credential attribution is that a credential shared by multiple processes still identifies only the shared key, not the individual process. Separate credentials further where operational owners or revocation schedules differ. Perfect attribution for activity predating per-credential records is impossible: the missing join key cannot be inferred with certainty.
That historical gap stays visible in the report.
Roll out in three passes: inventory existing credentials and owners; propagate stable IDs through authentication, usage and ledger writes with idempotent event keys; then rehearse revocation and reconcile the resulting billing slice. Alert on usage after a credential's effective revocation time, allowing for the stated verifier cache and in-flight request policy. Record those limits in the drill report. The boundary is credible when a reviewer can reproduce which credential admitted each billable operation and explain any exception without consulting the secret itself.
Top comments (0)