XSS Attacks: How Malicious Code Hijacks Web Applications
An attacker posts a comment on a social media site with hidden JavaScript inside it. When other users view the comment, the code runs in their browsers and steals their login tokens and personal data. This is XSS—Cross-Site Scripting—one of the most common web vulnerabilities.
Unlike CSRF, which tricks browsers into making requests, XSS injects malicious code straight into the page. The attacker's script runs with the same permissions as legitimate code, so it can reach session data, cookies, and sensitive information.
How XSS Attacks Work
XSS exploits the trust between users and websites. When a site renders user-supplied data without validation, attackers inject JavaScript that runs in visitors' browsers.
For example, a website displays user comments without filtering:
When the page displays this comment, the image fails to load and triggers the onerror event. The JavaScript runs in the visitor's browser with full access to their data.
Unlike CSRF, XSS doesn't require the user to visit another site. The attacker's code runs on the legitimate website itself, with full access to page content, cookies, and session data.
Types of XSS Attacks
Stored XSS
The malicious code is saved in the database and served to every user who views it:
This is the most dangerous type because it hits every user who views the malicious content.
Reflected XSS
The malicious code is sent through a URL parameter and reflected back in the response:
Reflected XSS needs the victim to click a malicious link, so it's less damaging than stored XSS.
DOM-Based XSS
JavaScript updates the DOM with user input without validation:
DOM-based XSS happens entirely on the client and is harder to detect.
Why XSS is Dangerous
XSS gives attackers access to:
- Session cookies and authentication tokens
- Personal data on the page
- Users' browsing history
- Ability to perform actions as the user
- Malware distribution
- Phishing attacks
A single XSS vulnerability can compromise thousands of users.
Defense Strategies
Input Validation
Validate all user input and reject anything that doesn't match the expected format. Sanitize dangerous content before storing it:
Never trust user input. Validate format first, then sanitize (remove dangerous content) or encode (escape special characters) before storing or displaying.
Output Encoding
When you display data, encode it by converting special characters to HTML entities. This differs from sanitization—encoding keeps the data but makes it safe:
Content Security Policy (CSP)
Set a CSP header to control where scripts can load from:
CSP is powerful but needs careful configuration to avoid breaking legitimate functionality.
HttpOnly Cookies
Prevent JavaScript from accessing session cookies:
Quick Defense Checklist
- ✅ Validate input format before processing
- ✅ Sanitize user input (remove dangerous content)
- ✅ Encode output when displaying user data (escape special characters)
- ✅ Use frameworks with built-in XSS protection (React handles encoding automatically)
- ✅ Set Content Security Policy headers
- ✅ Mark sensitive cookies as httpOnly
- ✅ Use security libraries like DOMPurify for HTML sanitization
- ✅ Keep dependencies updated for security patches
- ✅ Test for XSS vulnerabilities regularly
Real-World Examples
XSS has compromised major platforms:
- Facebook (2013): Attackers embedded code in comments
- Twitter (2014): DOM-based XSS allowed account hijacking
- Google (2019): Multiple XSS vulnerabilities found in services
These incidents led to stricter input validation and CSP adoption across the industry.
Conclusion
XSS attacks are common because they're simple to run but easy to overlook. A single validation or encoding mistake can expose thousands of users to attack.
The defense takes three layers: validate input format, sanitize dangerous content, and encode output. Modern frameworks like React encode automatically, but custom DOM manipulation needs care.
Never assume user input is safe. Always validate format, sanitize content, encode output, and use CSP.