Frontend Security Implementation Practices

1. XSS Prevention DOMPurify Sanitization

Attack Type Prevention Method Implementation Example
Stored XSS Sanitize on input/output DOMPurify, validator.js Clean user-generated content
Reflected XSS Escape user input Template literals, React escape URL parameters, search queries
DOM-based XSS Avoid dangerous APIs Avoid innerHTML, eval, document.write Use textContent, createElement
CSP Headers Restrict script sources Content-Security-Policy header Prevent inline scripts
HTTP-only Cookies Prevent JS access Set HttpOnly flag Protect session tokens
React dangerouslySetInnerHTML Sanitize before use DOMPurify + dangerouslySetInnerHTML Rich text editors

Example: XSS prevention with DOMPurify

// Install DOMPurify
npm install dompurify
npm install --save-dev @types/dompurify

// Sanitize user input with DOMPurify
import DOMPurify from 'dompurify';

function SafeHTML({ html }) {
  const cleanHTML = DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
    ALLOWED_ATTR: ['href', 'title', 'target'],
  });

  return (
    <div
      dangerouslySetInnerHTML={{ __html: cleanHTML }}
    />
  );
}

// Usage
const userContent = '<script>alert("XSS")</script><p>Safe content</p>';
<SafeHTML html={userContent} />
// Output: <p>Safe content</p> (script removed)

// React custom hook for sanitization
import { useMemo } from 'react';
import DOMPurify from 'dompurify';

function useSanitizedHTML(html: string) {
  return useMemo(() => DOMPurify.sanitize(html), [html]);
}

// Usage
function RichTextDisplay({ content }) {
  const cleanContent = useSanitizedHTML(content);
  
  return <div dangerouslySetInnerHTML={{ __html: cleanContent }} />;
}

// Strict sanitization (remove all HTML)
const strictClean = DOMPurify.sanitize(html, { ALLOWED_TAGS: [] });

// Custom sanitization with hooks
DOMPurify.addHook('afterSanitizeAttributes', (node) => {
  // Force all links to open in new tab
  if (node.tagName === 'A') {
    node.setAttribute('target', '_blank');
    node.setAttribute('rel', 'noopener noreferrer');
  }
});

// Avoid dangerous DOM APIs
// ❌ Dangerous - XSS vulnerable
element.innerHTML = userInput;
eval(userInput);
document.write(userInput);
new Function(userInput)();

// ✅ Safe alternatives
element.textContent = userInput;
element.innerText = userInput;
const node = document.createTextNode(userInput);
element.appendChild(node);

// React automatically escapes
function SafeComponent({ userInput }) {
  // React escapes this automatically
  return <div>{userInput}</div>;
}

// Validate and sanitize URL inputs
import validator from 'validator';

function validateURL(url: string) {
  if (!validator.isURL(url, { protocols: ['http', 'https'] })) {
    throw new Error('Invalid URL');
  }
  
  // Additional check for javascript: protocol
  if (url.toLowerCase().startsWith('javascript:')) {
    throw new Error('JavaScript URLs not allowed');
  }
  
  return url;
}

// Safe link component
function SafeLink({ href, children }) {
  const [isValid, setIsValid] = useState(false);

  useEffect(() => {
    try {
      validateURL(href);
      setIsValid(true);
    } catch {
      setIsValid(false);
    }
  }, [href]);

  if (!isValid) {
    return <span>{children}</span>;
  }

  return (
    <a
      href={href}
      target="_blank"
      rel="noopener noreferrer"
    >
      {children}
    </a>
  );
}

// Content Security Policy in Next.js
// next.config.js
const ContentSecurityPolicy = `
  default-src 'self';
  script-src 'self' 'unsafe-eval' 'unsafe-inline';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  font-src 'self' data:;
  connect-src 'self' https://api.example.com;
  frame-ancestors 'none';
`;

module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: ContentSecurityPolicy.replace(/\s{2,}/g, ' ').trim(),
          },
          {
            key: 'X-Frame-Options',
            value: 'DENY',
          },
          {
            key: 'X-Content-Type-Options',
            value: 'nosniff',
          },
          {
            key: 'Referrer-Policy',
            value: 'strict-origin-when-cross-origin',
          },
        ],
      },
    ];
  },
};

// Server-side sanitization (Node.js)
const express = require('express');
const { body, validationResult } = require('express-validator');
const DOMPurify = require('isomorphic-dompurify');

app.post('/comment',
  body('content').trim().escape(),
  (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    const cleanContent = DOMPurify.sanitize(req.body.content);
    // Save cleanContent to database
  }
);

