Learning outcomes
- Replace arbitrary URLs with named destinations where the product permits it.
- Validate parsed URL, resolved address, redirect, credential, and network policy as one decision.
- Isolate the fetch worker from internal services, control planes, metadata, and unnecessary secrets.
- Test direct, blind, DNS-changing, redirected, encoded, and resource-exhaustion SSRF paths.
Protection need
Assume an authenticated user can submit a webhook or import destination. The service must reach approved external systems without becoming a proxy to cloud metadata, loopback services, control planes, internal APIs, private tenants, local files, or arbitrary protocols. It must not forward application credentials to the selected destination or consume unbounded resources.
Map the request path from user input through URL parsing, validation, DNS, proxy settings, connection selection, TLS, redirects, authentication, response parsing, logging, and any later fetch by a worker. Include every network interface, resolver, egress proxy, service mesh, and retry process.
Inventory sensitive destinations:
- Cloud instance and container metadata.
- Kubernetes and cloud control-plane APIs.
- Loopback and host services.
- Private address ranges and service discovery names.
- Databases, queues, caches, and secret stores.
- Unix sockets, local files, and non-HTTP protocol handlers.
- Tenant-private destinations reachable from shared infrastructure.
Security objectives and requirements
Start with product semantics. If users choose among known integrations, store a destination identifier and map it to a server-controlled URL and credential. If tenants register webhooks, require an explicit registration and verification step before events can be sent. Avoid accepting a new arbitrary destination on each sensitive operation.
For unavoidable user-selected URLs:
- Parse once with one maintained URL parser.
- Allow only required schemes, normally HTTPS.
- Reject user information, fragments, unexpected ports, and ambiguous host forms.
- Convert international host names through one defined process.
- Resolve all addresses through the controlled resolver.
- Reject prohibited address categories according to the feature policy.
- Connect only to an address that passed the decision and retain the expected host for TLS verification.
- Disable redirects or repeat the complete decision for every hop.
- Remove caller-controlled authorization, forwarding, host, proxy, and hop-by-hop fields.
- Attach credentials only after an approved named destination is selected.
Treat DNS and connection as one operation. A separate validation lookup followed by a library lookup creates a rebinding window. Use a resolver and connection interface that lets the application validate and connect to the same selected address where practical.
Security invariants and evidence
Use independent application and network invariants:
- A fetch can connect only to an address and port allowed for its destination class.
- The fetch worker has no route to metadata, control planes, loopback services outside its namespace, or unrelated private networks.
- A caller cannot add a credential or forwarding field to the outbound request.
- An integration credential is usable only for its approved service and is never sent after an unapproved redirect.
- Response bytes, decompressed bytes, time, redirects, connections, and parser depth stay within fixed budgets.
- Logs identify the caller, normalized destination class, selected address category, decision, and result without recording credentials or sensitive response data.
Verify application policy with unit and integration tests. Verify the deployed egress boundary with connection tests from the exact worker identity and network. Capture destination decisions and denied categories in test evidence.
Failure cases
Test at least these cases:
- IPv4, IPv6, integer, hexadecimal, shortened, mixed, and encoded address forms.
- Loopback, link-local, private, multicast, unspecified, broadcast, and metadata destinations.
- A host with several addresses where one is prohibited.
- A DNS answer that changes between lookup and connection.
- Redirects from public to private, HTTPS to another scheme, and approved to unapproved ports.
- Credentials embedded in the URL or supplied through request fields.
- Proxy environment variables and alternate URL handlers.
- Blind requests that change state or reveal reachability through time and status.
- Huge, slow, compressed, recursive, or never-ending responses.
- Retry after timeout when the destination may have processed the request.
Test from every worker path. A synchronous preview and an asynchronous importer can use different libraries, networks, and credentials.
Design tradeoffs and residual risk
An allowlist provides strong control and limits flexible user integrations. A denylist of internal ranges must track complex address categories and still depends on parser and DNS behavior. An egress proxy centralizes policy and becomes a high-value, high-availability component. Response inspection improves safety and increases parser surface.
Select controls from the actual destination model. Record residual risks such as customer-controlled DNS, required private callbacks, shared egress, or a legacy redirect. Give each one a bounded network scope, monitoring signal, owner, and review trigger.
Pomerium boundary
Pomerium normally protects inbound access to the application. It does not automatically mediate outbound fetches from the upstream worker. If an approved internal destination is separately protected by Pomerium, the worker still needs a suitable identity and narrow policy, and the fetch feature must not let a caller select or reuse that credential for another resource.
The application and platform own URL policy, DNS and connection consistency, egress routes, cloud metadata protection, credentials, resource limits, and response parsing.
Exercise
Choose a webhook, URL preview, remote import, or callback feature. Produce:
- A destination-class table with allowed schemes, hosts, ports, addresses, redirects, and credentials.
- A network map from worker to every sensitive internal destination.
- An invariant and test for URL parsing, DNS selection, connection, redirect, headers, credential attachment, and response bounds.
- A deployed egress test from the worker identity.
- A failure test for DNS change and a public-to-private redirect.
Replace arbitrary input with a named destination if the product does not need open URL selection. Record the remaining risk if it does.
Evaluation checklist
- Does product design minimize arbitrary URL input and prefer registered named destinations?
- Are parsed URL, resolved address, selected connection, TLS host, redirects, and credentials one coherent policy?
- Is the worker unable to reach metadata, control planes, loopback, and unrelated private services at the network layer?
- Are caller-controlled authorization, proxy, forwarding, and host fields removed?
- Are time, redirects, response bytes, decompression, parser depth, and retries bounded?
- Do tests cover every library and synchronous or asynchronous worker path?
- Does evidence prove the deployed egress result rather than only configuration intent?
Next learning unit
Server-Side Request Forgery (SSRF)
Prevent untrusted input from making a server reach internal services, cloud metadata, local files, or other unapproved destinations.
