|$ curl https://forge-ai.dev/api/markdown?path=docs/security/owasp
$cat docs/security-—-owasp-top-10.md
updated Recently·18 min read·published

Security — OWASP Top 10

SecurityAdvanced
Introduction

The OWASP Top 10 is the most widely recognized list of web application security risks. Published by the Open Web Application Security Project, it represents the consensus of security experts on the most critical vulnerabilities found in web applications.

Understanding these risks is the foundation of application security. Each category includes real-world attack patterns, example code, and mitigation strategies. Use this guide as a reference when designing, building, and reviewing applications.

A01: Broken Access Control

Broken Access Control occurs when users can access resources or perform actions beyond their permissions. This is the most common and most severe OWASP category.

broken-access-control.ts
TypeScript
1// Insecure Direct Object Reference (IDOR)
2// BAD — user can access any invoice by changing the ID
3app.get('/api/invoices/:id', async (req, res) => {
4 const invoice = await db.findInvoice(req.params.id);
5 res.json(invoice); // No ownership check!
6});
7
8// GOOD — verify ownership before returning data
9app.get('/api/invoices/:id', async (req, res) => {
10 const invoice = await db.findInvoice(req.params.id);
11
12 if (!invoice) {
13 return res.status(404).json({ error: 'Not found' });
14 }
15
16 if (invoice.userId !== req.session.userId && req.session.role !== 'admin') {
17 return res.status(403).json({ error: 'Forbidden' });
18 }
19
20 res.json(invoice);
21});
22
23// Missing function-level access control
24function requireRole(role: string) {
25 return (req, res, next) => {
26 if (req.session.role !== role) {
27 return res.status(403).json({ error: 'Admin access required' });
28 }
29 next();
30 };
31}
32
33app.delete('/api/admin/users/:id', requireRole('admin'), async (req, res) => {
34 await db.deleteUser(req.params.id);
35 res.json({ success: true });
36});

best practice

Enforce access control on every request — never rely on the UI hiding buttons or routes. Check authorization at the controller or middleware layer, not just in the frontend. Use a deny-by-default approach.
A02: Cryptographic Failures

Cryptographic failures (previously "Sensitive Data Exposure") cover weak encryption, missing encryption, and improper certificate validation. This includes transmitting sensitive data in clear text, using weak ciphers, or mishandling keys.

cryptographic-failures.ts
TypeScript
1// Password hashing — NEVER use MD5, SHA1, or unsalted hashes
2import bcrypt from 'bcrypt';
3
4// BAD — unsalted SHA256 (trivial to crack)
5const hash = crypto.createHash('sha256').update(password).digest('hex');
6
7// GOOD — bcrypt with cost factor 12
8const saltRounds = 12;
9const hash = await bcrypt.hash(password, saltRounds);
10
11const isValid = await bcrypt.compare(password, storedHash);
12
13// GOOD — argon2 (preferred for new applications)
14import argon2 from 'argon2';
15
16const hash = await argon2.hash(password, {
17 type: argon2.argon2id,
18 memoryCost: 65536,
19 timeCost: 3,
20 parallelism: 4,
21});
22
23const isValid = await argon2.verify(storedHash, password);
24
25// Encryption — use authenticated encryption
26// BAD — ECB mode, no authentication
27const cipher = crypto.createCipheriv('aes-128-ecb', key, null);
28
29// GOOD — AES-256-GCM with authentication
30const algorithm = 'aes-256-gcm';
31const iv = crypto.randomBytes(16);
32const cipher = crypto.createCipheriv(algorithm, key, iv);
33
34// HTTPS — always enforce TLS 1.2+
35response.setHeader(
36 'Strict-Transport-Security',
37 'max-age=63072000; includeSubDomains; preload'
38);

danger

Never implement your own cryptography. Use well-audited libraries like bcrypt, argon2, or libsodium. Use HTTPS everywhere, not just on login pages. Encrypt sensitive data at rest with authenticated encryption (AES-GCM or ChaCha20-Poly1305).
A03: Injection

Injection attacks occur when untrusted data is sent to an interpreter as part of a command or query. SQL injection is the most well-known, but injection includes XSS, NoSQL injection, and command injection.

