DEV Community

JerichoRhodes5847
JerichoRhodes5847

Posted on

Postgres API Key Controls: Choose Rotation or Revocation by Downtime Tolerance

A marketplace credential should be revoked immediately when continued access can create unacceptable spend; it should be rotated with a measured overlap when availability matters more than an unconfirmed exposure. That is the governing trade-off. Downtime tolerance determines the first action, but auditability determines whether the action can be trusted. If nobody can prove which workload used which credential, which spending boundary applied, and when the old credential stopped authorizing requests, a nominally successful rotation has preserved the incident's hardest question.

TL;DR: use revocation as a fail-closed containment control for confirmed compromise or a workload that has reached its spending cap. Use rotation as a planned replacement protocol: issue a distinct credential, deploy it, observe adoption, then revoke the predecessor. For uncertain incidents, prepare both paths and set a short, explicit decision deadline. Never treat expiration, deletion from a secret store, or deployment of a new value as proof that the old value can no longer authorize a purchase.

Should you choose API key rotation or revocation when downtime is acceptable?

Revocation and rotation are related operations with different postconditions. Revocation changes authorization now: requests presenting the old credential must fail after the authorization system applies the change. Rotation changes identity material while trying to preserve service: for some interval, both old and new credentials may remain valid. That overlap is useful during deployment and dangerous during containment.

Containment comes first.

The decision begins with the loss function. If a marketplace settlement importer can spend up to its remaining workload allowance while a leaked key stays valid, every minute of overlap preserves that authority. When the cap has been reached, or exposure is confirmed, accepting downtime means choosing a bounded authorization failure over unbounded continued access. Revoke first. Recovery follows.

If exposure is only suspected and interrupting checkout reconciliation would create a larger, immediate operational loss, overlapping rotation may be defensible. The word may matters. The old key needs a deadline, its permissions must not expand, and every use during overlap needs to be attributable. An overlap with no terminal revocation is duplication, not rotation.

This is also why a periodic rotation schedule is not an incident response plan. OWASP recommends automating secret rotation and ensuring that revoked secrets cannot be reused, while also warning that rotation can create availability problems when the consuming application and secret change are not coordinated. A calendar addresses credential age. It does not answer whether an attacker currently holds a valid credential.

Model authority before moving secrets

The cleanest design gives each marketplace workload its own credential and its own spend boundary. Do not share one account-wide key among catalog sync, seller payouts, and promotion bidding, then hope log labels reconstruct authority afterward. Shared credentials turn a narrow incident into an account-wide decision because revocation disables unrelated jobs and attribution collapses to the shared principal.

A credential record needs more than a hash and an active flag. At minimum, retain a stable credential identifier, workload identity, allowed actions, spending-policy identifier, creation time, activation time, expiration if used, revocation time, and predecessor identifier. Store the presented secret as a one-way verifier or keyed digest appropriate to the system; logs should carry the stable identifier, never the secret. Secret values belong in a secrets-management system, not source code or ordinary application logs, consistent with OWASP guidance.

The authorization path should evaluate credential state and the workload's spending policy in one decision. A simplified Python model makes the ordering visible:

from dataclasses import dataclass
from datetime import datetime, timezone
from decimal import Decimal
from enum import Enum


class KeyState(str, Enum):
    PENDING = "pending"
    ACTIVE = "active"
    REVOKED = "revoked"


@dataclass(frozen=True)
class Credential:
    key_id: str
    workload_id: str
    policy_id: str
    state: KeyState
    expires_at: datetime | None


def authorize_purchase(
    credential: Credential,
    amount: Decimal,
    remaining_allowance: Decimal,
    now: datetime | None = None,
) -> tuple[bool, str]:
    checked_at = now or datetime.now(timezone.utc)
    if credential.state is not KeyState.ACTIVE:
        return False, "credential_not_active"
    if credential.expires_at is not None and checked_at >= credential.expires_at:
        return False, "credential_expired"
    if amount <= Decimal("0") or amount > remaining_allowance:
        return False, "spend_boundary_exceeded"
    return True, "authorized"
Enter fullscreen mode Exit fullscreen mode

Production code must make the allowance update atomic with the authorized business operation, normally through a database transaction or an equivalent conditional write. A separate read followed by a later debit admits races: two requests can each observe enough remaining allowance and jointly exceed it. The credential decision should emit an immutable audit event containing the key ID, workload ID, policy version, decision, reason, request correlation ID, and timestamp. It should not emit the key.

Durability deserves skepticism here. An audit row acknowledged in the same transactional system as the spend reservation provides a stronger account of the decision than a best-effort log message sent afterward. Exporting those rows to immutable or retention-locked storage can protect long-term evidence, but the export is a second layer; it cannot repair an authorization decision that was never recorded at commit time.

