UNPKG

ai

Version:

AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.

112 lines (85 loc) 4.67 kB
--- title: Secure URL Fetching description: How the AI SDK protects server-side fetches of URLs returned by model providers, and how to harden your deployment further. --- # Secure URL Fetching Many providers return a **URL in their response body** — a generated image, audio, or video to download, or a polling URL to check job status. The AI SDK fetches these server-side and returns the result to your code. Because that URL comes from an external service, a malicious or compromised provider (or anyone able to tamper with the response) could point it at an internal address such as a cloud-metadata endpoint (`http://169.254.169.254/…`), a private host (`http://10.0.0.5/…`), or `localhost`. To prevent that, the SDK validates every response-supplied URL before fetching it. This happens automatically inside the provider packages — you don't need to configure anything. ## What the SDK protects against When the SDK fetches a URL taken from a provider response, it: - **Rejects private, loopback, and link-local targets**IPv4 (`10/8`, `172.16/12`, `192.168/16`, `127/8`, `169.254/16`, CGNAT, multicast, ) and the equivalent IPv6 ranges, plus `localhost` and `.local`. Non-`http(s)` schemes are rejected too. - **Re-validates every redirect hop** — a URL that passes but then redirects to an internal address is blocked; the redirect is never followed blindly. - **Validates DNS at connection time on Node.js** — every resolved address is checked, and the socket is pinned to the validated DNS result so DNS rebinding cannot introduce a different address between validation and connection. - **Strips risky request headers** — proxy-forwarding, cloud-metadata, and cookie headers are removed before the request. - **Drops credentials across origins** — caller headers (`Authorization`, `Cookie`, and provider-specific API-key headers alike) are not sent to a host on a different origin than the provider's; a redirect that crosses origin drops all of them except the user-agent. A blocked URL surfaces as a `DownloadError`. ## Self-hosted and local endpoints URLs that are same-origin with the provider endpoint **you configured** (e.g. a custom `baseURL` pointing at a self-hosted or `localhost` deployment) are exempt from these checks — they target exactly the host you told the SDK to talk to. Any redirect off that origin is still validated. ## DNS validation across runtimes On Node.js, the default validated download fetch uses `node:dns` and an `undici` connector hook to validate every resolved address at connection time. The connector uses those exact results, closing both hostname-to-private-IP and DNS-rebinding bypasses. If you inject or globally replace `fetch`, it is responsible for equivalent DNS validation and connection pinning. Other runtimes do not expose Node's DNS/socket hooks, so server deployments on those runtimes should restrict network egress to private, loopback, link-local, and cloud-metadata ranges. ## Hardening your deployment If your server fetches provider-supplied URLs and you want to close the DNS gaps, use one (ideally both) of these: ### 1. Restrict outbound egress at the network layer Deny your server's network egress to `169.254.0.0/16`, RFC-1918 ranges, and loopback. This is the most robust control and is independent of application code. ### 2. Harden an injected `fetch` The Node.js default is already pinned. If you inject or globally replace `fetch`, back it with an `undici` `Agent` whose `connect.lookup` validates the resolved IP and lets the socket connect only to a safe address — closing both the hostname-to-private and the DNS-rebinding windows: ```ts import { Agent, fetch as undiciFetch } from 'undici'; import { lookup } from 'node:dns'; // Your own check that returns true for private/loopback/link-local addresses. declare function isUnsafeAddress(ip: string): boolean; const safeLookup: typeof lookup = (hostname, options, callback) => { lookup(hostname, options as any, (err, address, family) => { if (!err && typeof address === 'string' && isUnsafeAddress(address)) { callback(new Error(`Refusing to connect to ${address}`), '', 0); return; } (callback as any)(err, address, family); }); }; const safeDispatcher = new Agent({ connect: { lookup: safeLookup } }); const safeFetch: typeof fetch = (input, init) => undiciFetch(input, { ...init, dispatcher: safeDispatcher }) as any; ``` ```ts import { createFal } from '@ai-sdk/fal'; const fal = createFal({ fetch: safeFetch }); ``` The SDK's URL validation and your custom fetch's connect-time pinning are complementary — keep both.