// Vue 3 safe rendering
<template>
  <!-- Safe by default -->
  <div>{{ userInput }}</div>
  
  <!-- Dangerous - sanitize first -->
  <div v-html="sanitizedHTML"></div>
</template>

<script setup>
import DOMPurify from 'dompurify';
import { computed } from 'vue';

const props = defineProps(['userInput']);

const sanitizedHTML = computed(() => 
  DOMPurify.sanitize(props.userInput)
);
</script>

// Angular safe HTML pipe
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import DOMPurify from 'dompurify';

@Pipe({ name: 'safeHtml' })
export class SafeHtmlPipe implements PipeTransform {
  constructor(private sanitizer: DomSanitizer) {}

  transform(html: string): SafeHtml {
    const cleanHTML = DOMPurify.sanitize(html);
    return this.sanitizer.bypassSecurityTrustHtml(cleanHTML);
  }
}

// Usage
<div [innerHTML]="userContent | safeHtml"></div>

2. CSRF Protection SameSite Cookies

Protection Method Implementation Description Browser Support
SameSite Cookie Set-Cookie: SameSite=Strict Prevent cross-site cookie sending All modern browsers
CSRF Token Synchronizer token pattern Unique token per session/request Universal
Double Submit Cookie Token in cookie + request header Compare values server-side Universal
Custom Header X-Requested-With: XMLHttpRequest CORS prevents cross-origin AJAX requests only
Origin Header Check Verify Origin/Referer header Validate request source Server-side validation

Example: CSRF protection implementation

// Set SameSite cookies (Express.js)
const express = require('express');
const session = require('express-session');

app.use(session({
  secret: process.env.SESSION_SECRET,
  cookie: {
    httpOnly: true,
    secure: true, // HTTPS only
    sameSite: 'strict', // or 'lax' for better UX
    maxAge: 24 * 60 * 60 * 1000, // 24 hours
  },
}));

// SameSite options:
// - Strict: Cookie not sent on any cross-site request
// - Lax: Cookie sent on top-level navigation (GET)
// - None: Cookie sent on all requests (requires Secure flag)

// CSRF Token with csurf middleware
const csrf = require('csurf');
const cookieParser = require('cookie-parser');

app.use(cookieParser());
app.use(csrf({ cookie: true }));

// Send token to client
app.get('/form', (req, res) => {
  res.render('form', { csrfToken: req.csrfToken() });
});

// Validate token on POST
app.post('/submit', (req, res) => {
  // Token automatically validated by middleware
  // If invalid, 403 error is thrown
  res.json({ success: true });
});

// React CSRF token implementation
// Store token in meta tag (server-rendered)
<head>
  <meta name="csrf-token" content="{{ csrfToken }}" />
</head>

// Read and include in requests
function getCSRFToken() {
  const meta = document.querySelector('meta[name="csrf-token"]');
  return meta ? meta.getAttribute('content') : '';
}

// Axios interceptor for CSRF token
import axios from 'axios';

axios.interceptors.request.use((config) => {
  const token = getCSRFToken();
  if (token) {
    config.headers['X-CSRF-Token'] = token;
  }
  return config;
});

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

  return response.json();
}

// React hook for CSRF protection
function useCSRFToken() {
  const [token, setToken] = useState('');

  useEffect(() => {
    // Fetch token from server
    fetch('/api/csrf-token')
      .then(res => res.json())
      .then(data => setToken(data.token));
  }, []);

  return token;
}

// Usage
function SecureForm() {
  const csrfToken = useCSRFToken();

  const handleSubmit = async (data) => {
    await fetch('/api/submit', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-CSRF-Token': csrfToken,
      },
      body: JSON.stringify(data),
    });
  };

  return <form onSubmit={handleSubmit}>...</form>;
}

// Next.js API route with CSRF protection
// pages/api/submit.js
import { csrf } from 'lib/csrf';

export default csrf(async (req, res) => {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  // Token validated by middleware
  // Process request
  res.json({ success: true });
});

// Custom CSRF middleware
// lib/csrf.js
import { randomBytes } from 'crypto';

const tokens = new Map();

export function generateCSRFToken(sessionId) {
  const token = randomBytes(32).toString('hex');
  tokens.set(sessionId, token);
  return token;
}

export function validateCSRFToken(sessionId, token) {
  const storedToken = tokens.get(sessionId);
  return storedToken && storedToken === token;
}

