← Catalog

No. 011 · security

Security Hardening

The OWASP Top 10, addressed in code

Version 1.0.0 License MIT Format SKILL.md

Security is not a feature you add at the end — it’s a set of habits applied throughout development. Most vulnerabilities come from trusting user input or third-party dependencies, not from exotic attack vectors.

Input validation

Validate and sanitize all external input at the boundary:

import { z } from 'zod';

const CreateUserSchema = z.object({
  email: z.string().email().max(255),
  name: z.string().min(1).max(100).regex(/^[a-zA-Z\s'-]+$/),
  age: z.number().int().min(0).max(150).optional(),
});

// Validate before any processing
const result = CreateUserSchema.safeParse(req.body);
if (!result.success) {
  return res.status(400).json({ errors: result.error.issues });
}

Rules:

  • Validate on the server, always — client-side validation is UX, not security
  • Use allowlists, not blocklists (“allow letters” not “disallow
  • Validate type, length, range, and format separately
  • Reject input that doesn’t match the expected schema

SQL injection

Never concatenate user input into SQL queries:

// Bad — vulnerable
db.query(`SELECT * FROM users WHERE id = ${userId}`);

// Good — parameterized
db.query('SELECT * FROM users WHERE id = ?', [userId]);

Use an ORM or query builder that handles parameterization automatically. If you must write raw SQL, always use parameterized queries.

Authentication

Password storage:

import bcrypt from 'bcrypt';

// Hash with cost factor 12+
const hash = await bcrypt.hash(password, 12);

// Verify
const valid = await bcrypt.compare(inputPassword, storedHash);

Never store passwords in plain text, never use MD5/SHA for passwords, never log passwords.

Session management:

  • Use HttpOnly, Secure, SameSite=Strict cookies
  • Set reasonable expiration (15 min for access tokens, 7 days for refresh tokens)
  • Invalidate sessions on password change
  • Implement rate limiting on login endpoints

Secrets management

// Bad — hardcoded
const API_KEY = 'sk-abc123...';

// Bad — logged
console.log('Using API key:', process.env.API_KEY);

// Good — environment variables, never logged
const apiKey = process.env.API_KEY;
if (!apiKey) throw new Error('API_KEY environment variable required');
  • Never commit secrets to version control
  • Use .env files for local development, secret managers for production
  • Rotate keys periodically
  • Use git-secrets or trufflehog to scan for leaked secrets

Dependency security

# npm
npm audit

# Check for known vulnerabilities before installing
npm audit --omit=dev
  • Run npm audit or yarn audit in CI
  • Use Dependabot or Renovate for automated updates
  • Pin dependency versions in production
  • Review changelogs before updating security-critical packages

HTTPS and headers

// Express example
app.use(helmet());

// Sets:
// Strict-Transport-Security: max-age=31536000; includeSubDomains
// X-Content-Type-Options: nosniff
// X-Frame-Options: DENY
// X-XSS-Protection: 0 (modern browsers don't need this)
// Content-Security-Policy: [appropriate policy]

See references/owasp-checklist.md for the full checklist.

When it triggers

  • security vulnerability scan
  • adding input validation
  • handling user authentication
  • managing API keys and secrets