Key takeaways
- Validating the first URL is insufficient because redirects and browser subrequests can target private networks.
- Chromium should run non-root with its sandbox enabled, disposable contexts, and hard resource caps.
- Screenshot artifacts need private storage, authorization checks, short-lived delivery, deletion, and redacted logs.
Why is a screenshot API a security-sensitive service?
A screenshot API accepts a customer-selected URL and loads it with a network-capable browser. A malicious target can redirect, open frames, request scripts and images, connect through WebSockets, consume resources, or try to reach services that are not exposed to the public internet.
The renderer is therefore an untrusted-browser boundary. Secure request validation, network egress, process isolation, queue accounting, artifact delivery, logging, and cleanup all contribute to whether the service is safe.
How should a screenshot API prevent SSRF?
Permit only HTTP and HTTPS, reject embedded credentials and non-standard ports unless explicitly supported, resolve the hostname, and reject every answer that is loopback, private, link-local, multicast, reserved, or cloud metadata. Repeat the decision after redirects.
Application validation alone cannot see every request a browser makes. Scripts, frames, fonts, images, fetch calls, and WebSockets also need a controlled network path that resolves, validates, and connects to the approved public address without allowing DNS rebinding to change the destination.
- Reject literal and alternate encodings of private addresses.
- Reject a hostname if any returned address is blocked.
- Revalidate every redirect before following it.
- Apply the same policy to every browser subrequest.
- Ensure browser workers cannot route to control-plane or co-tenant services.
What does browser isolation require?
Run Chromium as a dedicated non-root user with its sandbox active. Use disposable, non-persistent contexts and recycle workers. Drop unnecessary operating-system capabilities, apply a reviewed seccomp policy, keep the filesystem read-only where practical, and isolate temporary data.
The browser should not receive database, queue, object-storage, billing, or customer-account credentials. A narrow internal protocol can return only bounded image bytes and normalized metadata to a trusted worker that owns those services.
| Control | Failure it limits |
|---|---|
| Non-root sandboxed Chromium | Reduces the impact of browser compromise. |
| Private-network egress denial | Blocks access to localhost, metadata, and internal services. |
| Disposable contexts and recycling | Limits state persistence and cross-job contamination. |
| CPU, memory, time, PID, and byte caps | Bounds malicious or broken page resource use. |
| No control-plane credentials | Limits what a compromised browser can steal. |
How should screenshot artifacts be protected?
Treat every screenshot as potentially sensitive. Store artifacts in a private encrypted bucket under random keys that contain no customer URL or identifier. Check account ownership before issuing a short-lived signed download URL, and never place that URL in logs.
Retention and deletion are durable workflows, not a single best-effort request. Record upload intent before transfer, verify the stored object, create an expiry task, retry deletion failures, and keep a tombstone after bytes are removed.
Which limits belong in the public API contract?
Document maximum viewport width and height, device scale, delay, redirects, full-page height, execution time, output bytes, requests per minute, concurrent jobs, signed URL lifetime, and artifact retention. Reject unknown fields so new behavior is always an explicit contract change.
Separate target failures from platform failures. Invalid input, blocked destinations, and oversized pages should not be hidden behind generic retries. Worker crashes and storage outages can be retried without charging or reserving usage twice.
Is URL validation enough to secure a screenshot API?
No. The browser network must enforce the destination policy for redirects and every subrequest so DNS changes or page resources cannot reach private services.
Can Chromium run with its sandbox disabled inside a container?
A container is not a substitute for the Chromium sandbox. Use both a hardened container boundary and an active browser sandbox unless a documented architecture provides equivalent isolation.
Should screenshot URLs be permanent and public?
No by default. Private storage with short-lived authorized delivery reduces accidental exposure and makes retention and deletion enforceable.
Review the screenshot job contract.
See the proposed request fields, job states, failure model, and private-artifact response.
Read the docs →