SSRF: when a request decides where your server connects
Server-side request forgery turns your server into a proxy. If request input chooses the URL your code fetches, an attacker can reach whatever your server can: internal APIs, admin panels, the cloud metadata service.
Example
app.get("/preview", async (req, res) => {
const response = await fetch(req.query.url);
res.send(await response.text());
});const API = "https://api.example.com/quotes/";
app.get("/quote", async (req, res) => {
const symbol = String(req.query.symbol);
if (!/^[A-Z]{1,5}$/.test(symbol)) return res.sendStatus(400);
const response = await fetch(API + encodeURIComponent(symbol), { redirect: "error" });
res.json(await response.json());
});How it happens
URL previews, webhooks, PDF renderers and import-from-URL features all fetch addresses supplied by users. A value of http://169.254.169.254/latest/meta-data/ asks a cloud VM for its own credentials; http://localhost:6379 talks to an internal Redis. Redirects and DNS names that resolve to private addresses get around naive blocklists.
How to fix it
Let input choose as little of the URL as possible.
- Build the URL from a fixed base and validate only the variable part
- If arbitrary hosts are a feature, resolve the name and refuse private, loopback and link-local addresses — on every redirect too
- Connect to the address you checked, so DNS rebinding cannot swap it
- Disable or re-check redirects, and cap response size and time
How RepoVerse finds it
- HighRequest input chooses where the server sends an HTTP request
Under the right network conditions the server can be made to call internal services or the cloud metadata endpoint (169.254.169.254) and hand the response back.
data flow · CWE-918
- HighRequest sent to a URL from the caller
The address is chosen by whoever made the request, so this will happily fetch `169.254.169.254` and hand back the cloud credentials behind it.
pattern · CWE-918
Questions
- Is a URL starting with my own domain safe from SSRF?
- When the host part is fixed and input only fills in the path, the server cannot be pointed elsewhere. RepoVerse treats a URL led by a literal host or a fixed base such as API_BASE_URL as safe and keeps reporting input that can still reach the host.
- How does RepoVerse handle SSRF in its own website scanner?
- Every request and every redirect is resolved and checked against private, loopback, link-local and reserved ranges, only ports 80 and 443 are used, and the socket connects to the address that was checked.
Related
- Open redirect: your domain as a phishing linkHow an unvalidated redirect parameter (CWE-601) turns your domain into a trusted-looking link to a phishing page, and how to allow only local paths.
- Path traversal: reading files outside the intended folderHow ../ sequences in a file name escape the intended folder (CWE-22) to read config files and keys, and the resolve-and-check pattern that stops it.
- GitHub security scanner for code, secrets and dependenciesScan a GitHub repository for injection flaws, leaked secrets and vulnerable dependencies. Data-flow traces from request to sink, lockfile-aware, with fixes.
Updated 2026-10-11