DEV Community

Cover image for What is Web Security? A Simple Guide for Developers
Venkatesh j
Venkatesh j

Posted on Originally published at spiderworld.in

What is Web Security? A Simple Guide for Developers

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:

  1. User Accounts & Identity: Ensuring users only access their own profiles and preventing account takeovers.
  2. Passwords: Guaranteeing that even if a database is leaked, raw user passwords cannot be recovered.
  3. Authentication Tokens & Sessions: Protecting the digital keys that keep users logged in.
  4. Personal & Sensitive Information (PII): Phone numbers, addresses, and sensitive records.
  5. API Endpoints: Preventing unauthorized users or automated bots from abusing backend logic.
  6. The Database: Preventing malicious actors from reading, modifying, or deleting records via injection attacks.
  7. Business & Financial Logic: Ensuring users cannot alter prices during checkout or skip subscription paywalls.
  8. 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

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

  1. The user enters their email and plaintext password into a React form.
  2. React sends an HTTPS POST request to /api/v1/auth/login.
  3. The Node.js backend searches for the user by email in the database.
  4. 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.
  5. 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) {
    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 });
});
Enter fullscreen mode Exit fullscreen mode

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);
});
Enter fullscreen mode Exit fullscreen mode

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);
});
Enter fullscreen mode Exit fullscreen mode
  • 401 Unauthorized: "I don't know who you are (please log in)."
  • 403 Forbidden: "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
});
Enter fullscreen mode Exit fullscreen mode
  • HttpOnly: When enabled, document.cookie in 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

  1. Header: Describes the signing algorithm (e.g., HMAC-SHA256).
  2. Payload: Contains the data claims (e.g., userId: "123", role: "developer").
  3. Signature: A cryptographic hash created using your server's secret key. If anyone alters the payload, the signature becomes invalid.

Critical JWT Myth to Avoid

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.

  • HTTP is like sending a postcard through the postal service. Every postal worker 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.

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

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: "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
}));
Enter fullscreen mode Exit fullscreen mode

Warning: CORS is NOT a firewall for your API! CORS is enforced strictly by web browsers. Attackers using Postman, Python, or curl bypass CORS completely. CORS does not replace proper Authentication and Authorization.


7. Common Web Security Attacks (Quick Overview)

  • 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/501 to /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

  1. Storing Passwords in Plaintext: Storing unhashed passwords in your database is dangerous and irresponsible. Always use bcrypt or argon2.
  2. 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.
  3. Leaking Secrets in React Code: Any environment variable prefixed with REACT_APP_ or VITE_ is bundled into public JavaScript. Keep database keys and payment private keys strictly in the backend .env.
  4. Verifying Authentication but Omitting Authorization: Confirming who is logged in, but forgetting to verify if they own the resource they are editing.
  5. Storing Sensitive Tokens in localStorage: Tokens in localStorage can be read by any JavaScript running on the page, leaving them vulnerable to XSS.
  6. Detailed Error Stack Traces in Production: Returning raw database errors 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 ≥ 10) or argon2.
  • [ ] Input Validated on Backend: Validated using libraries like Zod, Joi, or express-validator.
  • [ ] Cookies Hardened: Session cookies use HttpOnly, Secure, and SameSite flags.
  • [ ] Secrets Stored in Backend .env: Never commit secrets or .env files to GitHub.
  • [ ] Authorization Checked: Every protected endpoint verifies resource ownership and user roles.
  • [ ] Rate Limiting Added: Sensitive endpoints (like /login and /forgot-password) have rate limits.
  • [ ] Security Headers Configured: Added helmet middleware 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 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 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 or session tokens.

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 that invoice.


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, and SameSite cookies whenever possible.
  • HTTPS is non-negotiable: Encrypt all traffic in transit.
  • CORS is a browser mechanism, not a substitute for backend authentication.

👉 Read Part 2: Authentication vs Authorization — What's the Real Difference? exclusively on SPIDERWORLD!


Originally published on SPIDERWORLD. Explore full guides, system design breakdowns, and technical talks on spiderworld.in.

Top comments (0)