In the modern web era, the frontend is no longer just a dumb presentation layer. It manages complex state, handles highly sensitive authentication tokens, and interfaces directly with dozens of APIs. Consequently, security is no longer just a backend concern. A single compromised frontend dependency or unescaped input can result in hijacked user sessions, leaked credentials, and severe financial damage.


Module 1: Cross-Site Scripting (XSS)

XSS occurs when an attacker tricks your application into executing malicious JavaScript in the victim's browser. If successful, the attacker can read local storage, steal cookies, and perform actions as the user.

While modern frameworks (React, Vue) automatically escape HTML variables (preventing basic injection), vulnerabilities frequently arise through third-party dependencies, improper use of dangerouslySetInnerHTML, or injecting user input into href attributes (javascript:alert(1)).

The Ultimate Defense: Content Security Policy (CSP)

A CSP is an HTTP header sent by your server that dictates exactly what resources the browser is allowed to load and execute. It is your strongest line of defense against XSS.

HTTP Response Headershttp
# A robust, strict CSP
Content-Security-Policy: 
  default-src 'self'; 
  script-src 'self' https://trusted-analytics.com;
  style-src 'self' 'unsafe-inline'; 
  img-src 'self' data: https://images.unsplash.com;
  object-src 'none';
  frame-ancestors 'none';

CSP Directives Explained

  • default-src 'self': If not explicitly defined, only allow resources originating from the same domain.
  • script-src: The most critical directive. Explicitly whitelist domains where JS can be executed. NEVER use 'unsafe-inline' for scripts, as it completely breaks XSS protection.
  • object-src 'none': Blocks deprecated plugins like Flash or Java applets which carry huge vulnerabilities.
  • frame-ancestors 'none': Prevents Clickjacking by disallowing your site to be embedded in an iframe on an attacker's domain.

Module 2: JWTs and the LocalStorage Trap

JSON Web Tokens (JWTs) are the standard for modern authentication. However, thousands of developers make the critical mistake of storing these tokens in localStorage or sessionStorage.

The Solution: Store tokens in HTTP-Only Cookies. An HTTP-Only cookie is sent automatically by the browser to the server on every request, but it is physically inaccessible to JavaScript via document.cookie.

server.js (Express Auth Route)javascript
// Sending the token to the client safely upon login
res.cookie('auth_token', jwtToken, {
  httpOnly: true,  // Protects against XSS (JS cannot read it)
  secure: process.env.NODE_ENV === 'production', // Only sent over HTTPS
  sameSite: 'strict', // Protects against CSRF (See Module 3)
  maxAge: 3600000 // 1 hour
});
res.status(200).json({ success: true });

Module 3: Defending against CSRF

If you use cookies for authentication, you introduce a new vulnerability: Cross-Site Request Forgery (CSRF). If a user is logged into your bank (bank.com), and they visit evil.com, the attacker can trigger a hidden POST request to bank.com/transfer. Because the browser automatically attaches the bank.com cookies, the transfer succeeds.

Modern browsers have largely solved this with the SameSite cookie attribute. Setting SameSite=Strict ensures the cookie is ONLY sent if the request originates from the exact same domain. If evil.com makes the request, the browser drops the cookie.

Legacy Defense (Still used for APIs): The Anti-CSRF Token. The server generates a cryptographically random token, sends it to the client (not in a cookie), and the client must manually attach it to an HTTP header (e.g., X-CSRF-Token) on all POST/PUT requests. evil.com cannot read this token due to the Same Origin Policy, so their forged requests fail validation.


Module 4: Mastering CORS

Cross-Origin Resource Sharing (CORS) is widely misunderstood. It is a browser mechanism, not a backend security wall. It prevents a script on domain-a.com from reading data from an API at domain-b.com.

When your frontend makes a complex request (like a POST with JSON), the browser first sends a preflight OPTIONS request to ask the server for permission.

server.jsjavascript
const cors = require('cors');

// DANGEROUS: Allowing any domain to read your API
app.use(cors({ origin: '*' }));

// SECURE: Explicitly whitelist trusted frontend origins
app.use(cors({
  origin: [
    'https://production-frontend.com', 
    'https://staging-frontend.com'
  ],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  credentials: true // Crucial: Allows the frontend to send cookies/auth headers
}));

Module 5: Essential Security Headers

Ensure your web server or edge proxy injects these standard headers on every response.

Headershttp
# HSTS: Forces the browser to ONLY connect via HTTPS for the next year
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

# Prevents browsers from guessing the MIME type, stopping MIME confusion attacks
X-Content-Type-Options: nosniff

# Controls how much referrer information is passed when linking away from your site
Referrer-Policy: strict-origin-when-cross-origin