Skip to main content

Security Overview

Every web application is a target. Whether you run a personal blog or a banking platform, attackers probe for weaknesses automatically and at scale. Understanding why security matters — and how to think about it systematically — is the first step toward building resilient software.

Why Security Matters

A single vulnerability can lead to:

  • Data breaches — leaked user credentials, payment info, or personal data.
  • Financial loss — fraud, regulatory fines (GDPR penalties can reach 4% of annual revenue).
  • Reputation damage — users lose trust quickly and rarely come back.
  • Legal liability — negligence in handling user data can result in lawsuits.

Security is not a feature you bolt on at the end. It is a property of the entire system that must be considered from day one.

Threat Modeling

Before writing a single line of defensive code, identify what you are protecting and who might attack it.

A simple threat-modeling process:

  1. Identify assets — user data, API keys, session tokens, admin access.
  2. Identify entry points — forms, APIs, file uploads, third-party integrations.
  3. Identify threats — who would attack (script kiddies, competitors, insiders) and how.
  4. Rate risk — likelihood x impact. Focus on high-risk items first.
  5. Mitigate — apply controls (validation, encryption, access control).

A lightweight way to document this is a simple table:

// Example: threat model as structured data
const threats = [
  {
    asset: "User passwords",
    entryPoint: "Login form",
    threat: "Brute-force attack",
    likelihood: "high",
    impact: "high",
    mitigation: "Rate limiting, account lockout, bcrypt hashing",
  },
  {
    asset: "Session token",
    entryPoint: "Cookies",
    threat: "Session hijacking via XSS",
    likelihood: "medium",
    impact: "high",
    mitigation: "HttpOnly cookies, CSP, input sanitization",
  },
];

The OWASP Top 10

The OWASP Top 10 is the industry-standard list of the most critical web application security risks. Here is the 2021 edition at a glance:

#CategoryWhat It Covers
1Broken Access ControlUsers acting outside their permissions
2Cryptographic FailuresWeak encryption, plaintext storage
3InjectionSQL, NoSQL, OS, LDAP injection
4Insecure DesignMissing security architecture
5Security MisconfigurationDefault creds, open cloud storage
6Vulnerable ComponentsOutdated libraries with known CVEs
7Auth FailuresBroken authentication and session management
8Software & Data IntegrityUntrusted deserialization, CI/CD tampering
9Logging FailuresMissing audit trails
10SSRFServer-Side Request Forgery

We will cover the most common of these in detail throughout this course.

Defense in Depth

No single control is enough. Defense in depth means layering multiple security mechanisms so that if one fails, others still protect the system.

// Layers of defense for a typical web app
const layers = [
  "Input validation (client + server)",
  "Parameterized queries (prevent injection)",
  "Authentication & authorization checks",
  "HTTPS everywhere (encrypt data in transit)",
  "Security headers (CSP, HSTS, X-Frame-Options)",
  "Rate limiting & abuse detection",
  "Logging & monitoring (detect breaches early)",
  "Regular dependency audits (patch known CVEs)",
];

Think of it like a castle: the moat, the outer wall, the inner wall, and the keep each provide independent protection. An attacker must bypass all layers to reach the crown jewels.

Practical Example: Securing a Form Endpoint

import { rateLimit } from "@/lib/rate-limit";
import { sanitize } from "@/lib/sanitize";
import { db } from "@/lib/db";

export async function POST(request: Request) {
  // Layer 1: Rate limiting
  const ip = request.headers.get("x-forwarded-for") ?? "unknown";
  if (await rateLimit(ip, { max: 10, windowMs: 60_000 })) {
    return new Response("Too many requests", { status: 429 });
  }

  const body = await request.json();

  // Layer 2: Input validation
  const email = sanitize(body.email);
  if (!email || !email.includes("@")) {
    return new Response("Invalid email", { status: 400 });
  }

  // Layer 3: Parameterized query (prevent SQL injection)
  await db.query("INSERT INTO subscribers (email) VALUES ($1)", [email]);

  // Layer 4: Logging
  console.log(`New subscriber: ${email} from ${ip}`);

  return new Response("Subscribed", { status: 201 });
}

Each layer handles a different class of threat. Remove any one of them and the endpoint is still partially protected by the others.

Key Takeaways

  • Security is a continuous process, not a one-time checklist.
  • Start with threat modeling to prioritize your effort.
  • The OWASP Top 10 gives you a roadmap of the most common risks.
  • Layer your defenses so no single failure is catastrophic.