export function csrf(handler) {
  return async (req, res) => {
    if (req.method === 'POST') {
      const token = req.headers['x-csrf-token'];
      const sessionId = req.cookies.sessionId;

      if (!validateCSRFToken(sessionId, token)) {
        return res.status(403).json({ error: 'Invalid CSRF token' });
      }
    }

    return handler(req, res);
  };
}

// Double Submit Cookie pattern
app.post('/api/submit', (req, res) => {
  const cookieToken = req.cookies.csrfToken;
  const headerToken = req.headers['x-csrf-token'];

  if (!cookieToken || cookieToken !== headerToken) {
    return res.status(403).json({ error: 'CSRF validation failed' });
  }

  // Process request
});

// Origin header validation
app.use((req, res, next) => {
  const origin = req.headers.origin || req.headers.referer;
  const allowedOrigins = [
    'https://example.com',
    'https://www.example.com',
  ];

  if (req.method !== 'GET' && req.method !== 'HEAD') {
    if (!origin || !allowedOrigins.some(allowed => origin.startsWith(allowed))) {
      return res.status(403).json({ error: 'Invalid origin' });
    }
  }

  next();
});

// Custom header check (AJAX only)
app.use((req, res, next) => {
  if (req.method !== 'GET' && req.method !== 'HEAD') {
    const xhr = req.headers['x-requested-with'] === 'XMLHttpRequest';
    if (!xhr) {
      return res.status(403).json({ error: 'Invalid request' });
    }
  }
  next();
});

// React Context for CSRF token
const CSRFContext = createContext('');

export function CSRFProvider({ children }) {
  const [token, setToken] = useState('');

  useEffect(() => {
    fetch('/api/csrf-token')
      .then(res => res.json())
      .then(data => setToken(data.token));
  }, []);

  return (
    <CSRFContext.Provider value={token}>
      {children}
    </CSRFContext.Provider>
  );
}

export const useCSRF = () => useContext(CSRFContext);

3. Content Security Policy Headers

Directive Purpose Example Value Protection
default-src Fallback for all directives 'self' Restrict all resources
script-src JavaScript sources 'self' 'nonce-{random}' Prevent inline scripts
style-src CSS sources 'self' 'unsafe-inline' Control stylesheets
img-src Image sources 'self' data: https: Restrict image loading
connect-src XHR, WebSocket, fetch 'self' https://api.example.com Control API endpoints
frame-ancestors Embedding pages 'none' Prevent clickjacking

Example: Comprehensive CSP implementation

// Strict CSP policy
Content-Security-Policy: 
  default-src 'self';
  script-src 'self' 'nonce-{random}';
  style-src 'self' 'nonce-{random}';
  img-src 'self' data: https:;
  font-src 'self' data:;
  connect-src 'self' https://api.example.com;
  frame-src 'none';
  frame-ancestors 'none';
  base-uri 'self';
  form-action 'self';
  upgrade-insecure-requests;
  block-all-mixed-content;

// Express.js CSP middleware
const helmet = require('helmet');

app.use(
  helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", "data:", "https:"],
      connectSrc: ["'self'", "https://api.example.com"],
      fontSrc: ["'self'", "data:"],
      objectSrc: ["'none'"],
      mediaSrc: ["'self'"],
      frameSrc: ["'none'"],
      frameAncestors: ["'none'"],
      upgradeInsecureRequests: [],
    },
  })
);

// Next.js CSP with nonce
// next.config.js
const crypto = require('crypto');

module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'Content-Security-Policy',
            value: generateCSP(),
          },
        ],
      },
    ];
  },
};

function generateCSP() {
  const nonce = crypto.randomBytes(16).toString('base64');
  
  const csp = {
    'default-src': ["'self'"],
    'script-src': ["'self'", `'nonce-${nonce}'`, "'strict-dynamic'"],
    'style-src': ["'self'", "'unsafe-inline'"],
    'img-src': ["'self'", "data:", "https:"],
    'font-src': ["'self'", "data:"],
    'connect-src': ["'self'", "https://api.example.com"],
    'frame-ancestors': ["'none'"],
    'base-uri': ["'self'"],
    'form-action': ["'self'"],
  };

  return Object.entries(csp)
    .map(([key, values]) => `${key} ${values.join(' ')}`)
    .join('; ');
}

// Use nonce in scripts
<script nonce="{nonce}">
  console.log('Allowed script');
</script>

// React Helmet for CSP
import { Helmet } from 'react-helmet';

