Security Fundamentals
Web Security Essentials
Learn the security mindset and the essential protections every web application must have.
The Security Mindset
Security starts with a fundamental shift in perspective: assume everything from the client is malicious. Every URL parameter, form field, cookie value, and API request could be crafted by an attacker specifically to exploit your application.
This is not paranoia — it is engineering discipline. Defense requires anticipating attacks.
The CIA Triad
All of information security is built on three principles:
Confidentiality — Data is only seen by authorized users. Your users' passwords, credit cards, and personal information must be protected from unauthorized access.
Integrity — Data is not modified without authorization. Database records, file uploads, and API responses must arrive unmodified and come from legitimate sources.
Availability — The system is accessible when needed. Your application must continue to serve legitimate users even under attack (DDoS resistance, rate limiting, graceful degradation).
HTTPS: Non-Negotiable
HTTP transmits data in plain text. Anyone on the same network — a coffee shop WiFi, an ISP, a government router — can read every request and response. HTTPS encrypts the connection using TLS.
HTTPS is mandatory because:
- Protects user credentials and sensitive data in transit
- Required for modern browser features (Service Workers, geolocation, camera access)
- Required for HTTP/2 (significantly faster than HTTP/1.1)
- Ranked higher in Google search results
- Users see "Not Secure" warnings in the address bar without it
CORS: Cross-Origin Resource Sharing
Same-origin policy prevents JavaScript on one domain from making requests to a different domain by default. CORS is the mechanism that explicitly relaxes this restriction.
Misconfigured CORS is a common vulnerability. Never do this:
// DANGEROUS — allows any origin to make requests
res.setHeader('Access-Control-Allow-Origin', '*');Instead, explicitly allowlist trusted origins:
const allowedOrigins = ['https://yourapp.com', 'https://staging.yourapp.com'];
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}Security Headers
HTTP response headers instruct the browser how to behave. These six headers form the foundation of web security:
// Next.js middleware to set security headers
export function middleware(request: NextRequest) {
const response = NextResponse.next();
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
response.headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
response.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
response.headers.set(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
);
return response;
}Cookie Security
Cookies that store session tokens or authentication state need three attributes:
res.setHeader('Set-Cookie', [
'sessionToken=abc123; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600'
]);- HttpOnly — Prevents JavaScript from reading the cookie (XSS protection)
- Secure — Only sent over HTTPS connections
- SameSite=Strict — Not sent with cross-site requests (CSRF protection)
Defense in Depth
Never rely on a single security control. Layer multiple protections so that if one fails, others catch the attack.
Example: Protecting an API endpoint:
- HTTPS (transit encryption)
- Authentication token validation
- Rate limiting (prevents brute force)
- Input validation (prevents injection)
- Database parameterized queries (prevents SQL injection even if input validation fails)
- Error messages that don't leak internal details
Key Takeaways
- Assume all client input is malicious — validate everything on the server
- HTTPS is mandatory — there is no valid reason to use HTTP for any production application
- Misconfigured CORS is a vulnerability — explicitly allowlist trusted origins, never use wildcard for sensitive endpoints
- Six security headers form the baseline: CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy
- Defense in depth: layer multiple protections rather than relying on any single control
Example
// Next.js middleware: security headers
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const response = NextResponse.next();
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
response.headers.set(
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains; preload'
);
response.headers.set(
'Content-Security-Policy',
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"
);
return response;
}