Security audit of an Express.js demo API detailing findings, proofs, and minimal remediation for CORS, rate limiting, headers, request limits, secret management, and error handling.
This lesson documents the API and infrastructure security audit for an Express.js demo application (express-login-demo). It explains findings from an automated audit, provides concrete proof and minimal remediation code snippets, and links to resources for implementing fixes. If you run a public or internal API, apply layered security controls: CORS origin validation, request limits, secure secrets, HTTP security headers, and careful error handling.Below is the prompt used to drive the audit review and the checklist it produced. It defines the audit scope and the security areas inspected.
Copy
* Welcome to Claude Code!/help for help, /status for your current setupcwd: /Users/jeremy/Repos/Claude Code Course/Express-login-demo> ## API & Infrastructure SecurityReview API-specific security configurations.Check for:1. CORS configuration - Not using wildcard (*) in production - Proper origin validation - Credentials handling2. Rate Limiting - Implemented on all endpoints - Different limits for different operations - Distributed rate limiting for scaled apps3. API Versioning security - Deprecated version handling - Breaking change management4. Request size limits - Body parser limits - File upload restrictions - JSON depth limits5. HTTP Security Headers - Helmet.js configuration - CSP headers - X-Frame-Options - X-Content-Type-Options - Strict-Transport-Security6. API key/token management - Secure storage - Rotation policy - Scope limitations7. Error handling - No stack traces in production - Generic error messages - Proper status codes## Provide:A structured finding report with the following for each issue:- Concrete code locations and identifiers- Minimal, drop-in fix snippets over prose- If context is missing, mark as Unable to verify and state what code would prove it- Write this into a markdown file and place it in the audits/ folder.
Audit checklist (consolidated)
CORS: avoid wildcards in production, validate origins, handle credentials
🔴 CRITICAL: No Rate Limiting on Sensitive Endpoints
Severity: Critical
Evidence: No express-rate-limit or equivalent middleware applied to auth endpoints (e.g., /api/auth/login)
Rationale: Without limits, login endpoints are susceptible to brute force and credential stuffing.
PoC (brute-force loop — run only in safe test environments):
Copy
for i in {1..1000}; do curl -s -o /dev/null -w "%{http_code}\n" -X POST http://localhost:3000/api/auth/login \ -H "Content-Type: application/json" \ -d "{\"email\":\"victim@test.com\",\"password\":\"guess$i\"}"done
Minimal remediation using express-rate-limit:
Copy
// server.js or routes/auth.jsconst rateLimit = require('express-rate-limit');const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 5, // limit each IP to 5 attempts per window message: { error: 'Too many login attempts, please try again later' }, standardHeaders: true, legacyHeaders: false, handler: (req, res) => { res.status(429).json({ error: 'Too many login attempts', retryAfter: 15 * 60 // seconds }); }});// Apply to login routeapp.use('/api/auth/login', loginLimiter);
For scaled systems, configure a shared store (Redis) as the rate limiter store.
// server.jsconst helmet = require('helmet');app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'"], styleSrc: ["'self'"], // minimize 'unsafe-inline' in production imgSrc: ["'self'"], fontSrc: ["'self'"] } }, crossOriginEmbedderPolicy: false // set to true only if needed by your app}));// HSTS: only enable after ensuring HTTPS is served in productionapp.use(helmet.hsts({ maxAge: 63072000, // 2 years includeSubDomains: true, preload: true}));
Testing tip: Use curl to validate header presence:
Replace hardcoded/weak JWT secrets with secure secrets or secret manager
Implement rate limiting on authentication/login endpoints
Add security headers via Helmet (enable HSTS only when HTTPS is confirmed)
Short term (within 1 week)
4. Configure CORS origin whitelist for production
5. Enforce request size limits for JSON and URL-encoded parsers
6. Remove stack traces from HTTP responses and centralize safe loggingMedium term (within 1 month)
7. Add API versioning and deprecation strategy
8. Add automated security tests and continuous scanning
9. Implement distributed rate limiting with Redis if scaling
# After applying rate limiter to /api/auth/login, send more than limitcurl -X POST http://localhost:3000/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"test@test.com","password":"wrong"}'# Expect 429 after limit reached
The audit inspected server.js, files under routes/, and configuration files. If files or identifiers were missing, the audit marked them as Unable to verify and included the code that would prove compliance.
The complete audit report is saved as audits/API_INFRASTRUCTURE_SECURITY_AUDIT.md (379 lines) with concrete findings, exact code locations, PoC tests, and remediation snippets.
Prioritize quick mitigations (rate limiting, security headers, and secrets) before public deployment. These reduce the largest immediate attack surface.
The express-login-demo requires immediate hardening before production due to missing CORS restrictions, absent rate limiting, weak secret management, and a lack of security headers. Recommended next steps:
Re-run automated scans and conduct penetration testing after fixes.
Introduce secure secret storage and rotation policies.
Automate security checks in CI/CD and schedule periodic audits.
The full structured audit exists at audits/API_INFRASTRUCTURE_SECURITY_AUDIT.md. Inspect that file for detailed code locations, PoC tests, and remediation snippets.