Security Best Practices
Security Best Practices in JavaScript
Defending against XSS, CSRF, prototype pollution, injection, and supply chain threats.
The Medieval Castle Moat and Drawbridge
Client-side and Node.js JavaScript are primary targets for malicious actors. Applying defense-in-depth ensures applications resist injection, credential theft, and unauthorized actions.
Cross-Site Scripting (XSS) Prevention
Never inject raw user strings via `innerHTML` or `document.write`. Always use `textContent` or sanitize rich text using certified sanitizers like `DOMPurify`.
Content Security Policy (CSP)
An HTTP header that restricts where scripts, images, and fonts can load from, preventing unauthorized script execution even if an injection vulnerability exists.
Cross-Site Request Forgery (CSRF)
Use `SameSite=Lax` or `SameSite=Strict` on authentication cookies. Combine with anti-CSRF challenge tokens for sensitive state-changing mutations.
Prototype Pollution Defense
Deep-merge libraries must reject `__proto__`, `constructor`, and `prototype` keys to prevent attackers from mutating global Object prototypes.
Safely sanitizing untrusted user inputs and guarding against prototype pollution:
// 1. Safe Property Assignment (Guarding Prototype Pollution)
function safeSetProperty(target, key, value) {
// Reject prototype injection vectors
if (key === "__proto__" || key === "constructor" || key === "prototype") {
throw new Error("Security Violation: Prototype pollution attempt detected");
}
target[key] = value;
}
const safeData = {};
safeSetProperty(safeData, "username", "alice");
console.log("Safe property set:", safeData.username);
try {
safeSetProperty(safeData, "__proto__", { isAdmin: true });
} catch (err) {
console.log(err.message);
}
// 2. Safe DOM update (always use textContent for untrusted strings)
// element.textContent = untrustedInput; // Safe: browser treats input as raw text
Safe property set: alice
Security Violation: Prototype pollution attempt detected
Using innerHTML with Untrusted User Data
The Risk: `div.innerHTML = userComment;` allows attackers to inject ``, hijacking user sessions.
The Fix: Always use `div.textContent = userComment` or sanitize with `DOMPurify.sanitize(userComment)`.
Storing Sensitive Tokens in localStorage
The Risk: Any XSS script running on the page can execute `localStorage.getItem("token")` and transmit credentials to an attacker server.
The Fix: Store session tokens in `HttpOnly; Secure; SameSite=Strict` cookies.
- ✓ Never trust raw user input: sanitize or use `textContent`.
- ✓ Never use `eval()` or `new Function()` with dynamic strings.
- ✓ Protect cookies with `HttpOnly; Secure; SameSite=Strict`.
- ✓ Deploy a strict Content Security Policy (CSP) header.
- ✓ Block `__proto__` and `constructor` keys in object-merging utilities.