Skip to main content

Safer URL previews for MENA SaaS: an SSRF review

Design a constrained fetch service with connection-time checks, isolated workers and bounded responses for Saudi and UAE SaaS deployments.

Updated 9 min readMarkdown
Part of seriesBuilder Guides
Safer URL previews for MENA SaaS: an SSRF review

A customer pastes a supplier's website into your portal. The backend fetches a title and image for a preview. That small convenience has given an outside user a way to influence where your server connects.

For a MENA SaaS product handling supplier onboarding, property listings or customer support, the useful design question is specific: can the preview worker reach anything that the customer could not reach directly? Server-side request forgery, or SSRF, exploits that gap. OWASP's prevention guidance distinguishes calls to known destinations from features that must accept arbitrary external destinations. They need different policies.

This guide proposes a public-page preview service, not an authenticated document importer or a general-purpose browser. Its release boundary is the actual outbound connection, followed by a bounded read of untrusted content. The same review can be applied separately to Saudi and UAE deployments without assuming they have identical hosting obligations.

Cover: original SultanByte artwork showing an isolated URL-fetch worker between a submitted link and a permitted public destination.

Decide which destinations the product really needs

If the feature imports from a handful of partners, offer a partner selector and a document identifier. Build the endpoint from a server-controlled integration record. Do not accept a complete URL simply because it is convenient for the first implementation. OWASP favours allowlisting where destinations are known and recommends network controls alongside application validation.

An open-web preview is a different product promise. For this proposed first release, accept only HTTPS on the standard port, use GET, reject embedded credentials and give users no control over request headers. Return a plain-text title and description; leave authenticated pages unsupported. Make those limits visible in the interface rather than quietly broadening the worker when a customer reports a failed preview.

Arabic domain names should go through the same parsing policy as other names. The WHATWG URL Standard defines host parsing and domain-to-ASCII processing. Use a maintained parser, inspect its parsed components and keep the canonical destination separate from the label displayed to a person. A readable Arabic hostname is neither a reason to reject it nor proof that it is safe. Avoid hand-written string-prefix checks for trusted hosts.

Validate the address the connection will use

Checking a hostname when a user saves a record is too early. DNS can change before a queued job runs. Checking it again immediately before a request is still insufficient if the HTTP client performs another unchecked lookup.

GitLab's secure-coding guidance documents this distinction: its URL blocker can return an address-resolved URI, but ignoring that returned value loses the DNS-rebinding protection. Treat validation and connection establishment as one reviewed operation, rather than two helpers that happen to receive the same string.

For our proposed service, resolve the destination, classify the returned IPv4 and IPv6 addresses, and reject the request if any candidate violates the destination policy. Connect only through the approved address set. Apply the same rule to retries and address-family fallback. Do not let a failed IPv6 connection trigger an unchecked IPv4 attempt, or vice versa.

Use maintained address-classification data rather than a few regular expressions. IANA's IPv4 and IPv6 registries distinguish private use, loopback, link-local and other special-purpose assignments. The policy also needs your organisation's own prohibited destinations, including internal services exposed on public addresses. “Globally reachable” is a network property, not your application's permission to connect.

The transport must preserve the requested hostname for TLS verification while using the approved network address. libcurl's CURLOPT_RESOLVE illustrates the host-and-port-to-address mechanism; it populates the client's DNS cache for that pair. It is a building block, not a complete SSRF defence. Review connection reuse, proxy behaviour and the lifetime of those entries in the client you actually deploy. Never disable certificate checks to make address pinning work.

Treat redirects as new connection decisions

Keep automatic redirects disabled in the first release. A preview that cannot follow a redirect is an honest product limitation; an invisible second request is a security decision the application has delegated to its HTTP client.

If redirects become necessary, resolve each Location value against the current URL and run the complete destination policy again before connecting. Keep a small explicit hop budget and a single overall deadline. A permitted first host must not authorise the next one.

libcurl's redirect documentation explains that redirects issue new requests and that protections for Authorization and Cookie headers do not cover every other custom header. Build outgoing headers from a minimal service-owned set. Never copy the incoming browser request's headers into the fetch request, and do not carry partner credentials into this public-preview service.

For the proposed implementation, a redirected URL that needs credentials becomes an unsupported preview. It does not trigger an attempt to reuse the user's session. Keep authenticated integrations in their own reviewed connectors with separately scoped credentials.

Proposed URL-preview workflow: accept a constrained request, validate resolved addresses, connect only to approved addresses, and read within resource limits. Redirects repeat validation; private networks and metadata remain blocked.

