Running Code Locally
While this online editor runs real JavaScript, some security examples require a browser environment. Download Node.js to run JavaScript on your computer, use your browser's Developer Console (Press F12) to test code snippets, or create a .html file with <script> tags and open it in your browser.
Client-Side Security Essentials
Master XSS prevention, CSRF protection, input sanitization, secure token handling, and defense-in-depth architecture.
What You'll Learn
- XSS prevention techniques
- CSRF token protection
- Input sanitization
- Secure token handling
- Content Security Policy
- Defense-in-depth architecture
Why Client-Side Security Matters
The browser is exposed territory. Anyone can inject HTML, override JavaScript, modify requests, steal tokens, and manipulate the DOM. The only safe approach is to treat all user-controlled input as hostile.
What Attackers Can Do
- Steal session tokens and cookies
- Hijack user accounts
- Inject fake login forms
- Execute actions as the user
- Redirect to malicious sites
- Install keyloggers
Your Defense Goals
- Prevent script injection (XSS)
- Block forged requests (CSRF)
- Sanitize all user input
- Secure token storage
- Implement CSP headers
- Build defense-in-depth
Cross-Site Scripting (XSS) — The #1 Frontend Threat
XSS occurs when malicious script executes inside your page. Even one tiny mistake can compromise your entire application.
Three Types of XSS
- Stored XSS: Malicious script saved in database, shown to all users (comments, profiles)
- Reflected XSS: Script injected via URL parameters, executed immediately
- DOM XSS: JavaScript directly puts user input into DOM without server involvement
XSS Attack Basics
Understanding and preventing cross-site scripting attacks
// ❌ DANGEROUS - XSS vulnerabilities
const userInput = '<img src=x onerror="alert(document.cookie)">';
// This allows script execution!
document.getElementById("output").innerHTML = userInput;
// ✅ SAFE - Use textContent instead
document.getElementById("output").textContent = userInput;
// Renders as plain text, not HTML
// ❌ DANGEROUS - Setting href from user input
const link = document.createElement("a");
link.href = "javascript:alert('XSS')"; // Executes JS!
// ✅ SAFE - Validate URLs firs
...HTML Escaping — Your First Line of Defense
Escaping converts dangerous characters into safe HTML entities. This is essential when you must display user content.
HTML Escaping
Escape dangerous characters to prevent XSS
// HTML escape function - prevents XSS
function escapeHTML(str) {
const escapeMap = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/',
'`': '`',
'=': '='
};
return String(str).replace(/[&<>"'`=\/]/g, char => escapeMap[char]);
}
// Test with malicious input
const maliciousInput = '<script>alert("XSS")</script>';
const safeOutput = escapeHTML(maliciousInput);
console.log("Original:", maliciousInput);
console.l
...DOM-Based XSS — Common in SPAs
DOM XSS happens when JavaScript reads user input (from URLs, forms, storage) and inserts it into the page without sanitization. This is extremely common in React, Vue, and Angular apps.
DOM-Based XSS
Prevent DOM XSS in single-page applications
// DOM-based XSS - when URL params go directly to DOM
// ❌ DANGEROUS - Reading URL and inserting into DOM
const urlParams = new URLSearchParams("?name=<img src=x onerror=alert(1)>");
const name = urlParams.get("name");
// This would execute the XSS payload:
// document.body.innerHTML = "Hello, " + name;
// ✅ SAFE - Always sanitize URL parameters
function sanitizeParam(param) {
if (!param) return "";
// Use textContent approach
const div = document.createElement("div");
div.textCont
...Cross-Site Request Forgery (CSRF)
CSRF forces a victim's browser to make authenticated requests without their knowledge. If your app uses cookies for authentication, you're vulnerable.
CSRF Defense Layers
- SameSite cookies:
SameSite=StrictorLax - CSRF tokens: Random value verified on each request
- Origin validation: Check Origin/Referer headers
- Authorization headers: Use Bearer tokens instead of cookies
CSRF Protection
Implement CSRF token protection
// CSRF Attack Example and Defense
// ❌ How CSRF works:
// 1. User logs into bank.com (session cookie stored)
// 2. User visits evil.com
// 3. Evil.com has hidden form:
/*
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="amount" value="10000">
<input type="hidden" name="to" value="attacker">
</form>
<script>document.forms[0].submit()</script>
*/
// 4. Browser sends cookies automatically
// 5. Bank thinks it's legitimate request
// ✅ CSRF Token Protection
c
...Input Sanitization — Clean Everything
Any time user input appears in HTML, URLs, DOM, or attributes — it must be sanitized.
Input Sanitization
Comprehensive sanitization for text, HTML, and URLs
// Comprehensive Input Sanitization
// Basic sanitizer for plain text
function sanitizeText(input) {
if (typeof input !== 'string') return '';
return input
// Remove null bytes
.replace(/\0/g, '')
// Normalize unicode
.normalize('NFC')
// Trim whitespace
.trim()
// Limit length
.slice(0, 1000);
}
// HTML sanitizer (basic - use DOMPurify in production)
function sanitizeHTML(html) {
// Create temporary element
const temp = document.createElement('div');
...💡 Production Tip
Use DOMPurify for production HTML sanitization. It handles SVG attacks, mutation XSS, and edge cases that simple regex cannot.
Advanced XSS Vectors
Even when developers escape <script> tags, attackers exploit less obvious injection points.
Advanced XSS Vectors
Watch for event handlers, templates, SVG, and encoding attacks
// Advanced XSS Vectors to Watch For
// 1. Event Handler Injection
const userAvatar = 'x" onerror="stealCookies()';
// ❌ DANGEROUS:
// `<img src="${userAvatar}">`
// Results in: <img src="x" onerror="stealCookies()">
// ✅ SAFE: Escape attribute values
function escapeAttr(str) {
return String(str)
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/</g, '<')
.replace(/>/g, '>');
}
console.log("Safe attribute:", escapeAttr(userAvatar));
...Secure Token Handling
Token theft is the ultimate goal of most XSS attacks. Never store sensitive tokens in localStorage — XSS can steal them.
❌ Dangerous Token Storage
- localStorage (XSS can read it)
- sessionStorage (XSS can read it)
- URL parameters (visible in logs)
- Non-HttpOnly cookies (JS can access)
Secure Token Handling
Store and handle tokens securely in memory
// Secure Token Handling
// ❌ DANGEROUS - Token in localStorage (XSS can steal it)
// localStorage.setItem('token', 'secret-jwt-token');
// ❌ DANGEROUS - Token in URL
// window.location = '/dashboard?token=secret';
// ✅ BETTER - Token in memory (cleared on page refresh)
class SecureTokenStore {
#accessToken = null;
#tokenExpiry = null;
setToken(token, expiresIn = 3600) {
this.#accessToken = token;
this.#tokenExpiry = Date.now() + (expiresIn * 1000);
}
getToken() {
...Content Security Policy (CSP)
CSP is a browser-level defense that restricts what scripts can run. Even if an attacker injects a script, CSP can block it from executing.
Content Security Policy
Implement CSP headers to block script execution
// Content Security Policy (CSP) - Browser-Level XSS Protection
// CSP is set via HTTP header by the server:
// Content-Security-Policy: default-src 'self'; script-src 'self'
// Example CSP configurations:
const cspExamples = {
// Strict - Only same-origin resources
strict: {
"default-src": "'self'",
"script-src": "'self'",
"style-src": "'self'",
"img-src": "'self' data:",
"connect-src": "'self'",
"frame-ancestors": "'none'",
"object-src": "'none'"
},
//
...Defense-in-Depth Architecture
No single mechanism protects a modern app. You need layered security — if one layer fails, others prevent disaster.
Defense-in-Depth Architecture
Build layered security with multiple protection mechanisms
// Defense-in-Depth Security Architecture
class SecureApp {
constructor() {
this.csrfToken = this.generateCSRFToken();
}
// Layer 1: Input Validation
validateInput(input, rules) {
const errors = [];
if (rules.required && !input) {
errors.push('Field is required');
}
if (rules.maxLength && input.length > rules.maxLength) {
errors.push(`Max length is ${rules.maxLength}`);
}
if (rules.pattern && !rules.pattern.test(input)) {
errors.push
...Security Best Practices Checklist
Security Best Practices
Complete security checklist and audit tools
// Security Best Practices Checklist
const securityChecklist = {
// XSS Prevention
xss: {
"Use textContent instead of innerHTML": true,
"Escape HTML entities": true,
"Use DOMPurify for HTML sanitization": true,
"Validate URL protocols": true,
"Never use eval()": true,
"Implement CSP headers": true
},
// CSRF Prevention
csrf: {
"Use CSRF tokens": true,
"SameSite cookie attribute": true,
"Verify Origin header": true,
"Use POST for state changes
...🛡️ Security Mastery Summary
- XSS Prevention: Use textContent, escape HTML, sanitize with DOMPurify
- CSRF Protection: Use tokens, SameSite cookies, validate Origin headers
- Input Sanitization: Validate, sanitize, and escape ALL user input
- Token Security: Use HttpOnly cookies, memory storage, short expiry
- CSP: Implement strict Content Security Policy headers
- URL Safety: Validate protocols, prevent open redirects
- Defense-in-Depth: Layer multiple security mechanisms
- Never trust: localStorage, URL params, API responses, cookies
Sign up for free to track which lessons you've completed and get learning reminders.