Rotation and revocation have different proof obligations

The useful comparison is not which operation sounds safer. It is what an operator can prove after invoking it.

Control Availability posture Required proof Principal failure mode
Immediate revocation Fail closed; the workload may stop Old credential is rejected on every authorization path Stale caches or replicas continue to accept it
Overlapping rotation Preserve service during deployment New credential is in use, then old credential is rejected The old credential remains valid after the deadline
Expiration Deferred fail closed at a predetermined time All verifiers agree on time and expiry semantics Expiry is mistaken for immediate containment

Revocation is only as fast as the slowest enforcement point. If gateways cache credential state, document the maximum cache lifetime and provide an invalidation mechanism whose result is observable. If authorization reads from replicated Postgres state, replication lag changes the containment interval; the write being committed on the primary is insufficient evidence that every reader rejects the key. A test should present the old credential to each distinct enforcement path until all return the expected denial, and the evidence should record which path was checked.

Rotation has a longer proof chain. First create a new credential in a pending state. Deliver it through the secret distribution channel. Deploy the consumer, activate the new credential, and observe successful requests attributed to its new identifier. Only then revoke the predecessor. Rollback means returning traffic to a still-valid predecessor before the deadline, not quietly extending dual validity because nobody knows which key is live.

Prove the cutoff.

One subtle trap is retry behavior. A queued marketplace request signed with, or carrying, an old credential may execute after revocation. Retrying it with the new credential can be correct only if the business operation has an idempotency key and the spend reservation has not already committed. Credential replacement must not turn an uncertain purchase into a duplicate purchase.

Build an incident path that can switch modes

An incident often begins with weak evidence: a key appeared in an unexpected process, access logs show a new network, or a repository scanner found a string that might be inactive. The response should not pretend certainty. Classify the maximum remaining authority first: workload, actions, spend allowance, data scope, and enforcement paths. Then choose one of two explicit modes.

For confirmed exposure, cap exhaustion, or unexplained spending, revoke and disable retries that could amplify the event. Preserve the credential metadata and audit records; deleting the database row can destroy the link between historical requests and the principal that made them. Issue a replacement only after the workload's authority and queued operations have been reviewed.

For suspicion with low immediate impact and strict uptime requirements, begin an overlapping rotation, shorten the old credential's deadline, and alert on any old-key use. A request using the predecessor after the deployment is a signal, not harmless background noise. It can identify a missed replica, a forgotten worker, or continued unauthorized access.

Keep the decision rule compact:

  1. Revoke now when continued authority can cross the workload's acceptable spend or data boundary.
  2. Rotate with overlap when the exposure is unconfirmed, downtime is unacceptable, and old-key use is observable by credential ID.
  3. Escalate from rotation to revocation when the deadline passes, unexplained use continues, or the spend boundary changes.

Short deadlines beat vague urgency.

Test the control, not the dashboard

A green administrative status is not an end-to-end assertion. Exercise the same interfaces a workload uses. Before release, test an active key below the allowance, a request that would exceed it, a revoked key, an expired key, concurrent requests near the boundary, and a retry after an ambiguous response. During a rotation drill, hold one worker on the old value deliberately; verify that telemetry identifies it and that revocation ends its access without erasing the audit trail.

Measure timestamps that describe the mechanism: revocation requested, state committed, cache invalidated, last accepted old-key request, and first rejected old-key request. The interval between request and last acceptance is the effective containment time. Do not promise a universal number unless the whole enforcement topology has been tested under degraded conditions, including delayed workers and lagging replicas.

Operational access needs its own audit boundary. The person or automation allowed to create, reveal, rotate, or revoke credentials should be distinguishable from the marketplace workload using them. Record actor, action, target credential ID, reason, approval reference when required, and outcome. Restrict secret-reading access separately from metadata-reading access; an auditor usually needs lifecycle evidence, not the raw credential.

Roll out without weakening the boundary

Start by inventorying credentials and mapping each one to a workload owner and spending policy. Split shared keys before introducing automated rotation, because automation applied to an account-wide credential merely makes a large blast radius move faster. Next, add stable key IDs and decision events, then enforce revocation consistently across caches, replicas, workers, and gateways.

Run a revoke-first drill and an overlap drill. Confirm that the former stops authorization and that the latter ends with the predecessor rejected everywhere. Finally, automate issuance and deployment while keeping the terminal revocation and its evidence explicit. The durable outcome is not frequent key churn. It is the ability to bound marketplace authority, stop it on demand, and reconstruct who changed access and what happened afterward.

Sources

Top comments (0)