injection.ts
TypeScript
1// SQL Injection
2// BAD — string concatenation
3const query = `SELECT * FROM users WHERE id = '${req.query.id}'`;
4await db.query(query); // Attacker: 1' OR '1'='1
5
6// GOOD — parameterized query
7await db.query('SELECT * FROM users WHERE id = $1', [req.query.id]);
8
9// NoSQL Injection (MongoDB)
10// BAD — passing unvalidated input directly
11const user = await User.findOne({
12 username: req.body.username,
13 password: req.body.password,
14}); // Attacker: { "$gt": "" } matches all
15
16// GOOD — validate and sanitize first
17const schema = z.object({
18 username: z.string().min(3).max(50),
19 password: z.string().min(8).max(100),
20});
21const { username, password } = schema.parse(req.body);
22const user = await User.findOne({ username, password: hash(password) });
23
24// Cross-Site Scripting (XSS)
25// BAD — rendering unsanitized user input
26app.get('/profile', (req, res) => {
27 res.send(`<div>${req.query.name}</div>`); // <script>alert('xss')</script>
28});
29
30// GOOD — escape output
31import escape from 'escape-html';
32app.get('/profile', (req, res) => {
33 res.send(`<div>${escape(req.query.name)}</div>`);
34});
35
36// Command Injection
37// BAD — passing user input to shell
38const result = execSync(`ping -c 4 ${req.body.host}`);
39// Attacker: ; cat /etc/passwd
40
41// GOOD — use spawn with array arguments
42const result = spawn('ping', ['-c', '4', sanitizeHost(req.body.host)]);
A04: Insecure Design

Insecure design covers architecture-level flaws that cannot be fixed with a simple code change. These are "bugs in the design" rather than "bugs in the implementation." It includes missing threat modeling, lack of rate limiting, and improper trust boundaries.

insecure-design.ts
TypeScript
1// Missing rate limiting — allows brute force
2// BAD — no protection on login
3app.post('/login', async (req, res) => {
4 const user = await authenticate(req.body.email, req.body.password);
5 // Unlimited attempts!
6});
7
8// GOOD — rate limit login attempts
9import { Ratelimit } from '@upstash/ratelimit';
10
11const loginLimiter = new Ratelimit({
12 limiter: Ratelimit.slidingWindow(5, '15 m'), // 5 attempts per 15 min
13});
14
15app.post('/login', async (req, res) => {
16 const ip = req.ip;
17 const { success } = await loginLimiter.limit(`login:${ip}`);
18
19 if (!success) {
20 return res.status(429).json({
21 error: 'Too many login attempts. Try again later.',
22 });
23 }
24
25 const user = await authenticate(req.body.email, req.body.password);
26});
27
28// Account lockout
29async function handleFailedLogin(email: string) {
30 const attempts = await redis.incr(`lockout:${email}`);
31 await redis.expire(`lockout:${email}`, 900);
32
33 if (attempts >= 5) {
34 await redis.set(`locked:${email}`, 'true', 'EX', 1800);
35 }
36}
37
38// STRIDE threat modeling categories:
39// Spoofing, Tampering, Repudiation, Info Disclosure, DoS, Elevation

warning

Insecure design flaws cannot be fixed by adding a WAF or input validation at the end. They require architectural changes. Build security into the design phase with threat modeling and security requirements reviews.
A05: Security Misconfiguration

Security misconfiguration includes default credentials, unnecessary features enabled, overly permissive CORS, missing security headers, and verbose error messages that leak system information.

misconfiguration.ts
TypeScript
1// Security headers checklist
2import helmet from 'helmet';
3import cors from 'cors';
4
5app.use(helmet()); // Sets 15+ security headers
6
7// CORS — restrict origins in production
8app.use(cors({
9 origin: process.env.NODE_ENV === 'production'
10 ? ['https://example.com']
11 : '*',
12 methods: ['GET', 'POST'],
13}));
14
15// Disable x-powered-by header
16app.disable('x-powered-by');
17
18// Error handling — don't leak stack traces
19app.use((err, req, res, next) => {
20 console.error(err.stack);
21 res.status(500).json({
22 error: process.env.NODE_ENV === 'production'
23 ? 'Internal server error'
24 : err.message,
25 });
26});
27
28// Remove unnecessary routes and features
29// Default admin panels, debug endpoints, and unused API routes
A06: Vulnerable & Outdated Components

Using components with known vulnerabilities is a common entry point for attacks. Regular dependency auditing and updating is essential.