function App() {
  return (
    <>
      <Helmet>
        <meta
          httpEquiv="Content-Security-Policy"
          content="default-src 'self'; script-src 'self' 'unsafe-inline'"
        />
      </Helmet>
      <div>App content</div>
    </>
  );
}

// CSP Report-Only mode (testing)
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report

// CSP violation reporting endpoint
app.post('/csp-report', express.json({ type: 'application/csp-report' }), (req, res) => {
  console.log('CSP Violation:', req.body);
  // Log to monitoring service
  res.status(204).end();
});

// CSP violation handler (client-side)
document.addEventListener('securitypolicyviolation', (e) => {
  console.error('CSP Violation:', {
    blockedURI: e.blockedURI,
    violatedDirective: e.violatedDirective,
    originalPolicy: e.originalPolicy,
  });

  // Send to analytics
  fetch('/api/csp-violation', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      blockedURI: e.blockedURI,
      violatedDirective: e.violatedDirective,
    }),
  });
});

// Next.js 13 App Router with CSP
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
  
  const cspHeader = `
    default-src 'self';
    script-src 'self' 'nonce-${nonce}' 'strict-dynamic';
    style-src 'self' 'nonce-${nonce}';
    img-src 'self' blob: data:;
    font-src 'self';
    object-src 'none';
    base-uri 'self';
    form-action 'self';
    frame-ancestors 'none';
    upgrade-insecure-requests;
  `.replace(/\s{2,}/g, ' ').trim();

  const requestHeaders = new Headers(request.headers);
  requestHeaders.set('x-nonce', nonce);
  requestHeaders.set('Content-Security-Policy', cspHeader);

  const response = NextResponse.next({
    request: {
      headers: requestHeaders,
    },
  });

  response.headers.set('Content-Security-Policy', cspHeader);
  return response;
}

// Vite CSP plugin
// vite.config.ts
import { defineConfig } from 'vite';
import { createHtmlPlugin } from 'vite-plugin-html';

export default defineConfig({
  plugins: [
    createHtmlPlugin({
      inject: {
        data: {
          csp: "default-src 'self'; script-src 'self' 'unsafe-inline'",
        },
      },
    }),
  ],
  server: {
    headers: {
      'Content-Security-Policy': "default-src 'self'; script-src 'self'",
    },
  },
});

// Additional security headers
app.use((req, res, next) => {
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('X-XSS-Protection', '1; mode=block');
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
  res.setHeader('Permissions-Policy', 'geolocation=(), microphone=()');
  next();
});

4. JWT Token HttpOnly Storage

Storage Method Security Level Pros Cons
HttpOnly Cookie 🟢 High XSS protection, automatic sending CSRF risk (mitigated with SameSite)
localStorage 🔴 Low Easy access, persists XSS vulnerable, no auto-sending
sessionStorage 🟡 Medium Tab-scoped, clears on close XSS vulnerable
Memory (React state) 🟢 High XSS resistant, cleared on refresh Lost on refresh, needs refresh token
Secure Cookie + CSRF Token 🟢 Highest XSS + CSRF protection Complex implementation

Example: Secure JWT token storage and handling

// ✅ RECOMMENDED: HttpOnly cookie with refresh token
// Server-side (Express.js)
const jwt = require('jsonwebtoken');

// Login endpoint
app.post('/auth/login', async (req, res) => {
  const { email, password } = req.body;
  
  // Validate credentials
  const user = await validateUser(email, password);
  
  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // Generate access token (short-lived)
  const accessToken = jwt.sign(
    { userId: user.id, email: user.email },
    process.env.ACCESS_TOKEN_SECRET,
    { expiresIn: '15m' }
  );

  // Generate refresh token (long-lived)
  const refreshToken = jwt.sign(
    { userId: user.id },
    process.env.REFRESH_TOKEN_SECRET,
    { expiresIn: '7d' }
  );

  // Store refresh token in database
  await storeRefreshToken(user.id, refreshToken);

  // Set HttpOnly cookies
  res.cookie('accessToken', accessToken, {
    httpOnly: true,
    secure: true, // HTTPS only
    sameSite: 'strict',
    maxAge: 15 * 60 * 1000, // 15 minutes
  });

  res.cookie('refreshToken', refreshToken, {
    httpOnly: true,
    secure: true,
    sameSite: 'strict',
    maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
    path: '/auth/refresh', // Only sent to refresh endpoint
  });

  res.json({ success: true, user: { id: user.id, email: user.email } });
});

