Tag: Security

Access control, signing, origin protection, and abuse prevention.

  • Choose a private image delivery pattern

    Choose a private image delivery pattern

    Short answer: Use an application proxy when every request needs application-level authorization, signed URLs when a time-bounded bearer capability is acceptable, and edge authorization when you need both access checks and CDN delivery. Some transformed derivatives may be safely public even when originals are private, but that must be an explicit policy.

    There is no single definition of a private image. A customer invoice scan, a draft campaign asset, a paid publication image, and a profile photo each have different access, sharing, revocation, and caching needs. Start with the threat model and lifecycle, not the delivery feature name.

    Pattern 1: proxy every request through the application

    The browser requests an application URL with its normal session. The application authorizes the user, retrieves or streams the image, and returns it. This provides immediate business-rule enforcement and can hide storage details completely.

    The tradeoff is load. Your application handles image bandwidth and long responses unless a protected acceleration layer is added. Poorly implemented proxies buffer entire files, tie up workers, omit range support, and become a reliability bottleneck.

    Use streaming, strict upstream limits, correct content headers, and a cache policy consistent with the data. Do not allow an arbitrary upstream URL parameter or the proxy becomes an SSRF surface. The origin protection guide applies here.

    Pattern 2: issue expiring signed URLs

    After authenticating the user, the application returns a URL containing a verifiable signature and expiry. The browser retrieves the image directly from the media service or CDN. This offloads transfer and works well when temporary bearer access matches the product.

    Anyone who obtains the URL can generally use it until expiry unless the token is bound to another property. Short lifetimes improve revocation but can break long sessions and caching. Long lifetimes improve reliability but extend exposure.

    Cloudflare documents this model for serving private images. Cloudinary describes authenticated and private delivery types in its control access documentation. Behavior and cache semantics differ, so test your provider.

    The signed image URL guide explains what to sign and where keys belong.

    Pattern 3: authorize at the edge

    An edge function or CDN authorization feature validates a cookie, token, or application-issued assertion before allowing a cached object to be returned. This can preserve low-latency delivery while applying an access decision on every request.

    The cache must not bypass authorization. Validate before cache delivery or use a product feature explicitly designed for protected caching. Avoid varying the stored image by every user identity when the bytes are shared among an authorized group; validate the user and then map to stable content identity.

    Edge authorization adds another runtime and deployment surface. Keep token verification narrow, use protected secret storage or asymmetric verification, and define failure behavior if identity services are unavailable.

    Pattern 4: keep originals private and publish selected derivatives

    A site may hold an original privately while intentionally publishing a watermarked, low-resolution, or approved crop. This works when the derivative itself is not sensitive. It does not protect the derivative merely because the source is private.

    Make publication a recorded state transition. Use distinct delivery identifiers or namespaces so a public cache cannot accidentally serve a private variant. Prevent arbitrary transformations from a public derivative back to a larger or unwatermarked result.

    How should caching be designed?

    Decide whether the response is user-specific, group-shared, or identical for everyone who passes authorization. Set browser and shared-cache directives accordingly. A private cache directive concerns shared cache storage; it does not authenticate the requester.

    Separate the authorization token from the content key where the platform safely supports it. Otherwise, unique signatures can fragment the cache. But never remove a token from cache identity if doing so also removes the access check.

    Test a warm object with a valid token, expired token, missing token, and a token for another asset. Then repeat from another account and region. Review the image cache key guide before making query exclusions.

    What about revocation and logging?

    Expiry is not immediate revocation. For urgent removal, rotate an asset version, disable its delivery ID, update the authorization policy, or purge protected caches according to provider capabilities. Document which action reaches which layers.

    Log access decisions with asset ID, policy, result, and trusted principal identifier where required. Do not log full bearer tokens. Apply retention and privacy controls to logs because image access can itself reveal sensitive behavior.

    Choose the simplest pattern that satisfies the real access model. Public content should stay simple. Sensitive content needs explicit authorization boundaries, cache tests, revocation procedures, and observability that proves the boundary is working.

  • Protect remote image origins from SSRF

    Protect remote image origins from SSRF

    Short answer: Do not let a public image URL fetch arbitrary user-supplied destinations. Prefer registered source identifiers, restrict schemes and hosts, resolve and validate addresses, recheck redirects, block private networks, isolate egress, and enforce strict response limits.

    An origin-pull image service makes server-side requests on behalf of a caller. Without controls, an attacker may target internal services, cloud metadata endpoints, localhost, administrative interfaces, or large responses. Image decoding does not make the request safe.

    Prefer source IDs over arbitrary URLs

    The safest public interface accepts an approved asset ID or a path relative to a configured origin. The service looks up the actual base URL and credentials from trusted configuration. Callers cannot choose the scheme, host, port, or network destination.

    If a business workflow must import remote URLs, place it behind authentication and an ingestion job. Fetch once, validate, store the result as a managed asset, and serve future derivatives from that controlled source. The upload pipeline describes the validation stages.

    Avoid embedding remote credentials in public URLs. Configure origin authentication on the server and scope it to a narrow path.

    What should a URL allowlist validate?

    Allow only required schemes, normally HTTPS. Parse with a maintained URL library and reject ambiguous or malformed forms. Compare normalized hostnames against an exact allowlist or a carefully defined subdomain rule. Do not use a substring check.

    Disallow unexpected ports, user-info fields, IP literals, and fragments. Normalize internationalized domains consistently. Resolve DNS and reject loopback, link-local, private, multicast, and other nonpublic address ranges unless a specifically isolated private origin is part of the design.

    OWASP’s SSRF Prevention Cheat Sheet details allowlisting, network controls, and common bypasses. Apply the checks before each connection, not only when configuration is saved.

    Why must redirects and DNS be rechecked?

    An allowed public URL can redirect to a forbidden internal destination. Either disable redirects or validate every hop with the same policy. Limit the number of hops and do not forward origin credentials across hosts.

    DNS answers can change between validation and connection. Resolve through a controlled resolver, validate all returned addresses, and ensure the connection uses an approved result. Rebinding defenses need both application validation and network egress controls.

    Cloudflare documents its source-origin allowlist for image transformations. Product protections vary, so do not assume a managed fetcher exactly matches your allowlist requirements.

    What network controls provide defense in depth?

    Run fetch and decode work in an isolated environment with no route to internal control planes or metadata services. Use outbound firewall rules or a proxy that permits only approved destinations. Separate this worker’s identity from application and infrastructure credentials.

    Network controls remain valuable if URL parsing has a bug. Application allowlists remain valuable if network configuration changes. Neither layer should be the only barrier.

    Set connection, header, body, and total timeouts. Limit response bytes before buffering, verify the detected media type, cap decompressed pixels and frames, and abort slow streams. A valid public host can still return a decompression bomb or an endless response.

    How do signatures help?

    A signature can ensure that only a trusted application constructs a remote-fetch request. It does not make the destination safe by itself. The signer must enforce the allowlist and resource policy before issuing the URL, and the image service should still enforce its own limits.

    Use the signed URL policy to cover the approved source reference and transformation. Do not sign a base URL while leaving a redirect target or nested source parameter mutable.

    What should be logged and alerted?

    Log the normalized source ID, approved host, resolved public address, redirect count, response size, content type, timing, and rejection reason. Avoid logging embedded credentials, full signed tokens, or sensitive query strings.

    Alert on denied private addresses, repeated malformed hosts, unusual ports, redirect loops, high fetch failure rates, and sudden traffic to a new approved origin. Correlate source fetch logs with transformation requests through a safe request ID.

    The image pipeline observability guide describes broader operational signals. SSRF defenses should appear in the same dashboards and incident playbooks as reliability failures because attackers often look like unusual origin errors first.

    Remote fetching is a privileged server capability. Expose a controlled asset vocabulary to normal callers, isolate the component that performs network access, and make every rejected destination visible enough to investigate.

  • When should you use signed image URLs?

    When should you use signed image URLs?

    Short answer: Sign URLs when the server must prove that a caller may access an asset or request a restricted transformation. Keep the signing key on trusted infrastructure, cover every policy-relevant field, use bounded expiry where appropriate, and separate access proof from cache identity.

    Public marketing images usually do not need per-request signatures. Private customer documents, embargoed media, paid content, and expensive transformation capabilities often do. The important question is what the signature authorizes.

    Delivery authorization or transformation authorization?

    Delivery authorization answers whether a requester may receive an asset. A token may include asset identity, expiry, and perhaps a user or policy scope. The server validates it before returning bytes.

    Transformation authorization answers whether a requested derivative is allowed. A public source might permit a few safe presets but require a signature for arbitrary overlays, very large output, or remote fetches. This protects compute and prevents unauthorized manipulation.

    The two concerns can share a signature scheme but should remain explicit. A URL that proves access to one original should not automatically authorize every possible transformation of it.

    Cloudinary documents signed delivery and transformations in its authentication signatures guide. Cloudflare documents signed URLs for Images in its private images guide. The algorithms and token fields differ, so follow the provider’s canonical implementation.

    What must the signature cover?

    Cover the asset identifier and version, normalized transformation or preset, expiry, and any policy scope that the verifier enforces. If width or crop can be changed without invalidating the signature, the token does not control those operations.

    Canonicalize before signing. Parameter order, encoding, default omission, and numeric formatting must be deterministic on both sides. Use one typed URL builder rather than reimplementing signature strings in multiple clients.

    Do not invent a cryptographic format when the provider supplies one. Use maintained libraries where available, compare signatures in constant time on systems you operate, and rotate keys through a documented process.

    Where should signing happen?

    Only on a trusted server, edge worker with protected secret storage, or another controlled backend. Never ship the signing secret in browser JavaScript, a mobile application, a public repository, or page markup.

    The browser can request a short-lived URL from your application after authentication. The application checks business authorization and returns the minimum capability required. For pages rendered on the server, it can generate signed URLs during rendering.

    Avoid exposing administrative API credentials when only a delivery signature is needed. Use the narrowest credential and isolate image signing from unrelated account operations.

    How long should a signed URL live?

    Choose expiry from the sensitivity of the content, expected viewing session, CDN caching model, and failure tolerance. A very short expiry can break slow clients, saved pages, retries, and caches. A very long expiry behaves more like a revocable secret link.

    Expiry limits future use but does not retract bytes already downloaded. For highly sensitive images, combine short-lived authorization with application controls, logging, and a realistic understanding that the client can copy visible content.

    Clock skew should be considered. Log the verifier’s rejection reason internally without revealing signing details to callers. Provide enough margin for normal request delay.

    How do signatures interact with caching?

    If each signed URL contains a unique token and the CDN includes it in the cache key, identical bytes can fragment across many entries. Some systems validate the token and then cache by stable content identity. Others require deliberate cache-key configuration.

    Never ignore a security parameter at the cache without ensuring authorization still runs on every protected request. A cached private response must not become publicly retrievable merely because the token was removed from identity.

    The cache key design guide explains content identity versus access proof. Test the actual sequence with valid, invalid, expired, and missing tokens, including a warm cache.

    What should you monitor?

    Track signature validation failures by reason, requested asset or policy class, credential ID, and rate. Avoid logging full tokens. Alert on unusual failure bursts, high-cardinality transformations, repeated requests near expiry, and use of a retired key.

    Keep an audit trail for signing operations involving sensitive assets. Record which trusted service issued the capability and the policy applied. Do not record more personal data than necessary.

    Pair signing with the private image delivery patterns appropriate to your threat model. A signature is one control within an access architecture, not a declaration that the entire path is private.

    Use signed URLs when they express a specific, enforceable capability. Scope them narrowly, generate them only in trusted code, test cache behavior, and make failure visible to operators.

  • Design a reliable image upload pipeline

    Design a reliable image upload pipeline

    Short answer: Accept uploads through a controlled endpoint or signed direct-upload flow, validate the real file, assign a stable asset ID, store the original, extract trusted metadata, and move the asset through explicit processing states. Make every retry idempotent.

    An upload endpoint is not finished when it returns a URL. It creates a source asset that future transformations, cache keys, editorial records, and deletion workflows depend on. A fragile ingestion model spreads that fragility across the entire delivery system.

    Direct upload or application proxy?

    In an application-proxy flow, the browser sends bytes to your server, which validates and forwards them. This gives the application tight control but consumes its bandwidth, memory, and request time. Large uploads can compete with normal API traffic.

    In a direct flow, the application issues a short-lived signed authorization and the client uploads to the media service or object store. This reduces application data transfer, but the server must still own policy and record completion. Do not give the browser a long-lived administrative credential.

    Cloudinary documents authenticated and unsigned methods in its upload guide. If unsigned presets are used, restrict them carefully because they represent a public capability. Provider-specific controls should map to your application’s policy, not replace it.

    What should be validated?

    Validate file size, detected media type, dimensions, pixel count, frame count, and allowed encoding. Do not rely only on the filename extension or client-provided Content-Type. Decode with maintained libraries in a resource-limited environment and fail closed on malformed content.

    Apply separate limits for animated images and high-resolution sources. A modest compressed file can expand into an enormous pixel buffer. Set time, memory, and dimension ceilings before expensive processing.

    Treat metadata as untrusted input. Strip metadata that is unnecessary for delivery, especially location data, while preserving fields your workflow deliberately needs. Normalize orientation before downstream crop logic if that is part of the platform contract.

    OWASP’s File Upload Cheat Sheet provides a broad security checklist. Adapt it to image-specific decoding and transformation risks.

    Which identity should the asset receive?

    Use a stable opaque asset identifier that does not depend on a mutable filename. Store the original filename as metadata if editors need it. Include a source version or content hash in delivery identity so replacing bytes does not silently reuse old derivatives.

    Deduplication can be helpful but should be explicit. Two users uploading identical bytes may not imply shared ownership or lifecycle. A content hash can detect repeats while business records remain separate.

    The upload response should return your canonical asset model, not a raw provider payload. Include asset ID, version, width, height, format, status, and safe preview information. The typed URL builder can consume that stable model.

    Which states make failure recoverable?

    Use explicit states such as initiated, uploading, received, validating, ready, rejected, and failed. Persist the state before asynchronous work starts. Each worker should be able to retry without creating another logical asset or overwriting a newer version.

    Assign an idempotency key to the upload intent. If a client retries after a lost response, return the existing result. For multipart upload, track parts and finalization separately. Expire abandoned intents and incomplete uploads through a scheduled cleanup policy.

    Do not publish a delivery URL until the source is validated and the required metadata exists. A placeholder status response is safer than letting the first public request discover a corrupt original.

    Should derivatives be generated during upload?

    Generate only predictable, high-value derivatives eagerly. A primary thumbnail, moderation preview, or guaranteed hero size may justify precomputation. Generating the full cross-product of widths, crops, qualities, and formats wastes work for variants never requested.

    On-demand generation is effective when the allowed set is bounded and cold latency is acceptable. The first-request behavior described in the cold path guide should inform which critical derivatives you warm.

    What should operations monitor?

    Track upload attempts, accepted bytes, rejection reasons, validation duration, processing duration, ready rate, orphaned intents, retry count, and storage growth. Correlate application upload IDs with provider request IDs without exposing secrets.

    Alert on a sustained rise in decode failures, timeouts, or assets stuck in a transitional state. A queue depth graph alone is not enough; age of the oldest item usually signals user impact more clearly.

    Provide administrators a safe retry and quarantine workflow. Preserve enough diagnostic metadata to understand failure, but do not retain malicious or rejected files indefinitely without a policy.

    A reliable upload pipeline creates a trustworthy asset before delivery begins. Stable identity, strict validation, explicit state, and idempotent recovery are what make later transformations and migrations routine instead of risky.

Share with