Cross-site scripting remains one of the most common web vulnerabilities because it appears anywhere untrusted input reaches a page without correct encoding. The consequence is frequently understated: successful XSS runs attacker code with the full privileges of your application, in your user's session, inside your origin.
The three types
- Stored (persistent) — the payload is saved server-side, in a comment, profile field or support ticket, and executes for every user who views it. The most damaging variant, because it needs no interaction from the victim and can affect administrators reviewing content.
- Reflected — the payload travels in the request, typically a query parameter, and is echoed straight back in the response. Requires the victim to follow a crafted link, which makes it a natural pairing with phishing.
- DOM-based — the vulnerability lives entirely in client-side JavaScript, which reads attacker-controllable input and writes it into a dangerous sink. The server may never see the payload at all, so server-side logs and filters provide no protection.
What an attacker actually gains
XSS is often dismissed as "just a popup". In reality the attacker executes arbitrary JavaScript with your application's privileges in the victim's browser.
- Perform any action the user can perform, using their live session — no stolen password required.
- Read any data rendered on the page, including data the user has legitimate access to.
- Capture keystrokes and form input, including credentials typed into your own login form.
- Rewrite the page to present convincing fake prompts within a genuine, correctly certificated origin.
- Steal session tokens where they are reachable from JavaScript, which is why HttpOnly cookies matter.
- Chain into account takeover by changing the email address or adding a new authentication factor.
Why filtering input is the wrong model
Attempting to strip dangerous characters on input is a losing strategy: the same data may be rendered later into HTML, an attribute, JavaScript, CSS or a URL, and each of those contexts requires different escaping.
The correct model is to store input faithfully and encode on output, according to the context it is being written into. Encoding for HTML text is not sufficient when the value lands inside a script block or an event handler attribute.
Defending properly
- Use a template engine that performs context-aware auto-escaping, and treat any use of its "raw" escape hatch as a code-review trigger.
- Avoid dangerous DOM sinks. Prefer assigning text content over writing markup, and never pass untrusted input to functions that evaluate strings as code.
- Deploy a strict Content Security Policy, ideally nonce-based, so that even a successful injection cannot execute. Treat CSP as a strong second layer, not a substitute for encoding.
- Set cookies HttpOnly, Secure and SameSite so session tokens are not readable from script.
- Sanitise rich text with a well-maintained, allow-list-based library rather than a homemade filter.
- Consider Trusted Types in supporting browsers to make unsafe DOM sinks fail closed.
Related from TechBiz Security
Sources & further reading
- OWASP Cross Site Scripting Prevention Cheat Sheet
- PortSwigger Web Security Academy — Cross-site scripting
- MDN — Content Security Policy
- CWE-79: Cross-site Scripting
0 comments
Leave a comment