Imagine this familiar scenario:
You just finished building your full-stack application. You built a sleek frontend in React, developed a fast REST API in Node.js & Express, connected it to MongoDB, styled everything with CSS, and deployed it to the cloud.
Everything works smoothly. Forms submit, buttons respond, and data loads quickly.
Then, a senior developer sits down beside you and asks a few simple questions:
- “If I open the browser DevTools, can I see another user’s authentication token?”
- “What happens if I bypass your React frontend and call your delete API directly from Postman?”
- “Can an employee change the employee ID in the URL and view their manager’s salary slip?”
- “If someone posts a malicious comment containing
<script>tags, will it execute inside other users’ browsers?” - “What happens if an automated script sends 100,000 login requests to your API in one minute?”
These questions do not mean your code is broken. They mean your code is currently built only for the happy path — when users behave honestly.
Web security is the art and engineering of protecting your application, your data, and your users when people do NOT behave honestly.
What Exactly Are We Protecting?
When developers hear “security,” they often think of mysterious black screens with green text. In reality, web security is very concrete. As full-stack developers, we are protecting specific assets:
- User Accounts & Identity: Ensuring users only access their own profiles and preventing account takeovers.
- Passwords: Guaranteeing that even if a database is leaked, raw user passwords cannot be recovered.
- Authentication Tokens & Sessions: Protecting the digital keys that keep users logged in.
- Personal & Sensitive Information (PII): Phone numbers, addresses, Aadhaar/PAN details, and medical records.
- API Endpoints: Preventing unauthorized users or automated bots from abusing backend logic.
- The Database: Preventing malicious actors from reading, modifying, or deleting records via injection attacks.
- Business & Financial Logic: Ensuring users cannot alter prices during checkout, manipulate UPI callbacks, or skip subscription paywalls.
- Cloud Infrastructure: Keeping your server CPU, memory, and bandwidth available without being taken down by Denial of Service (DoS) attacks.
How Does a Typical Web Application Work?
To secure a web application, you must first visualize how data travels between the user and your database.
sequenceDiagram
autonumber
actor User
participant Browser as Browser (React)
participant Network as HTTPS / Network
participant API as Backend API (Express)
participant Auth as Auth & Middleware
participant DB as Database (MongoDB/SQL)
User->>Browser: Enters email & password
Browser->>Network: Sends HTTP POST /api/login
Network->>API: Encrypted request delivered
API->>Auth: Validates input & checks password hash
Auth->>DB: Query user record
DB-->>Auth: User record returned
Auth-->>API: Authentication verified
API-->>Network: Sends response + Auth Token / Cookie
Network-->>Browser: Response received & stored
Browser-->>User: Redirects to Dashboard
Security risks can emerge at every single step of this chain:
| Layer | What Happens Here | Potential Security Risk |
|---|---|---|
| Frontend (React) | UI rendering, user input collection | Untrusted input, XSS, exposed API secrets in JS bundles |
| Network (HTTP/S) | Data in transit over Wi-Fi and the internet | Packet sniffing, man-in-the-middle (MitM) eavesdropping |
| Backend API | Request handling, routing, business rules | Unauthenticated routes, missing rate limits, IDOR |
| Auth Middleware | Verifying identity and access permissions | Broken access controls, expired or forged tokens |
| Database | Permanent data storage | SQL / NoSQL injection, unencrypted sensitive fields |
[!IMPORTANT] The Golden Rule of Web Security: Never trust the frontend. The user’s browser is completely under the user’s control. Anyone can inspect network traffic, modify JavaScript, or send raw HTTP requests using curl or Postman. All real security checks must happen on the backend server.
1. Authentication: Checking WHO You Are
What is Authentication?
Authentication is the process of verifying that someone is who they claim to be.
Real-World Analogy
Imagine entering your college campus or your company’s tech park. At the front entrance, the security guard asks for your College ID Card or Employee Badge. The guard checks your photo and name to confirm your identity.
The guard is answering one question: “Are you really who you say you are?”
That is Authentication.
How Login Actually Works Behind the Scenes
- The user enters their email and plaintext password into a React form.
- React sends an HTTPS
POSTrequest to/api/v1/auth/login. - The Node.js backend searches for the user by email in the database.
- The backend takes the incoming password, hashes it using a cryptographic library like
bcrypt, and compares the hash with the stored hash in the database. - If the hashes match, the backend issues an authentication credential (like a Session Cookie or a JWT) and sends it back to the browser.
// server.js - Simple Express.js Authentication Example
import bcrypt from 'bcrypt';
import express from 'express';
import User from './models/User.js';
const app = express();
app.use(express.json());
app.post('/api/v1/auth/login', async (req, res) => {
const { email, password } = req.body;
// 1. Check if user exists
const user = await User.findOne({ email });
if (!user) {
// TIP: Avoid saying "Email not found" to prevent email enumeration attacks
return res.status(401).json({ error: 'Invalid email or password' });
}
// 2. Safely compare the plain password with the stored hash
const isMatch = await bcrypt.compare(password, user.passwordHash);
if (!isMatch) {
return res.status(401).json({ error: 'Invalid email or password' });
}
// 3. Identity confirmed! Issue session or token
res.json({ message: 'Login successful', userId: user._id });
});
Notice: We never compare passwords with if (password === user.password). We never store raw passwords. We store and verify salted hashes.
2. Authorization: Checking WHAT You Can Do
What is Authorization?
Authorization is the process of checking what permissions an authenticated user has.
Real-World Analogy
Once the security guard at your office park lets you through the front gate with your badge (Authentication), you walk inside.
Can you open the door to the Executive Boardroom or the Server Room?
Your badge gets scanned again at those doors. If you are a junior software engineer, the door stays locked. Only system administrators and executives have clearance for those rooms.
The door scanner is answering: “Do you have permission to enter this room?”
That is Authorization.
Developer Example: An Employee Portal
Consider an employee management application with three roles:
Employee(can view their own profile and payslip)Manager(can approve leaves for their team)Admin(can view all salaries, edit roles, and delete accounts)
A common mistake is checking only if a user is logged in, but forgetting to verify their role:
// ⚠️ INSECURE: DO NOT USE IN PRODUCTION
// Any logged-in employee can change the ID in the URL to view anyone's salary!
app.get('/api/v1/salaries/:employeeId', authenticateUser, async (req, res) => {
const salary = await Salary.findOne({ employeeId: req.params.employeeId });
res.json(salary);
});
Here is the secure approach:
// ✅ RECOMMENDED: Enforcing Authorization Rules
app.get('/api/v1/salaries/:employeeId', authenticateUser, async (req, res) => {
const requestingUser = req.user;
const requestedId = req.params.employeeId;
// Rule: You can only view your own salary, unless you are an Admin
const isOwner = requestingUser.id === requestedId;
const isAdmin = requestingUser.role === 'admin';
if (!isOwner && !isAdmin) {
return res.status(403).json({ error: 'Access denied. Unauthorized request.' });
}
const salary = await Salary.findOne({ employeeId: requestedId });
res.json(salary);
});
Summary: 401 Unauthorized means “I don’t know who you are (please log in).“
403 Forbidden means “I know who you are, but you are not allowed to do this.”
3. Cookies: The Browser’s Built-in Memory
What is a Cookie?
A Cookie is a small piece of text (usually less than 4KB) stored by the web browser.
Why Do Browsers Use Cookies?
The HTTP protocol is stateless. This means when you click a button to view your cart, the server has no memory of the fact that you logged in five seconds ago.
To solve this, after you log in, the server sends a cookie containing a unique Session ID. Every time your browser makes another request to that server, it automatically attaches that cookie.
Essential Security Flags for Cookies
If you configure cookies carelessly, malicious JavaScript can steal them. Always use these three flags in production:
// Express.js cookie configuration
res.cookie('sessionId', sessionToken, {
httpOnly: true, // Prevents JavaScript from reading the cookie
secure: true, // Transmitted ONLY over encrypted HTTPS connections
sameSite: 'lax', // Protects against Cross-Site Request Forgery (CSRF)
maxAge: 24 * 60 * 60 * 1000 // 1 day in milliseconds
});
HttpOnly: When enabled,document.cookiein JavaScript cannot read this cookie. Even if an attacker injects malicious JavaScript into your page (XSS), they cannot easily extract your session cookie!Secure: The browser will never send this cookie over unencrypted plain HTTP. It requires HTTPS.SameSite: Restricts whether cookies are sent along with requests originating from third-party websites (preventing CSRF attacks).
4. JWT (JSON Web Tokens): Portable Digital Passes
What is a JWT?
A JSON Web Token (JWT) is a compact, URL-safe string formatted into three parts separated by dots (.):
Header.Payload.Signature
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjM0NTYiLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE2NzI1MzExMTB9.k1N9...
- Header: Describes the signing algorithm (e.g., HMAC-SHA256).
- Payload: Contains the data claims (e.g.,
userId: "123",role: "developer"). - Signature: A cryptographic hash created using your server’s secret key. If anyone alters the payload, the signature becomes invalid.
Why Do Developers Use JWTs?
JWTs are stateless. The backend does not need to store every active session in database memory. When a request arrives, the server simply verifies the cryptographic signature with its secret key. If valid, the server trusts the payload.
Critical JWT Myths to Avoid
[!WARNING] JWT is Signed, NOT Encrypted!
Anyone can take a JWT, paste it into jwt.io, and read everything inside the payload in plain text. Never store passwords, secrets, or sensitive personal data inside a JWT payload.
Where should you store a JWT?
Storing JWTs in browser localStorage leaves them exposed to theft via Cross-Site Scripting (XSS). In modern web applications, storing authentication tokens in HttpOnly, Secure cookies is the recommended best practice.
5. HTTPS and TLS: The Encrypted Highway
Why Does HTTPS Exist?
When you send data over plain HTTP, every packet travels across the internet in readable text. If you are sitting in a café, airport, or college canteen on public Wi-Fi, anyone running packet-inspection software on that network can read your emails, passwords, and form submissions.
The Letter Analogy
- HTTP is like sending a postcard through the postal service. Every postal worker, delivery driver, and neighbor along the road can read your postcard.
- HTTPS is putting your letter inside a sealed titanium briefcase locked with an unbreakable key. Only you and the intended recipient have the keys to unlock it. Everyone else in between only sees scrambled noise.
flowchart LR
subgraph Insecure [HTTP Plaintext]
A[Browser] -->|Password: mySecret123| B(Public Wi-Fi / ISP) -->|Readable by anyone!| C[Server]
end
subgraph Secure [HTTPS / TLS]
D[Browser] -->|a8#f9$k2@z!0xL...| E(Public Wi-Fi / ISP) -->|Scrambled / Encrypted| F[Server]
end
Key Terms Clarified:
- TLS (Transport Layer Security): The actual cryptographic protocol that encrypts network traffic. (SSL is the older, retired predecessor of TLS).
- HTTPS: Simply HTTP running over an encrypted TLS connection.
- SSL/TLS Certificate: A digital identity passport issued by a recognized Certificate Authority (CA) proving that your domain really belongs to you.
6. CORS: The Browser’s Cross-Origin Traffic Police
Few security topics cause more developer confusion than CORS (Cross-Origin Resource Sharing).
Have you ever seen this red error in your browser console?
Access to fetch at 'http://api.myapp.com' from origin 'http://localhost:3000' has been blocked by CORS policy.
What is CORS Actually Doing?
By default, browsers follow the Same-Origin Policy (SOP). A web page loaded from website-a.com is forbidden from reading data from website-b.com. This prevents a malicious site from secretly reading your bank balance in another tab.
When your React app runs on http://localhost:3000 and requests data from your Node API on http://localhost:5000, the origins are different (different ports).
The browser intercepts this and asks your Node.js server:
“Hey server, is http://localhost:3000 allowed to read your data?”
If your Node server includes the proper CORS header (Access-Control-Allow-Origin: http://localhost:3000), the browser delivers the data to your React code.
// Express.js with the 'cors' package
import cors from 'cors';
import express from 'express';
const app = express();
// ✅ RECOMMENDED: Only allow your specific frontend domain
app.use(cors({
origin: 'https://myblogapp.com',
credentials: true
}));
[!CAUTION] CORS is NOT a firewall for your API!
CORS is enforced strictly by web browsers. Hackers do not use web browsers to attack your APIs. They use Python scripts, Postman, or terminal commands likecurl. These tools ignore CORS completely. CORS does not replace proper Authentication and Authorization.
7. Common Web Security Attacks (The Quick Overview)
As we continue through this series, we will break down each major vulnerability step-by-step. Here is your introductory mental map:
mindmap
root((Web Attacks))
Injection
SQL Injection (SQLi)
NoSQL Injection
Command Injection
Client-Side
Cross-Site Scripting (XSS)
Cross-Site Request Forgery (CSRF)
Clickjacking
Broken Access
IDOR / BOLA
Privilege Escalation
Missing Authz checks
Abuse & Automation
Credential Stuffing
Brute Force
API Rate-Limit Abuse
- XSS (Cross-Site Scripting): An attacker injects malicious JavaScript into your site (e.g., via a comment box), which runs in other users’ browsers and steals session tokens.
- CSRF (Cross-Site Request Forgery): A malicious site tricks an authenticated user’s browser into performing unwanted actions on another site (like transferring funds).
- SQL / NoSQL Injection: An attacker enters malicious database queries into input fields to bypass login or dump tables.
- IDOR (Insecure Direct Object Reference): Altering an ID in a request (e.g.,
/api/orders/501to/api/orders/502) to view another customer’s private data. - Brute Force & Credential Stuffing: Automated bots testing millions of leaked passwords against your login API.
8. Common Beginner Mistakes to Avoid
- Storing Passwords in Plaintext: Storing unhashed passwords in your database is dangerous and irresponsible. Always use
bcryptorargon2. - Trusting Frontend Validation Alone: Disabling a submit button in React with
disabled={!isValid}is great for UX, but attackers can send requests directly to the endpoint. Always re-validate on the backend. - Leaking Secrets in React Code: Any environment variable prefixed with
REACT_APP_orVITE_is bundled into public JavaScript. Anyone can view them. Keep database keys and payment private keys strictly in the backend.env. - Verifying Authentication but Omitting Authorization: Confirming who is logged in, but forgetting to verify if they own the resource they are editing.
- Storing Sensitive Tokens in
localStorage: Tokens inlocalStoragecan be read by any JavaScript running on the page, leaving them vulnerable to XSS. - Detailed Error Stack Traces in Production: Returning raw database errors (
Error: Cast to ObjectId failed at path "_id"...) gives attackers valuable clues about your architecture.
Simple Developer Security Checklist
Use this checklist when building your next full-stack project:
- HTTPS Enforced: All traffic is redirected to
https://. - Passwords Hashed: Passwords are hashed with
bcrypt(salt rounds $\ge 10$) orargon2. - Input Validated on Backend: Validated using libraries like Zod, Joi, or express-validator.
- Cookies Hardened: Session cookies use
HttpOnly,Secure, andSameSiteflags. - Secrets Stored in Backend
.env: Never commit secrets or.envfiles to GitHub. - Authorization Checked: Every protected endpoint verifies resource ownership and user roles.
- Rate Limiting Added: Sensitive endpoints (like
/loginand/forgot-password) have rate limits. - Security Headers Configured: Added
helmetmiddleware in Express. - Generic Error Messages: Database errors are logged internally, not exposed to the user.
10 Common Interview Questions & Answers
1. What is the difference between Authentication and Authorization?
Answer: Authentication verifies who you are (e.g., verifying email and password). Authorization determines what you are allowed to do (e.g., checking if a user has admin rights to delete a post).
2. Why should passwords never be stored in plaintext?
Answer: If the database is compromised or leaked, plaintext passwords immediately expose all users. Passwords must be hashed using a slow cryptographic algorithm like bcrypt with a salt to prevent rainbow table and dictionary attacks.
3. What does the HttpOnly flag on a cookie do?
Answer: It prevents client-side JavaScript from accessing the cookie via document.cookie. This provides a critical defense against session hijacking via Cross-Site Scripting (XSS).
4. What does the Secure flag on a cookie do?
Answer: It ensures that the browser will only transmit the cookie over encrypted HTTPS connections, preventing it from being intercepted over unencrypted HTTP.
5. Is a JWT encrypted by default?
Answer: No. A standard JWT is digitally signed, not encrypted. Anyone can decode and view the payload. Sensitive information like passwords or financial data should never be stored in a JWT payload.
6. Where is the most secure place to store a session token in a web app?
Answer: In an HttpOnly, Secure, SameSite cookie. This prevents client-side JavaScript access while protecting against unauthorized cross-site requests.
7. Does CORS protect my backend API from attackers using Postman or Python?
Answer: No. CORS is a browser-only security feature. Tools like Postman, curl, and automated scripts bypass CORS entirely. APIs must rely on authentication, authorization, and rate limiting for protection.
8. What is the difference between HTTP and HTTPS?
Answer: HTTP transmits data in unencrypted plaintext, vulnerable to eavesdropping. HTTPS encrypts all communication using TLS (Transport Layer Security), ensuring confidentiality and integrity.
9. What is Cross-Site Scripting (XSS)?
Answer: An attack where malicious JavaScript is injected into a trusted website. When other users view the page, their browsers execute the script, which can steal cookies, session tokens, or manipulate the page.
10. What is an IDOR vulnerability?
Answer: Insecure Direct Object Reference occurs when an application exposes a direct database reference (like /api/invoices/1042) without verifying whether the requesting user actually owns or has permission to view that invoice.
Frequently Asked Questions (FAQ)
Is frontend form validation enough for security?
No. Frontend validation provides a smooth user experience by catching typos quickly. However, an attacker can bypass the frontend entirely by making requests using Postman, curl, or custom scripts. All validation must be repeated on the backend.
Can a website be 100% secure?
In software engineering, there is no such thing as 100% security. Security is about reducing risk and raising the cost of an attack so high that an attacker moves on. By adopting industry-standard best practices, you protect your users from the overwhelming majority of common vulnerabilities.
Do I need to be a cybersecurity specialist to write secure code?
No. Writing secure web applications is an essential engineering skill for every frontend, backend, and full-stack developer. Understanding fundamental concepts like authentication, authorization, and input validation is part of building reliable software.
Final Summary
- Web security is about defense-in-depth: Never rely on a single layer of protection.
- Never trust the client: Always validate input, enforce authentication, and check authorization on the server.
- Authentication confirms identity; Authorization enforces permissions.
- Protect tokens: Use
HttpOnly,Secure, andSameSitecookies whenever possible. - HTTPS is non-negotiable: Encrypt all traffic in transit.
- CORS is a browser mechanism, not a substitute for backend authentication.
What’s Next?
👉 Part 2: Authentication vs Authorization — What’s the Real Difference?