javascript 208 lines · 1 tab

Front-end security - XSS and CSRF prevention

Alex Chang Feb 2026
1 tab
// 1. DANGEROUS: Never use innerHTML with user input
const userInput = '<img src=x onerror="alert('XSS')">';

// WRONG - vulnerable to XSS
document.getElementById('output').innerHTML = userInput;

// RIGHT - safe, treats as text
document.getElementById('output').textContent = userInput;

// 2. Safe DOM manipulation
function displayUserComment(comment) {
  const commentEl = document.createElement('div');
  commentEl.className = 'comment';

  // Create text nodes (safe)
  const authorEl = document.createElement('strong');
  authorEl.textContent = comment.author; // Safe

  const textEl = document.createElement('p');
  textEl.textContent = comment.text; // Safe

  commentEl.appendChild(authorEl);
  commentEl.appendChild(textEl);

  document.getElementById('comments').appendChild(commentEl);
}

// 3. Sanitize HTML with DOMPurify
import DOMPurify from 'dompurify';

function displayRichContent(html) {
  const clean = DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p'],
    ALLOWED_ATTR: ['href'],
  });

  document.getElementById('content').innerHTML = clean;
}

// Usage
const userHTML = '<p>Hello <script>alert("XSS")</script></p>';
displayRichContent(userHTML); // Script tag removed

// 4. Escape user input for HTML context
function escapeHtml(unsafe) {
  return unsafe
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;');
}

// Usage
const userText = '<script>alert("XSS")</script>';
const safe = escapeHtml(userText);
// Result: &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;

// 5. Validate and sanitize URLs
function isSafeUrl(url) {
  try {
    const parsed = new URL(url);
    // Only allow http/https
    return ['http:', 'https:'].includes(parsed.protocol);
  } catch {
    return false;
  }
}

function createSafeLink(url, text) {
  if (!isSafeUrl(url)) {
    console.warn('Unsafe URL blocked:', url);
    return document.createTextNode(text);
  }

  const link = document.createElement('a');
  link.href = url;
  link.textContent = text;
  link.rel = 'noopener noreferrer'; // Security: prevent window.opener access

  return link;
}

// 6. CSRF protection with tokens
async function submitForm(data) {
  // Get CSRF token from meta tag
  const token = document.querySelector('meta[name="csrf-token"]')?.content;

  if (!token) {
    throw new Error('CSRF token not found');
  }

  const response = await fetch('/api/submit', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-CSRF-Token': token,
    },
    credentials: 'same-origin', // Include cookies
    body: JSON.stringify(data),
  });

  return response.json();
}

// HTML: <meta name="csrf-token" content="<%= csrf_token %>">

// 7. Input validation
function validateEmail(email) {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
}

function validateUsername(username) {
  // Only alphanumeric and underscore, 3-20 characters
  const regex = /^[a-zA-Z0-9_]{3,20}$/;
  return regex.test(username);
}

function sanitizeInput(input, maxLength = 100) {
  // Remove control characters and trim
  return input
    .replace(/[\x00-\x1F\x7F]/g, '')
    .trim()
    .slice(0, maxLength);
}

// 8. Prevent clickjacking
// Server-side header: X-Frame-Options: DENY
// Or use CSP: Content-Security-Policy: frame-ancestors 'none'

// Client-side check
if (window.top !== window.self) {
  // Page is in an iframe
  document.body.innerHTML = 'This page cannot be displayed in an iframe';
}

// 9. Secure cookie handling
// Server sets HTTP-only cookies
// Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict

// Client can't access HTTP-only cookies
// But can read non-HTTP-only cookies
function getCookie(name) {
  const cookies = document.cookie.split(';');
  for (let cookie of cookies) {
    const [key, value] = cookie.trim().split('=');
    if (key === name) {
      return decodeURIComponent(value);
    }
  }
  return null;
}

// 10. Content Security Policy (CSP)
/*
Server-side header:
Content-Security-Policy:
  default-src 'self';
  script-src 'self' https://trusted-cdn.com;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  font-src 'self' https://fonts.gstatic.com;
  connect-src 'self' https://api.example.com;
  frame-ancestors 'none';
*/

// 11. Subresource Integrity (SRI)
/*
<script
  src="https://cdn.example.com/library.js"
  integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
  crossorigin="anonymous">
</script>
*/

// 12. Rate limiting on client
class RateLimiter {
  constructor(maxRequests, windowMs) {
    this.maxRequests = maxRequests;
    this.windowMs = windowMs;
    this.requests = [];
  }

  canMakeRequest() {
    const now = Date.now();
    this.requests = this.requests.filter(time => now - time < this.windowMs);

    if (this.requests.length < this.maxRequests) {
      this.requests.push(now);
      return true;
    }

    return false;
  }
}

// Usage
const limiter = new RateLimiter(5, 60000); // 5 requests per minute

async function makeApiCall() {
  if (!limiter.canMakeRequest()) {
    alert('Too many requests. Please wait.');
    return;
  }

  await fetch('/api/data');
}
1 file · javascript Explain with highlit

Front-end security protects users from malicious attacks. I prevent Cross-Site Scripting (XSS) by sanitizing user input and using textContent instead of innerHTML. Content Security Policy (CSP) headers restrict resource loading to trusted sources. Cross-Site Request Forgery (CSRF) tokens validate form submissions. HTTP-only cookies prevent JavaScript access to sensitive data. Input validation checks data on both client and server. The DOMPurify library safely sanitizes HTML. Escaping user input prevents script injection. HTTPS ensures encrypted data transmission. Understanding security vulnerabilities protects user data and builds trust.


Related snips

ruby
payload = {
  sub: user.id,
  iss: 'https://auth.example.com',
  aud: 'codesnips-api',
  exp: 15.minutes.from_now.to_i,
  iat: Time.now.to_i,

JWT issuance and verification without common footguns

jwt authentication api
by Kai Nakamura 2 tabs
javascript
// Creating a Promise
const myPromise = new Promise((resolve, reject) => {
  const success = true;

  setTimeout(() => {
    if (success) {

Promises and async/await patterns for asynchronous JavaScript

javascript promises async-await
by Alex Chang 1 tab
javascript
import { Controller } from "@hotwired/stimulus"
import Mousetrap from "mousetrap"

export default class extends Controller {
  connect() {
    // Global shortcuts

Keyboard shortcuts with Stimulus and Mousetrap

stimulus javascript ux
by Jordan Lee 2 tabs
bash
#!/usr/bin/env bash
set -euo pipefail

export VAULT_ADDR="https://vault.internal:8200"
export VAULT_TOKEN="${VAULT_TOKEN:?missing VAULT_TOKEN}"

Secrets management with environment isolation and Vault

secrets-management vault environment-variables
by Kai Nakamura 1 tab
typescript
import { randomBytes, createHash } from "crypto";
import jwt from "jsonwebtoken";
import { RefreshTokenStore } from "./store";

const ACCESS_SECRET = process.env.ACCESS_SECRET!;
const ACCESS_TTL = "15m";

JWT access + refresh token rotation (conceptual)

security node jwt
by codesnips 3 tabs
javascript
// Get canvas and context
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

// 1. Basic shapes
// Rectangle (filled)

Canvas API for graphics and animations

canvas javascript graphics
by Alex Chang 1 tab

Share this code

Here's the card — post it anywhere.

Front-end security - XSS and CSRF prevention — share card
Link copied