CDN Access Control
Security analysis of CDN access control for the Item Image Upload feature. This document is intended for review by security auditors. It describes the threat model, the chosen solution (CloudFront signed cookies), the security properties it provides, residual risks, and operational procedures.
Problem Statement
Section titled “Problem Statement”The Arda platform stores product images in an S3 bucket and serves them via CloudFront CDN. These images are sensitive — customers may upload photographs of internal R&D prototypes, proprietary manufacturing processes, or confidential product designs. The CDN access control mechanism must satisfy two requirements:
- System-generated URLs only. Only URLs generated by the Arda system should be resolvable. An attacker who guesses or constructs a URL should not be able to access image content.
- Tenant isolation. A user operating in Tenant A’s context must not be able to access images belonging to Tenant B, even if they know or obtain the URL.
An additional constraint is that the active tenant of a user session may change without re-authentication (multi-tenant users switching between organizations). The access control mechanism must handle this transition securely.
Design decisions: TD-11, TD-12
Solution: CloudFront Signed Cookies
Section titled “Solution: CloudFront Signed Cookies”How It Works
Section titled “How It Works”CloudFront signed cookies use RSA-based cryptographic signatures to restrict access to CDN content. The mechanism has three components:
-
Key pair: An RSA key pair is generated and managed by the infrastructure team. The public key is registered with CloudFront as a trusted key. The private key is stored securely (AWS Secrets Manager) and used only by the BFF to sign cookies.
-
Cookie issuance: When a user authenticates, the BFF generates three
Set-Cookieheaders containing a custom policy, its RSA signature, and the key pair ID. The custom policy specifies:- Resource:
https://<partition>.<infra>.assets.arda.cards/<tenantId>/*— restricts access to the active tenant’s image namespace. - DateLessThan: Current time + TTL (default 30 minutes) — restricts access temporally.
- Resource:
-
Cookie validation: On every CDN request, CloudFront validates the cookie signature against the trusted key group and checks that the requested URL matches the Resource pattern and that the current time is before DateLessThan. Invalid or missing cookies result in 403 Forbidden.
Cookie Attributes
Section titled “Cookie Attributes”| Attribute | Value | Security Rationale |
|---|---|---|
Domain | .arda.cards | Shared registrable domain allows <img> requests from app.arda.cards to carry cookies to assets.arda.cards without cross-site restrictions. Accepted trade-off: This sends cookies to all *.arda.cards subdomains (including api.arda.cards). Scoping to <partition>.<infra>.assets.arda.cards would be more precise but browsers set the cookie domain to the full FQDN, preventing <img> tags on app.arda.cards from sending cookies to assets.arda.cards. The broad domain is required for cross-subdomain cookie delivery. The cookies are HttpOnly and carry no application secrets — the overhead is three cookie headers (~1 KB) on non-CDN requests. |
Path | / | Broad path — the Resource field in the signed policy provides the actual path restriction. |
Secure | Yes | Cookie transmitted only over HTTPS. Prevents interception over unencrypted connections. |
HttpOnly | Yes | Cookie inaccessible to JavaScript. Mitigates XSS-based cookie theft. |
SameSite | Lax | Sufficient because app.arda.cards and assets.arda.cards are same-site (same registrable domain). <img> tags in same-site context send cookies automatically. Prevents cross-site request forgery from external domains. |
Cookie Lifecycle
Section titled “Cookie Lifecycle”Security Properties
Section titled “Security Properties”What This Solution Prevents
Section titled “What This Solution Prevents”| Threat | Prevention Mechanism |
|---|---|
| Unauthenticated access | CloudFront rejects requests without valid signed cookies (403). No anonymous access to any image. |
| Cross-tenant access | Cookie policy Resource field restricts to /<tenantId>/*. Tenant A’s cookie cannot access Tenant B’s paths. CloudFront validates the requested URL against the Resource pattern. |
| URL guessing | Even with a correct URL, the request fails without a valid signed cookie. UUID-based keys (122 bits of entropy) provide additional protection. |
| Cookie forgery | Cookies are RSA-signed. Without the private key, an attacker cannot forge a valid signature. The private key never leaves Secrets Manager / BFF runtime. |
| Cookie theft via XSS | HttpOnly flag prevents JavaScript access to cookies. An XSS attack cannot read the cookie values. |
| Cookie theft via network interception | Secure flag ensures cookies are only sent over HTTPS. |
| Cross-site cookie exfiltration | SameSite=Lax prevents cookies from being sent with cross-site requests initiated by external sites. |
| Tenant escalation via API | The BFF cdn-cookies endpoint extracts the tenant ID from the authenticated session, not from a client parameter. A compromised SPA cannot request cookies for a different tenant. |
What This Solution Does NOT Prevent
Section titled “What This Solution Does NOT Prevent”| Threat | Residual Risk | Mitigation |
|---|---|---|
| Cookie exfiltration from a compromised browser | If an attacker has full control of the user’s browser (e.g., malicious extension, physical access), they can extract cookies from the browser’s cookie store. | Short TTL (30 min) limits the window. HttpOnly prevents JS-based extraction. Browser-level compromise is outside the application’s trust boundary. |
| Insider threat (authenticated user sharing images) | A user with valid access can screenshot, download, or share images they are authorized to view. | Out of scope for CDN access control. This is a data loss prevention (DLP) concern addressed by organizational policy, watermarking, or DRM — none of which are in scope. |
| Signing key compromise | If the CloudFront private key is leaked, an attacker can forge valid cookies for any tenant. | Key is stored in Secrets Manager with restricted access. Key rotation procedure (OAM-S-012) enables rapid revocation. CloudFront trusted key groups support multiple active keys for zero-downtime rotation. |
| Tenant switch race condition | During the brief window between tenant switch and cookie refresh (~200-500ms), the SPA holds cookies for the old tenant. No security impact — the old cookies only grant access to the tenant the user already had access to. The SPA shows loading state during the transition. | Acceptable. No data leakage occurs. |
Alternatives Considered
Section titled “Alternatives Considered”| Option | Description | Why Not Chosen |
|---|---|---|
| Public CDN (no auth) | Serve all images without authentication. Rely on UUID key entropy for protection. | Rejected — product images are sensitive. URL disclosure (logs, shared links, browser history) would expose content. |
| CloudFront signed URLs (per-image) | Generate a signed URL for each image on every entity query. | Higher overhead: every entity read must transform URLs. Defeats cache sharing between users (unique URLs). More complex client-side code. Signed cookies provide equivalent security with better performance. |
| Lambda@Edge JWT validation | Validate the user’s JWT on every CDN request via Lambda@Edge. | CloudFront Functions cannot verify RS256/ES256 JWTs. Lambda@Edge adds latency (~50ms) and requires us-east-1 deployment. Cookie validation is sub-ms at the CloudFront edge with no Lambda involvement. |
| Presigned GET URLs (bypass CDN) | Generate presigned S3 GET URLs via the Backend for every image load. | Eliminates CDN entirely. Every image load hits the Backend. Defeats the purpose of CDN-based delivery (TD-06). |
| CloudFront Functions with HMAC | Use a CloudFront Function to validate an HMAC-signed token. | Requires custom token protocol. Less standard than CloudFront’s native signed cookie support. Same security properties but more implementation effort and a larger attack surface. |
Cookie TTL Considerations
Section titled “Cookie TTL Considerations”The cookie TTL balances two competing concerns:
| Concern | Favors | Impact |
|---|---|---|
| Security | Shorter TTL | Reduces breach window from exfiltrated cookies. A 30-minute cookie is useless to an attacker after 30 minutes. |
| Performance | Longer TTL | Reduces BFF load from cookie refresh requests. At 30 min TTL with proactive refresh at 15 min, each user generates ~4 cookie requests/hour. |
| UX on tenant switch | Shorter TTL is neutral | Tenant switch always requires an immediate cookie refresh regardless of TTL. |
Recommended default: 30 minutes. This aligns with typical session token refresh intervals in the existing system. The TTL is configurable (OAM-C-002 pattern) to allow tightening for high-security deployments.
Refresh strategy: The SPA refreshes proactively at ~50% of TTL (15 minutes for a 30-minute TTL). This ensures cookies do not expire during normal use. If the proactive refresh fails (BFF unavailable), the SPA falls back to 403-triggered refresh on the next image load.
Tenant Switch Security
Section titled “Tenant Switch Security”Multi-tenant users can switch the active tenant without re-authenticating. This creates a transition where CDN cookies must be re-scoped:
| Phase | State | Security Property |
|---|---|---|
| Before switch | Cookies scoped to Tenant A. | User can view Tenant A’s images. Cannot view Tenant B’s. |
| During switch (~200-500ms) | SPA requests new cookies. Old cookies still present in browser. | Old cookies still valid for Tenant A (user had legitimate access). Images for Tenant B return 403 (old cookies don’t match). SPA shows loading state. |
| After switch | New cookies scoped to Tenant B. Old cookies overwritten (same cookie names). | User can view Tenant B’s images. Cannot view Tenant A’s (cookies replaced). |
Key property: At no point during the transition does the user gain access to a tenant they are not authorized for. The BFF cdn-cookies endpoint extracts the tenant ID from the authenticated session context — it is impossible for the SPA to request cookies for a tenant the user is not authorized to operate in.
Operational Procedures
Section titled “Operational Procedures”Key Rotation
Section titled “Key Rotation”Frequency: At minimum annually, or immediately if compromise is suspected.
Procedure (zero-downtime):
- Generate a new RSA key pair.
- Store the new private key in Secrets Manager under a new version.
- Upload the new public key to CloudFront and add it to the trusted key group (both old and new keys are now active).
- Update the BFF configuration to use the new private key for signing.
- Wait for all existing cookies to expire (max 1 TTL period = 30 minutes).
- Remove the old public key from the trusted key group.
- Delete the old private key from Secrets Manager.
During steps 3–5, cookies signed with either key are valid. There is no downtime or user impact.
Emergency Revocation
Section titled “Emergency Revocation”If the signing key is known to be compromised:
- Immediately remove the compromised public key from the CloudFront trusted key group.
- All existing cookies signed with the compromised key become invalid instantly.
- Users will see 403 on all image loads until they receive new cookies.
- Generate and deploy a new key pair (steps 1–4 of rotation procedure).
- Users’ next cookie refresh (proactive or 403-triggered) will use the new key.
Impact: Brief image outage (~15-30 seconds) while SPA detects 403 and requests new cookies. No data leakage after key removal.
Monitoring
Section titled “Monitoring”| Metric | Source | Alert Condition |
|---|---|---|
| CloudFront 403 rate | CloudFront metrics | Spike indicates expired/missing cookies (normal during key rotation) or attack. Sustained elevation indicates misconfiguration. |
| BFF cdn-cookies endpoint call rate | BFF application logs | Rate significantly exceeding expected (users × refresh_frequency) may indicate a retry storm or misconfigured TTL. |
| Cookie signing latency | BFF application logs | P95 > 100ms indicates key loading or cryptographic performance issues. |
Summary for Security Auditors
Section titled “Summary for Security Auditors”| Property | Status |
|---|---|
| Data classification | Sensitive — customer R&D prototype images |
| Authentication mechanism | CloudFront signed cookies (RSA-SHA1 custom policy) |
| Authorization scope | Per-tenant path prefix (/<tenantId>/*) |
| Cookie security | Secure; HttpOnly; SameSite=Lax |
| Cookie TTL | 30 minutes (configurable, shorter for high-security) |
| Tenant switch | Immediate cookie re-issuance; no cross-tenant leakage during transition |
| Key management | RSA key pair in Secrets Manager; zero-downtime rotation via trusted key groups |
| Emergency revocation | Key removal from trusted group invalidates all cookies instantly |
| Residual risks | Browser-level compromise, insider threat, signing key compromise (see table above) |
| CDN caching | Preserved — cookies are not part of the cache key |
| Related documents | requirements.md (FR-036–039, NFR-017–020), oam-requirements.md (OAM-S-011–015), bff-specification.md (BFF-FR-008–010), aws-specification.md (CloudFront configuration) |
Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved