Chapters of From Frontend to Full-Stack Backend

Week 3 - Interview-এ সবচেয়ে বেশি জিজ্ঞেস করা TopicsChapter 17 of 283 min read

Security - rate limiting, what CORS really is, and helmet

Stopping brute force, CORS and preflight requests, helmet headers, input sanitization

Your login endpoint is open to everyone. An attacker can write a script and try 100 passwords per second - with no barrier. Or imagine unwanted requests coming to your API from some other website. This chapter is about closing those doors - without which all other good code is meaningless.

Rate limiting - tying down the speed

Rate limiting means no more than a fixed number of requests will be taken in a fixed time. Like at most 100 requests in 15 minutes from the same IP, and stricter for login - after 5 failures, wait a while. That makes brute force attacks almost impossible, and the server also survives abnormal pressure.

import rateLimit from "express-rate-limit";

const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 20,                  // at most 20 times in this period
  message: { success: false, message: "Too many attempts, please try again after a while" },
});

app.post("/auth/login", loginLimiter, loginController);

CORS - the browser's guard, not the server's

The biggest misconception about CORS: that it is a server security measure. Actually CORS is a browser rule - whether a website of one origin (domain) can read data from an API of another origin, the browser blocks that. The server says which origins are allowed. Code running in a browser from an unknown origin cannot read the data - but CORS will not stop requests from Postman or scripts; for that, auth and rate limiting are the reliance.

Preflight is the advance check for this: before the real request, the browser sends a small OPTIONS request asking 'can I send this kind of request from this origin?' Only when the server says yes does the real request go. When you see a 'CORS error' from the frontend, it almost always means this - your frontend's domain is not in the server's allowed origin list.

import cors from "cors";
import helmet from "helmet";

app.use(helmet()); // puts security headers in place on its own
app.use(cors({ origin: ["https://your-frontend.com"], credentials: true }));

Helmet puts many security headers in place in one line - like stopping the site from being put inside another site's frame. And input sanitization means not trusting user-given data - validation (chapter 4) is its first step, and on top of that always using parameterized ways in database queries, so there is no room for SQL injection. Prisma and prepared statements do this on their own - building queries by joining raw strings is where the danger lies.

The mistake everyone makes

Giving everyone permission in CORS with origin: "*", especially with credentials turned on - that is the same as opening the door. Give permission only to your own frontend domain. Another mistake: thinking CORS is security and keeping auth weak.

Today's practice

Put helmet in your API, open CORS only for your own frontend origin, and put a strict rate limit on the login endpoint. Then try logging in 25 times in a row with a script and check whether the limiter stops it properly.

How to say it in an interview

With rate limiting I stop brute force and abnormal pressure, especially on sensitive endpoints like login. CORS is the browser's origin check - I allow only my own frontend domain, never opening it for everyone. Along with that, helmet for security headers, and keeping all queries parameterized to stop injection.

All chapters of From Frontend to Full-Stack Backend