The Graffiti Analogy
A public bulletin board in a trusted location:
| Scenario | What Happens |
|---|---|
| You put up a notice | Many people trust it's official |
| Attacker puts up fake notice | Many people may follow the instructions |
| Nobody questions it | Because the board is "trusted" |
XSS is digital graffiti. Attackers inject their code into websites, and visitors' browsers run it as if it were legitimate.
What Is XSS?
XSS = Cross-Site Scripting
| Step | What Happens |
|---|---|
| 1 | Attacker injects malicious JavaScript into a website |
| 2 | Other users visit the site |
| 3 | Their browsers execute the attacker's script |
| 4 | Browser trusts it because it came from a "trusted" site |
What Malicious Scripts Can Do
| Attack | Damage |
|---|---|
| Steal cookies/sessions | Account takeover |
| Capture keystrokes | Password theft |
| Redirect to fake pages | Phishing |
| Modify page content | Spread misinformation |
| Make requests as user | Unauthorized actions |
Types of XSS
1. Stored XSS (Most Dangerous)
Attack is saved permanently on the server.
| Component | Example |
|---|---|
| Vector | Comment, profile, forum post |
| Persistence | Stored in database |
| Victims | Every user who views the page |
| Impact | Mass attack, persistent |
Example: Attacker posts a comment containing <script>stealCookies()</script>. Every user who views the comment page runs the script.
2. Reflected XSS
Attack is reflected back from the request.
| Component | Example |
|---|---|
| Vector | Search query, error message |
| Persistence | Not stored, in URL |
| Victims | Users who click malicious link |
| Impact | Targeted attack via link |
Example: URL site.com/search?q=<script>evil()</script> and page displays the query unsanitized.
3. DOM-Based XSS
Attack manipulates the DOM directly in browser.
| Component | Example |
|---|---|
| Vector | URL fragment, client-side data |
| Persistence | Mostly client-side |
| Victims | Users who visit crafted URL |
| Detection | Harder (server may not see it) |
Example: JavaScript reads from location.hash and writes to innerHTML without sanitization.
Attack Examples
Cookie Theft
Injected script:
Send document.cookie to attacker's server
Result:
Attacker has victim's session token
→ Account takeover
Keylogging
Injected script records every keystroke and sends to attacker. Captures passwords, credit cards, private messages.
Fake Login Form
Script replaces page content with fake login form. User sees trusted domain, enters password, credentials go to attacker.
Prevention Methods
1. Output Encoding (Escaping)
Convert special characters to escaped versions:
| Character | Encoded |
|---|---|
< | < |
> | > |
" | " |
' | ' |
& | & |
Input: <script>alert('XSS')</script>
Output: <script>alert('XSS')</script>
Browser shows text, doesn't execute!
2. Content Security Policy (CSP)
HTTP header that restricts script sources:
| CSP Policy | Effect |
|---|---|
script-src 'self' | Allows same-origin scripts |
script-src 'none' | No scripts at all |
script-src 'self' https://trusted.com | Self + specific CDN |
Inline scripts and unknown sources are blocked.
3. HttpOnly Cookies
| Attribute | Effect |
|---|---|
HttpOnly | JavaScript cannot access cookie |
| HTTPS-only | Sent only over HTTPS |
SameSite | Prevents cross-site sending |
Even if XSS exists, session cookies are protected.
4. Input Validation
| Validation | Example |
|---|---|
| Allowlist characters | Username: letters and numbers |
| Reject dangerous patterns | No <script>, javascript: |
| Type checking | Age should be a number |
Validation helps but is NOT sufficient alone. Encode output for the right context.
Defense in Depth
No single protection is enough. Use multiple layers:
| Layer | Protection |
|---|---|
| Input | Validate and sanitize |
| Output | Encode for context (HTML, JS, URL) |
| Headers | CSP, X-Content-Type-Options |
| Cookies | HttpOnly, HTTPS-only, SameSite |
| Framework | Use built-in XSS protections |
Common Mistakes
1. Trusting User Input
| ❌ Bad | ✅ Good |
|---|---|
| Display user input raw | Encode before display |
| Trust hidden fields | Validate server-side |
2. Wrong Encoding Context
| Context | Required Encoding |
|---|---|
| HTML body | HTML entity encoding |
| HTML attribute | Attribute encoding |
| JavaScript | JavaScript string encoding |
| URL | URL encoding |
Each context needs different encoding!
3. Allowing innerHTML
innerHTML = userInput is dangerous. Use textContent for text, or sanitize HTML with a library like DOMPurify.
4. Incomplete CSP
CSP with 'unsafe-inline' or 'unsafe-eval' defeats much of its purpose.
Testing for XSS
| Method | Approach |
|---|---|
| Manual testing | Try <script>alert(1)</script> in inputs |
| Automated scanners | OWASP ZAP, Burp Suite |
| Browser DevTools | Check what's rendered |
| CSP reports | Monitor blocked violations |
FAQ
Q: Is XSS still relevant?
Extremely. XSS is consistently in OWASP Top 10. Modern frameworks help but don't eliminate the risk.
Q: Which is worse: stored or reflected?
Stored XSS is often more dangerous (it can affect many users), but reflected XSS is more common.
Q: Do frameworks protect me automatically?
Most modern frameworks (React, Angular, Vue) escape output by default. But dangerouslySetInnerHTML or v-html bypass protections.
Q: What's the difference between XSS and SQL injection?
XSS attacks users' browsers. SQL injection attacks the database. Both are injection attacks, different targets.
Summary
XSS allows attackers to inject malicious scripts into websites, compromising users who visit.
Key Takeaways:
- Stored XSS: saved on server, can affect many users
- Reflected XSS: in URL, requires clicking link
- DOM-based XSS: client-side manipulation
- Output encoding: convert
<to< - CSP headers: restrict script sources
- HttpOnly cookies: protect sessions
- Defense in depth: multiple layers
- Treat user input as untrusted
XSS is a very common web vulnerability - learn to prevent it!
Related Concepts
Leave a Comment
Comments (0)
Be the first to comment on this concept.
Comments are approved automatically.