UNPKG

secure-express-setup

Version:

Military-grade one-command security setup for Express.js applications

243 lines (194 loc) 7.92 kB
// index.js - FULLY AUTOMATED VERSION const helmet = require('helmet'); const hpp = require('hpp'); const useragent = require('express-useragent'); const setupRateLimit = require('./lib/rateLimit'); const setupCors = require('./lib/cors'); const setupSanitization = require('./lib/sanitization'); const setupCsrf = require('./lib/csrf'); const setupLogger = require('./lib/logger'); const setupIpFilter = require('./lib/ipFilter'); const setupJwtSecurity = require('./lib/jwtSecurity'); const setupSqlInjection = require('./lib/sqlInjection'); const setupPathTraversal = require('./lib/pathTraversal'); const setupBruteForce = require('./lib/bruteForce'); const setupSecretsDetection = require('./lib/secretsDetection'); const { setupEncryption } = require('./lib/encryption'); const setupSessionSecurity = require('./lib/sessionSecurity'); const setupFileValidation = require('./lib/fileValidation'); const setupSlowloris = require('./lib/slowloris'); const setupHeaders = require('./lib/headers'); // Helpers const apiKeyFactory = require('./lib/apiKey'); const webhookSignatureFactory = require('./lib/webhookSignature'); const oauthFactory = require('./lib/oauth'); const requireRole = require('./lib/rbac'); /** * AUTO-LOADED CONFIG FROM ENV + OVERRIDES */ function loadConfig(options = {}) { return { jwtSecret: options.jwtSecret || process.env.JWT_SECRET, encryptionKey: options.encryptionKey || process.env.ENCRYPTION_KEY, sessionSecret: options.sessionSecret || process.env.SESSION_SECRET, cors: options.cors || { origin: (process.env.ALLOWED_ORIGINS || '*').split(','), credentials: true, methods: (process.env.CORS_METHODS || 'GET,POST,PUT,DELETE,PATCH').split(','), allowedHeaders: (process.env.CORS_ALLOWED_HEADERS || 'Content-Type,Authorization,x-api-key').split(','), }, rateLimit: options.rateLimit || { windowMs: Number(process.env.RATE_LIMIT_WINDOW_MS || 15 * 60 * 1000), max: Number(process.env.RATE_LIMIT_MAX || 100), message: 'Too many requests, please try again later.' }, bruteForce: options.bruteForce || { windowMs: Number(process.env.BRUTE_FORCE_WINDOW_MS || 15 * 60 * 1000), max: Number(process.env.BRUTE_FORCE_MAX || 10) }, redisUrl: options.redisUrl || process.env.REDIS_URL, csrf: typeof options.csrf !== 'undefined' ? options.csrf : process.env.CSRF_ENABLED === 'true', logging: options.logging !== false && process.env.LOGGING !== 'false', bodyLimit: options.bodyLimit || process.env.BODY_LIMIT || '10kb', ipWhitelist: options.ipWhitelist || (process.env.IP_WHITELIST ? process.env.IP_WHITELIST.split(',') : []), ipBlacklist: options.ipBlacklist || (process.env.IP_BLACKLIST ? process.env.IP_BLACKLIST.split(',') : []), slowloris: typeof options.slowloris === 'boolean' ? options.slowloris : process.env.SLOWLORIS_ENABLED !== 'false', headers: options.headers || {}, apiKeys: options.apiKeys || (process.env.API_KEYS ? JSON.parse(process.env.API_KEYS) : null), webhookSecret: options.webhookSecret || process.env.WEBHOOK_SECRET, oauth: { googleClientID: options.googleClientID || process.env.GOOGLE_CLIENT_ID, googleClientSecret: options.googleClientSecret || process.env.GOOGLE_CLIENT_SECRET, callbackURL: options.callbackURL || process.env.GOOGLE_CALLBACK_URL || '/auth/google/callback' } }; } /** * Main secure setup function */ function secureExpressSetup(app, options = {}) { const config = loadConfig(options); // Warn in production if secrets missing if (process.env.NODE_ENV === 'production') { if (!config.jwtSecret) console.warn('⚠️ JWT_SECRET missing!'); if (!config.sessionSecret) console.warn('⚠️ SESSION_SECRET missing!'); if (!config.encryptionKey) console.warn('⚠️ ENCRYPTION_KEY missing!'); } // 1. Helmet + strict headers app.use(helmet({ contentSecurityPolicy: false })); const defaultCSP = config.headers.contentSecurityPolicy || "default-src 'self'; img-src 'self' data:; connect-src 'self'; script-src 'none'; style-src 'none';"; app.use( setupHeaders({ contentSecurityPolicy: defaultCSP, strictTransportSecurity: config.headers.strictTransportSecurity || { maxAge: 31536000, includeSubDomains: true, preload: true } }) ); // 2. Slowloris if (config.slowloris) setupSlowloris(app); // 3. User Agent Parsing app.use(useragent.express()); // 4. IP Filtering if (config.ipWhitelist.length || config.ipBlacklist.length) { app.use(setupIpFilter(config.ipWhitelist, config.ipBlacklist)); } // 5. Rate Limiting app.use(setupRateLimit(config.rateLimit, config.redisUrl)); // 6. Brute Force const brute = setupBruteForce(config.bruteForce, config.redisUrl); app.locals.bruteForce = brute; app.post('/login', brute); app.post('/register', brute); app.post('/reset-password', brute); // 7. CORS app.use(setupCors(config.cors)); // 8. Body Parsing app.use(require('express').json({ limit: config.bodyLimit })); app.use(require('express').urlencoded({ extended: true, limit: config.bodyLimit })); // 9. Sanitization (XSS + NoSQL) setupSanitization(app); // 10. SQL Injection Protection app.use(setupSqlInjection()); // 11. Path Traversal Protection app.use(setupPathTraversal()); // 12. HPP app.use(hpp()); // 13. Secret Detection app.use(setupSecretsDetection()); // 14. Logging if (config.logging) app.use(setupLogger()); // 15. Sessions if (config.sessionSecret) { setupSessionSecurity(app, config.sessionSecret, config.redisUrl); } // 16. JWT if (config.jwtSecret) { app.locals.jwtHelper = setupJwtSecurity(config.jwtSecret); } // 17. Encryption if (config.encryptionKey) { app.locals.encryption = setupEncryption(config.encryptionKey); } // 18. File Validation app.locals.fileValidation = setupFileValidation(); // 19. CSRF Protection if (config.csrf) { const csrf = setupCsrf(app); app.use((req, res, next) => { if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(req.method)) { return csrf.validateRequest(req, res, next); } next(); }); } // 20. Attach Helper Factories app.locals.helpers = { apiKey: (opt) => apiKeyFactory(opt), webhookSignature: (opt) => webhookSignatureFactory(opt), oauth: () => { try { return oauthFactory(config.oauth); } catch (err) { console.warn("⚠️ OAuth not configured:", err.message); return null; } }, rbac: requireRole }; // Auto-load API keys from env if (config.apiKeys) { app.locals.helpers.defaultApiKey = apiKeyFactory({ keys: config.apiKeys }); } // Auto-load webhook secret if (config.webhookSecret) { app.locals.helpers.defaultWebhook = webhookSignatureFactory({ secret: config.webhookSecret }); } // Final error handler app.use((err, req, res, next) => { if (!err) return next(); if (err.code === 'EBADCSRFTOKEN') { return res.status(403).json({ error: 'Invalid CSRF token' }); } if (err.status === 429) { return res.status(429).json({ error: 'Too many requests' }); } console.error('Security Error:', err); return res.status(500).json({ error: 'Internal security error' }); }); console.log('✅ All security middleware applied automatically.'); } module.exports = secureExpressSetup; module.exports.apiKey = apiKeyFactory; module.exports.webhookSignature = webhookSignatureFactory; module.exports.oauth = oauthFactory; module.exports.rbac = requireRole;