Complete Security Stack for Your MERN App

Posted: 16-03-2025 | Views: 7
Complete Security Stack for Your MERN App

1️⃣ Prevent Direct API Access (Non-Browser Requests)

Many external tools (e.g., Postman, cURL) send requests without a real browser environment. Block them with:

User-Agent Whitelist (Only Allow Browsers)

Add this middleware in server.js:

app.use((req, res, next) => {
  const userAgent = req.get("User-Agent") || "";
  if (!userAgent.includes("Mozilla")) {
    return res.status(403).json({ message: "Forbidden: Only browser requests allowed." });
  }
  next();
});

🛡 Effect: Prevents requests from cURL, Postman, and automated bots.


Referer & Origin Check (Block Direct API Requests)

Ensure requests come from your frontend (not external tools).

app.use((req, res, next) => {
  const referer = req.get("Referer") || "";
  const origin = req.get("Origin") || "";

  if (!referer.includes("yourdomain.com") && !origin.includes("yourdomain.com")) {
    return res.status(403).json({ message: "Forbidden: Direct API access blocked." });
  }

  next();
});

🛡 Effect: Blocks API requests without a frontend (e.g., Postman, malicious scripts).


2️⃣ Implement Strong CSRF Protection

Use HTTP-Only Cookies for CSRF Token

Store CSRF tokens in cookies instead of localStorage to prevent JavaScript access.

📌 Install CSRF Package:

npm install csurf cookie-parser

📌 Update Express (server.js):

const cookieParser = require("cookie-parser");
const csrf = require("csurf");

app.use(cookieParser());

const csrfProtection = csrf({ cookie: true });

app.use(csrfProtection);

app.get("/csrf-token", (req, res) => {
  res.cookie("XSRF-TOKEN", req.csrfToken(), { httpOnly: true, secure: true, sameSite: "Strict" });
  res.json({ message: "CSRF token set" });
});

🔹 Frontend must include the token in all requests.


Frontend: Automatically Include CSRF Token

Modify React Axios setup (api.js):

import axios from "axios";

const api = axios.create({
  baseURL: "http://localhost:5000",
  withCredentials: true, // Include cookies automatically
});

api.interceptors.request.use(async (config) => {
  const csrfResponse = await api.get("/csrf-token");
  config.headers["X-CSRF-Token"] = csrfResponse.data.csrfToken;
  return config;
});

export default api;

🛡 Effect: External tools like Postman can’t send valid CSRF tokens.


3️⃣ Add Dynamic Browser Token (With Rotation)

Instead of storing a static token in localStorage, generate rotating tokens.

📌 Modify Backend (server.js):

app.use((req, res, next) => {
  const browserToken = req.cookies["browserToken"];

  if (!browserToken || browserToken.length < 10) {
    return res.status(403).json({ message: "Invalid Browser Access" });
  }

  next();
});

app.get("/browser-token", (req, res) => {
  const token = Math.random().toString(36).substring(2, 15);
  res.cookie("browserToken", token, { httpOnly: true, secure: true, sameSite: "Strict" });
  res.json({ message: "Browser token set" });
});

📌 Modify React (App.js):

import { useEffect } from "react";
import api from "./api"; // Axios instance

function App() {
  useEffect(() => {
    api.get("/browser-token"); // Automatically fetch browser token
  }, []);

  return <div>Your Secure App</div>;
}

export default App;

🛡 Effect: Prevents automated tools from reusing tokens.


4️⃣ Implement Rate Limiting

To block brute force attacks and prevent mass API testing.

📌 Install Rate Limiting Package:

npm install express-rate-limit

📌 Modify Backend (server.js):

const rateLimit = require("express-rate-limit");

const limiter = rateLimit({
  windowMs: 10 * 60 * 1000, // 10 minutes
  max: 100, // Limit each IP to 100 requests
  message: "Too many requests, please try again later.",
});

app.use(limiter);

🛡 Effect: Prevents security testers from sending thousands of requests.


5️⃣ Device Fingerprinting (Advanced)

Block users switching browsers or devices to bypass security.

📌 Install Fingerprint.js:

npm install @fingerprintjs/fingerprintjs

📌 Modify React (App.js):

import { useEffect, useState } from "react";
import FingerprintJS from "@fingerprintjs/fingerprintjs";
import api from "./api"; // Axios instance

function App() {
  const [deviceId, setDeviceId] = useState("");

  useEffect(() => {
    async function getFingerprint() {
      const fp = await FingerprintJS.load();
      const result = await fp.get();
      setDeviceId(result.visitorId);
    }
    getFingerprint();
  }, []);

  const handleRequest = async () => {
    await api.post("/api/protected", {}, { headers: { "X-Device-ID": deviceId } });
  };

  return <button onClick={handleRequest}>Send Secure Request</button>;
}

export default App;

📌 Modify Backend (server.js):

app.use((req, res, next) => {
  const deviceId = req.headers["x-device-id"];
  if (!deviceId) return res.status(403).json({ message: "Unauthorized device" });
  next();
});

🛡 Effect: Blocks security testers using different browsers or devices.


6️⃣ Enable Content Security Policy (CSP)

Prevent attackers from injecting malicious scripts.

📌 Modify Backend (server.js):

const helmet = require("helmet");

app.use(
  helmet.contentSecurityPolicy({
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'"],
      objectSrc: ["'none'"],
    },
  })
);

🛡 Effect: Blocks XSS and clickjacking attacks.


7️⃣ Use Web Application Firewall (WAF)

Deploy Cloudflare WAF to block:
Bot traffic
API scraping
Automated tools like Burp Suite

  • Set strict WAF rules to allow only real users.

Final Security Checklist ✅

Security Feature Protection Level
User-Agent & Referer Checks Blocks non-browser requests
CSRF Protection (HTTP-only cookies) Stops unauthorized API calls
Dynamic Browser Token Prevents token reuse
Rate Limiting Blocks mass API testing
Device Fingerprinting Stops multi-device bypassing
Content Security Policy (CSP) Prevents malicious scripts
Cloudflare WAF Blocks bots & security scanners

💡 Conclusion

🚀 With these layers, even skilled security testers will struggle to bypass your API.

Add comment