// Verify token middleware
function authenticateToken(req, res, next) {
  const token = req.cookies.accessToken;

  if (!token) {
    return res.status(401).json({ error: 'No token provided' });
  }

  jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
    if (err) {
      return res.status(403).json({ error: 'Invalid token' });
    }
    req.user = user;
    next();
  });
}

// Protected route
app.get('/api/profile', authenticateToken, (req, res) => {
  res.json({ user: req.user });
});

// Refresh token endpoint
app.post('/auth/refresh', async (req, res) => {
  const refreshToken = req.cookies.refreshToken;

  if (!refreshToken) {
    return res.status(401).json({ error: 'No refresh token' });
  }

  // Verify refresh token
  jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, async (err, user) => {
    if (err) {
      return res.status(403).json({ error: 'Invalid refresh token' });
    }

    // Check if token exists in database
    const storedToken = await getRefreshToken(user.userId);
    if (storedToken !== refreshToken) {
      return res.status(403).json({ error: 'Token revoked' });
    }

    // Generate new access token
    const newAccessToken = jwt.sign(
      { userId: user.userId },
      process.env.ACCESS_TOKEN_SECRET,
      { expiresIn: '15m' }
    );

    res.cookie('accessToken', newAccessToken, {
      httpOnly: true,
      secure: true,
      sameSite: 'strict',
      maxAge: 15 * 60 * 1000,
    });

    res.json({ success: true });
  });
});

// Logout endpoint
app.post('/auth/logout', async (req, res) => {
  const refreshToken = req.cookies.refreshToken;
  
  // Remove from database
  if (refreshToken) {
    await revokeRefreshToken(refreshToken);
  }

  // Clear cookies
  res.clearCookie('accessToken');
  res.clearCookie('refreshToken');
  
  res.json({ success: true });
});

// Client-side (React)
// Auth context with automatic refresh
import { createContext, useContext, useState, useEffect } from 'react';

const AuthContext = createContext(null);

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // Check authentication on mount
    checkAuth();

    // Set up automatic token refresh
    const interval = setInterval(() => {
      refreshAccessToken();
    }, 14 * 60 * 1000); // Refresh every 14 minutes

    return () => clearInterval(interval);
  }, []);

  const checkAuth = async () => {
    try {
      const response = await fetch('/api/profile', {
        credentials: 'include', // Include cookies
      });

      if (response.ok) {
        const data = await response.json();
        setUser(data.user);
      }
    } catch (error) {
      console.error('Auth check failed:', error);
    } finally {
      setLoading(false);
    }
  };

  const refreshAccessToken = async () => {
    try {
      await fetch('/auth/refresh', {
        method: 'POST',
        credentials: 'include',
      });
    } catch (error) {
      console.error('Token refresh failed:', error);
      setUser(null);
    }
  };

  const login = async (email, password) => {
    const response = await fetch('/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password }),
      credentials: 'include',
    });

    if (response.ok) {
      const data = await response.json();
      setUser(data.user);
      return true;
    }

    return false;
  };

  const logout = async () => {
    await fetch('/auth/logout', {
      method: 'POST',
      credentials: 'include',
    });
    setUser(null);
  };

  return (
    <AuthContext.Provider value={{ user, login, logout, loading }}>
      {children}
    </AuthContext.Provider>
  );
}

export const useAuth = () => useContext(AuthContext);

// Axios interceptor for automatic token refresh
import axios from 'axios';

axios.defaults.withCredentials = true;

let isRefreshing = false;
let failedQueue = [];

const processQueue = (error, token = null) => {
  failedQueue.forEach(prom => {
    if (error) {
      prom.reject(error);
    } else {
      prom.resolve(token);
    }
  });
  
  failedQueue = [];
};

axios.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;

    if (error.response?.status === 401 && !originalRequest._retry) {
      if (isRefreshing) {
        return new Promise((resolve, reject) => {
          failedQueue.push({ resolve, reject });
        }).then(() => axios(originalRequest));
      }

      originalRequest._retry = true;
      isRefreshing = true;

      try {
        await axios.post('/auth/refresh');
        processQueue(null);
        return axios(originalRequest);
      } catch (refreshError) {
        processQueue(refreshError);
        // Redirect to login
        window.location.href = '/login';
        return Promise.reject(refreshError);
      } finally {
        isRefreshing = false;
      }
    }

    return Promise.reject(error);
  }
);

// Next.js middleware for token validation
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { jwtVerify } from 'jose';