dependency-audit.ts
TypeScript
1// Regular dependency auditing
2npm audit // Check for vulnerabilities
3npm audit fix // Auto-fix (check for breaking changes)
4npm outdated // List outdated packages
5
6// Use Snyk for comprehensive scanning
7snyk test // Test project for vulnerabilities
8snyk monitor // Continuous monitoring
9snyk code test // SAST scanning
10
11// Dependabot config (.github/dependabot.yml)
12version: 2
13updates:
14 - package-ecosystem: "npm"
15 directory: "/"
16 schedule:
17 interval: "weekly"
18 open-pull-requests-limit: 10
19
20// Check for known CVEs before adding dependencies
21// https://www.cve.org/
22// https://snyk.io/vuln

danger

A single vulnerable dependency can compromise your entire application. Subscribe to CVE notifications for your critical dependencies, automate dependency updates with Dependabot or Renovate, and scan for known vulnerabilities in your CI/CD pipeline.
A07: Identification & Auth Failures

Authentication failures include weak passwords, credential stuffing, session fixation, and missing MFA. Proper session management and credential hygiene are critical.

auth-failures.ts
TypeScript
1// Session management best practices
2import session from 'express-session';
3
4app.use(session({
5 secret: process.env.SESSION_SECRET,
6 name: 'custom_sid', // Custom cookie name
7 cookie: {
8 httpOnly: true,
9 secure: process.env.NODE_ENV === 'production',
10 sameSite: 'lax',
11 maxAge: 24 * 60 * 60 * 1000,
12 },
13}));
14
15// Regenerate session after login
16req.session.regenerate((err) => {
17 req.session.userId = user.id;
18 // Prevents session fixation
19});
20
21// Password policy enforcement
22const passwordSchema = z.string()
23 .min(12, 'Password must be at least 12 characters')
24 .regex(/[A-Z]/, 'Must contain uppercase')
25 .regex(/[a-z]/, 'Must contain lowercase')
26 .regex(/[0-9]/, 'Must contain number')
27 .regex(/[^A-Za-z0-9]/, 'Must contain special character');
28
29// Multi-factor authentication
30// Use TOTP (time-based one-time passwords) or WebAuthn
31// Require MFA for admin accounts and sensitive operations
A08: Software & Data Integrity Failures

Software and data integrity failures cover supply chain attacks, unsigned updates, and insecure CI/CD pipelines. Trusting unverified third-party code or data without integrity checks is the core risk.

integrity-failures.ts
TypeScript
1// Verify package integrity
2npm audit signatures // Verify npm package signatures
3npm config set sign-git-tag true // Sign your releases
4
5// Subresource Integrity (SRI) for CDN resources
6// <script
7// src="https://cdn.example.com/lib.js"
8// integrity="sha384-abc123..."
9// crossorigin="anonymous"
10// ></script>
11
12// Verify webhook signatures
13import crypto from 'crypto';
14
15function verifyWebhook(payload: string, signature: string, secret: string) {
16 const expected = crypto
17 .createHmac('sha256', secret)
18 .update(payload)
19 .digest('hex');
20
21 return crypto.timingSafeEqual(
22 Buffer.from(signature),
23 Buffer.from(expected)
24 );
25}
26
27// Use lockfiles (package-lock.json, yarn.lock)
28// Pin dependency versions in production
29// Audit third-party scripts and CDN resources
A09: Security Logging & Monitoring Failures

Without proper logging and monitoring, breaches can go undetected for months. Security-relevant events must be logged with sufficient detail for investigation, and alerts must be actionable.

security-logging.ts
TypeScript
1// Security event logging middleware
2function securityLogger(req, res, next) {
3 const start = Date.now();
4
5 res.on('finish', () => {
6 const logEntry = {
7 timestamp: new Date().toISOString(),
8 method: req.method,
9 path: req.path,
10 status: res.statusCode,
11 ip: req.ip,
12 userAgent: req.headers['user-agent'],
13 userId: req.session?.userId,
14 duration: Date.now() - start,
15 };
16
17 // Log auth events separately
18 if (req.path.startsWith('/api/auth')) {
19 console.log('[AUTH]', JSON.stringify(logEntry));
20 // Send to SIEM: Splunk, Datadog, ELK
21 }
22
23 // Log errors and 4xx/5xx responses
24 if (res.statusCode >= 400) {
25 console.warn('[SECURITY]', JSON.stringify(logEntry));
26 }
27 });
28
29 next();
30}
31
32// Events to always log:
33// - Successful and failed login attempts
34// - Privilege changes (role upgrades, permission changes)
35// - Sensitive data access (PII, payment info)
36// - Configuration changes
37// - Admin actions
38// - Rate limit violations
39// - 4xx and 5xx errors
40
41// Never log: passwords, tokens, credit card numbers, or secrets

