design·lab

Security · applied

Signed URLs (S3-style)

A short-lived, tamper-proof URL that grants temporary access to one private object — without ever handing out your credentials.

✗ The problem

A user needs one private file — now what?

Three tempting fixes, three disasters:

🌐 Make the bucket public
leaks everything, forever
🔑 Share cloud credentials
browser now holds your keys
🖥️ Proxy every byte
server pays bandwidth + CPU
None of these are scoped or time-limited — each grants far more than "let this one user download this one file for five minutes."
✓ How it works

Server signs a capability, store verifies it

The server holds the secret key. It never ships the key — it ships a URL that is already proof of permission, checkable by the object store alone.

// server builds & signs the URL
const url = `/file.pdf`
  + `?exp=1699999&`
  + `sig=ab34c1f2`;
// sig = HMAC(path + exp, secretKey)

// object store verifies — no DB call
verify(path, exp, sig) {
  if (now() > exp)
    return 'expired';
  if (hmac(path, exp) !== sig)
    return 'bad signature';
  return 'allowed';
}
🧑 Client
asks server for access
🖥️ Server
holds secret key, signs URL
↓ signed URL
🧑 Client
uses URL directly
🗄️ Object Store
checks sig + expiry itself
✓ See it live

Generate, access, expire, tamper

A logical clock stands in for real time — advance it yourself. No crypto libraries; the signature is a small deterministic check you can reason about.

no URL yet — click Generate

Clock: 1000  |  Expires:

No attempt yet
✓ Takeaway

Capability, not identity

  • Capability-based: possessing the URL is the access — no per-request identity check.
  • Time-limited: the expiry is baked into the signature; nothing to revoke, it just stops working.
  • Scoped: one path, one permission, one window — never the whole bucket.
  • Split roles: the server signs (holds the secret), the object store verifies (no secret needed at request time).
  • Keep expiries short. This exact pattern powers S3, GCS, and CDN "private" content.
🎯 Relates: a signed URL is a capability token — same idea as an OAuth2 access token — applying least privilege & statelessness to object access.