⚠️ Threat 1: DOM Manipulation - innerHTML Vulnerability
Using
innerHTML with user input allows attackers to inject arbitrary HTML and JavaScript code, leading to DOM-based XSS attacks.
Why innerHTML is Dangerous:
- Parses and executes JavaScript within HTML strings
- Bypasses CSP script-src restrictions on inline scripts
- Allows injection of event handlers and scripts
- No input sanitization occurs automatically
- Can lead to session hijacking, credential theft, malware distribution
❌ Vulnerable Code UNSAFE
// VULNERABLE - DO NOT USE
const userInput = getFromURL();
document.getElementById('output')\n .innerHTML = userInput;
This code directly renders user input as HTML, executing any embedded scripts.
✅ Secure Code SAFE
// SECURE - USE textContent
const userInput = getFromURL();
document.getElementById('output')\n .textContent = userInput;
textContent treats input as plain text, escaping any HTML/JavaScript automatically.
⚠️ Threat 2: Event Handler Injection
Dynamically setting event handlers through user-controlled data allows attackers to execute arbitrary code on user interactions.
Attack Vectors:
- onclick, onload, onerror, onmouseover attributes
- Directly setting element.onclick, element.onmouseover properties
- Using addEventListener with attacker-controlled callbacks
- Injecting event handlers through HTML templates
- Data attributes that get converted to event handlers
❌ Vulnerable Event Handler UNSAFE
// VULNERABLE
const userCode = getUserInput();
button.onclick = function() {\n eval(userCode); // EXTREMELY DANGEROUS\n};
This executes arbitrary code when the button is clicked.
✅ Secure Event Handling SAFE
// SECURE
const userData = getUserInput();
button.addEventListener('click', () => {\n // Validate and process data safely\n if (isValidData(userData)) {\n processData(userData);\n }\n});
Event handlers are predefined functions, not user input. Data is validated before processing.
⚠️ Threat 3: Malicious JavaScript Execution
Using eval(), Function(), or dynamic script injection allows complete compromise of the application.
Dangerous Practices:
eval()- Executes any string as JavaScript codeFunction()constructor - Creates functions from stringssetTimeout()with string argumentsetInterval()with string argument- Dynamic <script> tag injection
❌ Vulnerable: Using eval() UNSAFE
// VULNERABLE - NEVER DO THIS
eval(userExpression);
// Attacker could input:
// "fetch('/steal', {..})"
eval() executes any JavaScript code, giving attackers full access to the application.
✅ Secure: Parse & Validate SAFE
// SECURE - Use proper parsers
const result = JSON.parse(userJSON);
// Or use a safe math library
const answer = mathEvaluator(expr);
Use specialized parsers like JSON.parse() or dedicated libraries designed for specific data types.
🛡️ Defense Strategies
1. Use textContent Instead of innerHTML
- textContent treats all input as plain text
- HTML entities are automatically escaped
- No script execution is possible
- Performance benefit - no HTML parsing
// ✅ GOOD: Safe text insertion
document.getElementById('result').textContent = userInput;
// ❌ BAD: Allows HTML/JS execution
document.getElementById('result').innerHTML = userInput;
// ✅ GOOD: Safe HTML using createElement
const div = document.createElement('div');
div.textContent = userInput;
parent.appendChild(div);
2. HTML Allowlisting
When safe HTML is needed, use a library that only allows specific safe tags and attributes.
// ✅ GOOD: Using DOMPurify library
import DOMPurify from 'dompurify';
const cleanHTML = DOMPurify.sanitize(userHTML, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'p', 'br'],
ALLOWED_ATTR: []
});
document.getElementById('result').innerHTML = cleanHTML;
3. Event Handler Stripping
Remove all event attributes before rendering or use predefined handlers only.
// ✅ GOOD: Remove event handlers
const div = document.createElement('div');
div.textContent = userContent;
// Remove any event attributes
['onclick', 'onload', 'onerror', 'onmouseover']
.forEach(attr => div.removeAttribute(attr));
// ✅ GOOD: Only use predefined handlers
button.addEventListener('click', () => {
handleUserData(userData);
});
4. Content Security Policy (CSP)
Use CSP headers to prevent inline script execution and restrict script sources.
// ✅ GOOD: CSP Headers Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; // This prevents inline scripts and restricts // script loading to same-origin only
5. Input Validation
Always validate and sanitize user input before processing.
// ✅ GOOD: Validate input
function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
function processUserInput(input) {
if (!isValidEmail(input)) {
console.error('Invalid input');
return;
}
// Process validated input
}
✅ Security Checklist
🎯 Key Takeaways
📋 textContent
Default choice for displaying user text. Safely escapes all HTML.
⚠️ innerHTML Carefully
Only with controlled, trusted content. Always sanitize if from users.
✅ Validation First
Always validate input on both client and server side.
🚫 Never eval()
eval() is equivalent to giving hackers full control. Avoid at all costs.
🎯 Explicit Handlers
Define event handlers in code, never from user data.
🛡️ CSP Headers
Use Content Security Policy to enforce security policies.