info

Centralize logs with a SIEM (Security Information and Event Management) system like Splunk, ELK Stack, or Datadog. Set up alerts for suspicious patterns: multiple failed logins, access from unusual locations, or privilege escalations. Regularly review logs for anomalies.
A10: Server-Side Request Forgery (SSRF)

SSRF occurs when an attacker can make the server send requests to internal or unintended destinations. This can lead to accessing internal services, cloud metadata endpoints, or scanning the internal network.

ssrf-prevention.ts
TypeScript
1// SSRF prevention
2import { URL } from 'url';
3
4// URL validation — block internal addresses
5const BLOCKED_HOSTS = [
6 '169.254.169.254', // AWS/GCP/Azure metadata
7 '127.0.0.1',
8 'localhost',
9 '0.0.0.0',
10 '10.',
11 '172.16.',
12 '172.17.',
13 '172.18.',
14 '172.19.',
15 '172.20.',
16 '172.21.',
17 '172.22.',
18 '172.23.',
19 '172.24.',
20 '172.25.',
21 '172.26.',
22 '172.27.',
23 '172.28.',
24 '172.29.',
25 '172.30.',
26 '172.31.',
27 '192.168.',
28];
29
30function isInternalHost(hostname: string): boolean {
31 return BLOCKED_HOSTS.some(blocked => hostname.startsWith(blocked));
32}
33
34async function safeFetch(url: string): Promise<Response> {
35 const parsed = new URL(url);
36
37 if (isInternalHost(parsed.hostname)) {
38 throw new Error('Blocked internal address');
39 }
40
41 // Allow only HTTPS
42 if (parsed.protocol !== 'https:') {
43 throw new Error('Only HTTPS URLs are allowed');
44 }
45
46 return fetch(url, {
47 signal: AbortSignal.timeout(5000), // 5 second timeout
48 });
49}
50
51// Use allowlist approach when possible
52const ALLOWED_DOMAINS = ['api.trusted-service.com', 'cdn.example.com'];
53
54async function safeFetchAllowlist(url: string): Promise<Response> {
55 const parsed = new URL(url);
56
57 if (!ALLOWED_DOMAINS.includes(parsed.hostname)) {
58 throw new Error('Domain not in allowlist');
59 }
60
61 return fetch(url);
62}

best practice

Use an allowlist of permitted domains for outbound requests when possible. If you must accept user-provided URLs, validate them strictly: reject private IP ranges, disable redirect following, enforce HTTPS, and set timeouts. Consider using a dedicated URL sanitizer library.
Mitigation Strategies Summary
CategoryPrimary DefenseTools & Techniques
A01: Access ControlDeny by defaultRBAC, ABAC, middleware, ownership checks
A02: CryptoStrong algorithmsbcrypt, argon2, TLS 1.3, AES-GCM
A03: InjectionParameterized queriesZod validation, ORM, prepared statements
A04: DesignThreat modelingSTRIDE, attack trees, security reviews
A05: MisconfigurationHardening checklistsHelmet.js, CIS benchmarks, CSP scanners
A06: ComponentsDependency scanningnpm audit, Snyk, Dependabot, Trivy
A07: AuthMFA + rate limitingTOTP, WebAuthn, bcrypt, session mgmt
A08: IntegritySignature verificationSRI, package signing, webhook verification
A09: LoggingCentralized SIEMELK, Splunk, Datadog, alerting rules
A10: SSRFAllowlist + validationURL sanitization, IP blocking, timeouts
Best Practices
  • Run OWASP ZAP or Burp Suite scans as part of your CI/CD pipeline.
  • Use a web application firewall (WAF) as a defense-in-depth layer, not as your only defense.
  • Conduct regular penetration tests and security audits.
  • Keep an inventory of all dependencies and their known vulnerabilities.
  • Implement a bug bounty program or vulnerability disclosure policy.
  • Train your development team on OWASP Top 10 annually.
  • Use SAST (Static Analysis) and DAST (Dynamic Analysis) tools in your pipeline.
  • Define and enforce a Software Security Development Lifecycle (SSDL).

best practice

The OWASP Top 10 is a starting point, not a comprehensive security program. Remediate all categories before going to production, but understand that real-world security requires threat modeling specific to your application, data, and threat model.
$Blueprint — Engineering Documentation·Section ID: SEC-OWASP·Revision: 1.0