export async function middleware(request: NextRequest) {
  const token = request.cookies.get('accessToken')?.value;

  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  try {
    await jwtVerify(
      token,
      new TextEncoder().encode(process.env.ACCESS_TOKEN_SECRET!)
    );
    return NextResponse.next();
  } catch {
    return NextResponse.redirect(new URL('/login', request.url));
  }
}

export const config = {
  matcher: ['/dashboard/:path*', '/profile/:path*'],
};

5. Input Validation Joi Yup Schemas

Library Ecosystem Features Use Case
Joi Node.js backend Rich schema, custom messages API validation
Yup React frontend Schema validation, TypeScript Form validation
Zod Full-stack TypeScript Type inference, parse tRPC, type-safe APIs
express-validator Express.js Middleware chain Request validation
class-validator NestJS, TypeScript Decorator-based DTO validation
Ajv JSON Schema Fast, standard-based Configuration validation

Example: Comprehensive input validation

// Joi validation (Backend/Node.js)
npm install joi

const Joi = require('joi');

// Define schema
const userSchema = Joi.object({
  username: Joi.string()
    .alphanum()
    .min(3)
    .max(30)
    .required()
    .messages({
      'string.min': 'Username must be at least 3 characters',
      'any.required': 'Username is required',
    }),
  
  email: Joi.string()
    .email({ minDomainSegments: 2 })
    .required(),
  
  password: Joi.string()
    .pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/)
    .required()
    .messages({
      'string.pattern.base': 'Password must contain uppercase, lowercase, number, and special character',
    }),
  
  age: Joi.number()
    .integer()
    .min(18)
    .max(120)
    .optional(),
  
  website: Joi.string()
    .uri()
    .optional(),
  
  birthdate: Joi.date()
    .iso()
    .max('now')
    .optional(),
  
  role: Joi.string()
    .valid('user', 'admin', 'moderator')
    .default('user'),
});

// Validate in Express route
app.post('/api/users', async (req, res) => {
  try {
    const value = await userSchema.validateAsync(req.body, {
      abortEarly: false, // Return all errors
    });
    
    // Value is validated and sanitized
    const user = await createUser(value);
    res.json(user);
  } catch (error) {
    if (error.isJoi) {
      return res.status(400).json({
        errors: error.details.map(detail => ({
          field: detail.path.join('.'),
          message: detail.message,
        })),
      });
    }
    res.status(500).json({ error: 'Internal server error' });
  }
});

// Yup validation (Frontend/React)
npm install yup

import * as yup from 'yup';

// Define schema
const loginSchema = yup.object({
  email: yup
    .string()
    .email('Invalid email address')
    .required('Email is required'),
  
  password: yup
    .string()
    .min(8, 'Password must be at least 8 characters')
    .matches(
      /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/,
      'Password must contain uppercase, lowercase, and number'
    )
    .required('Password is required'),
  
  rememberMe: yup.boolean().default(false),
});

// React Hook Form with Yup
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';

function LoginForm() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm({
    resolver: yupResolver(loginSchema),
  });

  const onSubmit = async (data) => {
    // Data is validated
    await login(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('email')} />
      {errors.email && <span>{errors.email.message}</span>}

      <input type="password" {...register('password')} />
      {errors.password && <span>{errors.password.message}</span>}

      <button type="submit">Login</button>
    </form>
  );
}

// Zod validation (Full-stack TypeScript)
npm install zod

import { z } from 'zod';

// Define schema with type inference
const userSchema = z.object({
  username: z.string().min(3).max(30),
  email: z.string().email(),
  password: z.string().min(8),
  age: z.number().int().min(18).optional(),
  role: z.enum(['user', 'admin', 'moderator']).default('user'),
});

// TypeScript type automatically inferred
type User = z.infer<typeof userSchema>;

// Validate
const result = userSchema.safeParse(data);

if (!result.success) {
  console.error(result.error.issues);
} else {
  const user: User = result.data;
}

// tRPC with Zod
import { router, publicProcedure } from './trpc';

export const userRouter = router({
  create: publicProcedure
    .input(userSchema)
    .mutation(async ({ input }) => {
      // input is typed as User
      return await createUser(input);
    }),
});

// express-validator
npm install express-validator

const { body, validationResult } = require('express-validator');

app.post('/api/users',
  // Validation middleware
  body('email').isEmail().normalizeEmail(),
  body('password').isLength({ min: 8 }),
  body('username').trim().isLength({ min: 3, max: 30 }).escape(),
  
  // Handler
  (req, res) => {
    const errors = validationResult(req);
    
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    
    // Process validated data
    const user = createUser(req.body);
    res.json(user);
  }
);

