Object Storage & Blob Storage • Presigned URLs & Access ControlMedium⏱️ ~3 min
Security Model, Scoping, and Blast Radius Reduction
Presigned URLs are bearer tokens: possession equals authorization until expiration. This security model demands careful scoping and defense in depth because unlike traditional authentication, you cannot individually revoke a presigned URL once issued. Your threat model must account for URL leakage through logs, crash reports, analytics beacons, Referer headers, or even screenshots.
Tight scoping is your primary defense. Every presigned URL should constrain the HTTP method (PUT, GET, not wildcard), the exact object key or a narrow prefix, expiration time, and where supported, additional constraints like Content-Length bounds, required headers, or even source IP ranges. For uploads, never sign URLs with wildcard object keys; this creates confused deputy vulnerabilities where an attacker can overwrite arbitrary keys in your bucket. Instead, enforce deterministic, tenant prefixed key namespaces: tenant-id/user-id/upload-type/high-entropy-uuid. Your signing service must validate that the requested key matches the authenticated user's allowed prefix before generating the signature.
Time To Live (TTL) strategy directly trades convenience against exposure risk. For writes, 1 to 5 minutes balances retry windows for flaky mobile networks against exposure if URLs leak. For reads, fetch presigned URLs just in time (seconds to 2 minutes TTL) rather than embedding them in long lived page loads. When using Content Delivery Networks (CDNs) for downloads at scale, prefer signed cookies or tokens at the CDN layer with session semantics rather than per object presigned URLs; this enables session revocation and reduces URL generation load.
Revocation remains the hard problem. Standard presigned URLs cannot be individually canceled. Your options are coarse: rotate the signing key (invalidates all URLs signed with that key), change bucket or object policies to deny access (requires careful scoping to avoid breaking legitimate traffic), or delete the object itself. Azure Shared Access Signature (SAS) tokens tied to stored access policies offer better revocation: you can disable the policy to immediately invalidate all SAS tokens referencing it, a capability Amazon S3 and Google Cloud Storage lack for standard presigned URLs. For use cases requiring immediate revocation (user account deletion, detected abuse), consider token validating proxies or short lived federated credentials with fine grained policies instead of presigned URLs.
💡 Key Takeaways
•Namespace isolation with high entropy: Use keys like tenant-id/user-id/date/uuid rather than sequential or guessable names. Enforce server side that signing requests only target allowed prefixes for the authenticated user. This limits blast radius if a signing vulnerability exists.
•Time To Live (TTL) as primary revocation: Without per URL revocation, expiration is your main control. Uploads typically 1 to 5 minutes; just in time downloads 30 to 120 seconds. Avoid embedding presigned URLs in cached pages or long lived clients. Fetch fresh URLs on demand to keep TTLs tight.
•Content constraints where available: Amazon S3 allows signing with Content-Length, Content-Type, and other headers that must match at request time. Use these to prevent oversized uploads or content type abuse. Validate post upload as defense in depth since header enforcement has edge cases with browser normalization.
•Leakage vectors to mitigate: Redact presigned URL query strings from application logs and crash reports. Disable or sanitize Referer headers to third party analytics. Use HTTPS only (never HTTP) to prevent network sniffing. Consider short lived, single use upload flows that invalidate server side state after first use for highest security scenarios.
📌 Examples
Correct scoping: User uploads profile photo. Backend generates presigned PUT for users/user-456/profile/2024-01-15-abc123.jpg, expires in 2 minutes, Content-Length max 5 MB. Signing service validates user-456 matches authenticated session and prefix users/user-456/* is allowed. If URL leaks, blast radius limited to one object key for 2 minutes.
Confused deputy attack prevented: Attacker requests presigned URL for ../../../admin/config.json. Backend validates requested key against user's allowed prefix, rejects because admin/* not in user-789/uploads/* namespace. Without this check, attacker could overwrite critical objects.
Azure SAS revocation: Issue SAS tokens bound to stored access policy named user-session-policy-abc. On user logout or abuse detection, update policy to deny all operations. All SAS tokens referencing that policy immediately fail with 403, effectively providing session revocation not available in S3 or GCS standard presigned URLs.