Original infographic: SultanByte. Proposed engineering workflow informed by OWASP, GitLab, libcurl and cloud-provider documentation, October 2026. The diagram describes controls, not a certification or measured security result.

Isolate the worker from cloud credentials

The fetch service should not share the main API's network reach or broad cloud role. In this design, its only application privileges are receiving authorised preview jobs and returning restricted results. Put egress enforcement at a layer it cannot bypass, and block direct alternate routes around an approved proxy. Verify the deployed path, including IPv6.

Metadata protection depends on the platform:

  • Amazon EC2 documents IMDSv2 as a session-oriented mechanism and supports configuring instances to reject IMDSv1. It also documents an IPv6 metadata endpoint. Require the appropriate instance settings where applicable, but do not treat IMDSv2 as permission to leave the fetcher's network unrestricted.
  • Azure's IMDS documentation describes a host-local endpoint and the required Metadata header. It warns that IMDS is open to applications on the VM, and documents managed-identity token retrieval through the service. A required header is not a substitute for isolating an untrusted fetcher.
  • Google Cloud's metadata guidance says processes that can query the metadata URL can access its values and recommends sandboxing processes that should not have that access. Its endpoint documentation includes both address families under the stated VM conditions.

These are provider-specific VM behaviours, not a claim that every container or managed runtime exposes the same interface. Review the actual runtime, identity mechanism and routes. A worker with no attached business-data role is easier to contain than one relying entirely on a URL filter to protect powerful credentials.

Bound the response before parsing it

A permitted public host can still send a slow, oversized or difficult response. OWASP API4:2023 treats execution time, memory, request volume and spending limits as separate resource controls.

Specify connection and overall deadlines, a streaming byte limit, a decoded-content limit and a per-tenant concurrency budget. Enforce them while reading, not after buffering the response. Choose values from your preview workload and safe testing; this guide does not prescribe a universal size or timeout.

Keep the parser offline. The proposed preview extracts text without executing scripts, fetching favicons, loading images or following embedded resources. If a later feature needs a screenshot, review it as a separate browser-execution service rather than enabling a headless browser inside the existing worker. Its subrequests would need their own enforcement boundary.

Render extracted fields as text, not remote HTML. Keep preview records tenant-scoped and avoid retaining response bodies by default. Record a request identifier, policy version, outcome and timing without copying full signed URLs or sensitive query strings into routine logs. Where fetched files become customer attachments, use the separate quarantine-and-release workflow; a successful network fetch does not establish that a file is safe.

Give each deployment a reviewable route

For a product operating in Saudi Arabia and the UAE, document the worker, resolver, egress proxy, result storage and diagnostic destinations for each deployment. This is a proposed operational record, not an assertion that either country requires every preview to remain local.

Ask buyers what kinds of links users will submit. Public marketing pages and signed supplier-document links create different confidentiality risks. If a third-party preview API receives the URL, include that disclosure in the processing review. Do not silently fail over to another country's worker or an external preview vendor when the approved route is unavailable.

The engineering decision is whether the feature can operate within the customer's approved processing arrangement. The legal assessment is separate; this is practical engineering information, not legal advice.

Test the connection boundary, not just the error message

Use synthetic destinations and controlled DNS in an isolated test environment. Do not probe third-party internal networks or production metadata services. The following are proposed acceptance cases, not benchmark results:

  1. Change a controlled DNS answer between validation and connection. Require the worker to use only the approved address or refuse the request.
  2. Return a mix of permitted and prohibited address candidates. Require rejection under the stated policy, including fallback paths.
  3. Redirect a permitted test endpoint toward a prohibited test destination. Require no connection to that destination.
  4. Supply an oversized response, a slow stream and compressed content that exceeds the decoded limit. Require bounded resource use and a truthful failure status.
  5. Try to bypass the approved egress path from the deployed worker identity. Require the network control to deny it.
  6. Request another tenant's preview result. Require denial even when the fetch itself succeeded.

Capture the destination observed by the test network as well as the application result. An error returned after an unwanted request is not a passed SSRF test.

For the release review, ask the team to trace one URL from input through DNS resolution, connection selection and response parsing. Every transition should have an owner and a test. Ship the narrow preview that passes those checks; add broader fetching capabilities only when their additional destinations and privileges have been reviewed.

About the author

  • I have lead the Engineering for multiple startups in UAE. I also have my own agency qualascend.com.

No comments yet. Be the first to comment.