// Custom validation with Yup
const passwordMatchSchema = yup.object({
  password: yup.string().required(),
  confirmPassword: yup
    .string()
    .oneOf([yup.ref('password')], 'Passwords must match')
    .required(),
});

// Conditional validation
const addressSchema = yup.object({
  country: yup.string().required(),
  state: yup.string().when('country', {
    is: 'US',
    then: (schema) => schema.required('State is required for US'),
    otherwise: (schema) => schema.optional(),
  }),
});

// Array validation
const orderSchema = yup.object({
  items: yup.array().of(
    yup.object({
      productId: yup.string().required(),
      quantity: yup.number().positive().integer().required(),
      price: yup.number().positive().required(),
    })
  ).min(1, 'At least one item required'),
});

// File validation
const uploadSchema = yup.object({
  file: yup
    .mixed()
    .required('File is required')
    .test('fileSize', 'File too large', (value) => {
      return value && value.size <= 5000000; // 5MB
    })
    .test('fileType', 'Invalid file type', (value) => {
      return value && ['image/jpeg', 'image/png'].includes(value.type);
    }),
});

// Sanitization helpers
function sanitizeInput(input) {
  return input
    .trim()
    .replace(/[<>"'\/]/g, (char) => {
      const entities = {
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&#x27;',
        '/': '&#x2F;',
      };
      return entities[char];
    });
}

// SQL injection prevention with parameterized queries
// ❌ Vulnerable
const query = `SELECT * FROM users WHERE email = '${email}'`;

// ✅ Safe
const query = 'SELECT * FROM users WHERE email = ?';
db.query(query, [email]);

// NoSQL injection prevention
// ❌ Vulnerable
const user = await User.findOne({ email: req.body.email });

// ✅ Safe with validation
const emailSchema = yup.string().email().required();
const email = await emailSchema.validate(req.body.email);
const user = await User.findOne({ email });

6. HTTPS Certificate Pinning Security

Security Measure Implementation Purpose Level
HTTPS/TLS SSL/TLS certificate Encrypt data in transit Essential
Certificate Pinning Pin public key/certificate Prevent MITM attacks Advanced
HSTS Strict-Transport-Security header Force HTTPS Recommended
TLS 1.3 Modern protocol version Latest encryption standards Best practice
Certificate Transparency CT logs monitoring Detect rogue certificates Advanced
Secure Ciphers Strong cipher suites Prevent weak encryption Essential

Example: HTTPS and security configuration

// HTTPS server setup (Node.js)
const https = require('https');
const fs = require('fs');
const express = require('express');

const app = express();

const options = {
  key: fs.readFileSync('/path/to/private-key.pem'),
  cert: fs.readFileSync('/path/to/certificate.pem'),
  ca: fs.readFileSync('/path/to/ca-certificate.pem'),
  
  // TLS options
  minVersion: 'TLSv1.3',
  ciphers: [
    'TLS_AES_128_GCM_SHA256',
    'TLS_AES_256_GCM_SHA384',
    'TLS_CHACHA20_POLY1305_SHA256',
  ].join(':'),
  honorCipherOrder: true,
};

https.createServer(options, app).listen(443);

// HSTS (HTTP Strict Transport Security)
app.use((req, res, next) => {
  res.setHeader(
    'Strict-Transport-Security',
    'max-age=31536000; includeSubDomains; preload'
  );
  next();
});

// Redirect HTTP to HTTPS
const http = require('http');

http.createServer((req, res) => {
  res.writeHead(301, {
    Location: `https://${req.headers.host}${req.url}`,
  });
  res.end();
}).listen(80);

// Next.js security headers
// next.config.js
module.exports = {
  async headers() {
    return [
      {
        source: '/:path*',
        headers: [
          {
            key: 'Strict-Transport-Security',
            value: 'max-age=63072000; includeSubDomains; preload',
          },
          {
            key: 'X-Content-Type-Options',
            value: 'nosniff',
          },
          {
            key: 'X-Frame-Options',
            value: 'SAMEORIGIN',
          },
          {
            key: 'X-XSS-Protection',
            value: '1; mode=block',
          },
          {
            key: 'Referrer-Policy',
            value: 'strict-origin-when-cross-origin',
          },
          {
            key: 'Permissions-Policy',
            value: 'camera=(), microphone=(), geolocation=()',
          },
        ],
      },
    ];
  },
};

// Helmet.js for Express security headers
npm install helmet

const helmet = require('helmet');

