SSRF: server-side request forgery
Many applications fetch resources on behalf of their users: they import a picture from a link, check a webhook address, generate a preview of a URL, or download a file from another service. SSRF appears when an attacker can control the address that the server contacts. The server then becomes an unwitting messenger, sending requests from inside the network, where the attacker cannot go.
Notions and lab only
SSRF can reach internal systems and cloud infrastructure, so testing it on a real application can have consequences well beyond the one page you are looking at. This lesson stays conceptual, with no ready-made attack addresses. Practise in the PortSwigger Web Security Academy SSRF labs and in the intentionally vulnerable applications designed for it. On real targets, SSRF tests require explicit authorisation, with clear limits on which internal addresses may be probed.
The idea in an analogy
Imagine a company where employees can ask the mailroom to fetch any document from any address you write on a slip. The mailroom is inside the building, with access to the internal archive that outsiders cannot enter. If the mailroom clerk does not check the address, a stranger can write “internal archive, cabinet 3” and the clerk will bring the contents back. The clerk was trusted; the stranger borrowed that trust.
That is SSRF: the server makes a request to a destination chosen by the user, and the server’s position in the network, its credentials, and its trusted status are used by the attacker.
Where SSRF appears
Anywhere a feature makes the server fetch something from a user-supplied location:
- “Import from URL”, avatar-from-link, or remote image upload features.
- Link preview and URL unfurling.
- Webhooks, callbacks and “test this endpoint” buttons.
- PDF or screenshot generators that render a given page.
- Document and feed importers (RSS, XML with external references).
- Parameters that look like addresses:
url=,dest=,redirect=,feed=,image=,callback=.
Look back at your inventory: any parameter that contains a full URL or a host name deserves an “SSRF?” note.
Why it is serious
The server often sits in a privileged position:
- It can reach internal services that are not exposed to the internet: admin panels, databases with web interfaces, monitoring tools, internal APIs.
- It can reach the server’s own local services, those listening on the machine itself and trusted because requests come from “inside”.
- In cloud environments, the platform provides a local metadata service that can hand out temporary credentials to the machine. Reaching it through SSRF has been behind several serious real-world breaches, which is why cloud providers have added protections that you should enable.
- It hides the attacker. The requests come from the server’s address.
- It allows internal reconnaissance: differences in response time or error message show which internal hosts and ports exist.
Direct and blind SSRF
- Direct (in-band): the application returns the content of the fetched resource to the user, for example showing the imported page or file. The tester sees the result immediately.
- Blind: the server makes the request but does not show the answer. The tester must detect it indirectly: through timing differences, error messages, or by checking whether a controlled external service in the lab received the request.
In an authorised lab, a safe first test for a blind SSRF uses an address that you control, for example a simple listener on your own lab machine, and checks whether the application contacts it. This shows that the server makes outbound requests on demand, without touching anything internal. It is also how the Academy labs demonstrate the concept.
How a tester reasons about it
- Identify the fetching feature and the parameter carrying the address.
- Determine whether the server really makes the request with a lab-controlled destination, and look at what it sends and what it shows back.
- Look at which restrictions exist. Does the application only accept certain hosts? Only
https? Does it block private ranges? Does it follow redirects? - Judge the potential reach in the lab’s network design: could the server reach internal-only services?
- Stop at the proof and report with the lab evidence; in real engagements, ask the client before probing internal addresses.
Why filters fail
Developers commonly try to block dangerous destinations with a deny-list: no localhost, no private ranges. These approaches are repeatedly bypassed because there are many ways to express the same destination:
- The same address can be written in different notations or encoded forms.
- A domain name controlled by someone else can be made to resolve to an internal address.
- The checked address and the contacted address may differ if the name is resolved twice, which attackers can exploit.
- An allowed external site could redirect the server toward an internal address, if redirects are followed automatically.
- Other URL schemes (not just
http) may be supported by the fetching library.
The lesson, once again: a blacklist is a weak defence, because the attacker only has to find one form you did not think of.
Prevention
A robust approach layers several controls.
- Prefer allowlists. If the feature only needs to talk to a few known services, accept only those exact destinations, chosen from a list in the code or configuration, not typed by the user. Where possible, let the user choose from an option rather than supplying a URL at all.
- Validate carefully when free URLs are unavoidable: parse the URL with a proper parser, allow only
https(andhttpif needed), resolve the host name, and verify the resolved address is a public one; then connect to that same address to avoid a second lookup. - Do not follow redirects automatically, or validate each hop.
- Network segmentation. Place the fetching component in a restricted network zone, with outbound firewall rules that deny access to internal services and to the cloud metadata service.
- Cloud hardening. Use the stronger, session-based protections for metadata services offered by cloud platforms, and grant machines minimal roles, so a stolen credential is of little use.
- Limit what is returned to the user and avoid displaying raw responses from remote servers.
- Logging and monitoring of outbound requests to detect unusual destinations.
Design idea (pseudocode)
1. Parse the URL; reject if the scheme is not https
2. Reject if the host is not in the allowlist
3. Resolve the host; reject if the address is private, loopback or link-local
4. Connect directly to the resolved address; disable automatic redirects
5. Enforce a short timeout and a maximum response size
Tip
The OWASP SSRF Prevention Cheat Sheet at owasp.org is a good reference to quote in a recommendation. Developers appreciate a link to a concrete design rather than a general warning.
Your turn: assess a fetching feature on paper
An application has an “import a profile picture from a link” feature: the user pastes a URL and the server downloads the image. List (a) the parameter and risk, (b) three things you would check in a lab, (c) the controls you would recommend. If you have access to the Academy SSRF labs, complete the first one afterwards.
See a solution
- Risk: the URL parameter lets the user choose where the server connects; SSRF, possibly blind (the application may only display the final picture).
- Checks in a lab: does the server contact an address you control (a listener on your lab machine)? Which schemes and hosts does it accept? Does it follow redirects and does it display the downloaded content or the error messages?
- Recommendations: prefer upload of the file instead of a URL; if a URL is required, allow only
https, validate the resolved address against private ranges, disable redirects, apply timeouts and size limits, and restrict outbound traffic from the server by network rules.
Add the feature to your notebook with these three points, as the beginning of a finding.
Key takeaways
- SSRF lets an attacker make the server send requests to a destination of their choosing, borrowing the server’s privileged network position.
- Look for features that fetch URLs: imports, previews, webhooks, document renderers and parameters named like
url. - It can expose internal services and cloud credentials; it can be direct or blind.
- Deny-lists are easy to bypass; prefer allowlists, careful URL parsing, no automatic redirects and network segmentation.
- Test only in labs or with explicit authorisation, because the effects reach beyond the application.
That completes chapter 5. Next up: flaws that do not live in the code you wrote but in how the platform is set up and in the components it depends on.