secure-express-setup
Version:
Military-grade one-command security setup for Express.js applications
243 lines (194 loc) • 7.92 kB
JavaScript
// 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;