app.use(helmet({
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true,
  },
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'"],
    },
  },
  frameguard: {
    action: 'deny',
  },
}));

// Certificate pinning (React Native / Mobile)
// Note: Not commonly used in web browsers, mainly mobile apps
const fetch = require('node-fetch');
const https = require('https');
const crypto = require('crypto');

const expectedFingerprint = 'AA:BB:CC:DD:EE:FF...';

const agent = new https.Agent({
  checkServerIdentity: (hostname, cert) => {
    const fingerprint = crypto
      .createHash('sha256')
      .update(cert.raw)
      .digest('hex')
      .toUpperCase()
      .match(/.{2}/g)
      .join(':');

    if (fingerprint !== expectedFingerprint) {
      throw new Error('Certificate fingerprint mismatch');
    }
  },
});

fetch('https://api.example.com', { agent });

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

// Generate SRI hash
const crypto = require('crypto');
const fs = require('fs');

function generateSRI(filePath) {
  const content = fs.readFileSync(filePath);
  const hash = crypto.createHash('sha384').update(content).digest('base64');
  return `sha384-${hash}`;
}

// React component for SRI
function SecureScript({ src, integrity }) {
  useEffect(() => {
    const script = document.createElement('script');
    script.src = src;
    script.integrity = integrity;
    script.crossOrigin = 'anonymous';
    document.body.appendChild(script);

    return () => {
      document.body.removeChild(script);
    };
  }, [src, integrity]);

  return null;
}

// Nginx SSL configuration
server {
  listen 443 ssl http2;
  server_name example.com;

  ssl_certificate /path/to/certificate.crt;
  ssl_certificate_key /path/to/private.key;

  # TLS settings
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
  ssl_prefer_server_ciphers on;

  # HSTS
  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

  # OCSP Stapling
  ssl_stapling on;
  ssl_stapling_verify on;
  ssl_trusted_certificate /path/to/ca.crt;

  # Security headers
  add_header X-Frame-Options "SAMEORIGIN" always;
  add_header X-Content-Type-Options "nosniff" always;
  add_header X-XSS-Protection "1; mode=block" always;
}

// Let's Encrypt certificate automation
npm install greenlock-express

const greenlock = require('greenlock-express');

greenlock
  .init({
    packageRoot: __dirname,
    configDir: './greenlock.d',
    maintainerEmail: 'admin@example.com',
    cluster: false,
  })
  .serve(app);

// Certificate monitoring
const https = require('https');
const tls = require('tls');

function checkCertificate(hostname) {
  return new Promise((resolve, reject) => {
    const socket = tls.connect(443, hostname, () => {
      const cert = socket.getPeerCertificate();
      
      if (!cert || !Object.keys(cert).length) {
        reject(new Error('No certificate'));
        return;
      }

      const validTo = new Date(cert.valid_to);
      const daysRemaining = Math.floor((validTo - new Date()) / (1000 * 60 * 60 * 24));

      resolve({
        subject: cert.subject,
        issuer: cert.issuer,
        validFrom: cert.valid_from,
        validTo: cert.valid_to,
        daysRemaining,
      });

      socket.end();
    });

    socket.on('error', reject);
  });
}

// Usage
checkCertificate('example.com').then(cert => {
  console.log(`Certificate expires in ${cert.daysRemaining} days`);
  
  if (cert.daysRemaining < 30) {
    console.warn('Certificate expiring soon!');
    // Send alert
  }
});

Frontend Security Best Practices Summary

  • XSS Prevention - Use DOMPurify for sanitization, avoid innerHTML/eval/document.write, implement CSP headers, escape user input automatically (React/Vue)
  • CSRF Protection - Set SameSite cookies (Strict/Lax), implement CSRF tokens, validate Origin/Referer headers, use custom headers for AJAX
  • Content Security Policy - Define strict CSP directives, use nonces for inline scripts, implement CSP violation reporting, prevent inline script execution
  • JWT Security - Store tokens in HttpOnly cookies, implement refresh token rotation, use short-lived access tokens (15min), automatic token refresh mechanism
  • Input Validation - Use Joi/Yup/Zod schemas, validate on both client and server, sanitize inputs, use parameterized queries for SQL, prevent injection attacks
  • HTTPS & TLS - Enforce HTTPS with HSTS headers, use TLS 1.3, strong cipher suites, certificate monitoring, Subresource Integrity for CDN resources
  • Defense in Depth - Layer multiple security controls, use helmet.js for Express, implement rate limiting, monitor security violations, regular security audits