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.