1. Latar Belakang
Memasuki minggu keempat, sesi kelima pembelajaran Express.js di Perwira Learning Center membahas Security dan Best Practice Backend. Ini adalah sistem pertahanan berlapis dalam aplikasi:
Analogi Rumah:
text
┌─────────────────────────────────────────────────────────┐ │ PERTAHANAN RUMAH │ ├─────────────────────────────────────────────────────────┤ │ │ │ 🔑 ENVIRONMENT VARIABLES = Brankas rahasia │ │ → Kunci tidak ditaruh di sembarang tempat │ │ │ │ 🚪 CORS = Pintu dengan aturan tamu │ │ → Hanya tamu tertentu yang boleh masuk │ │ │ │ 🛡️ HELMET = Pagar dan sistem keamanan │ │ → Proteksi dari serangan dari luar │ │ │ │ 📜 HTTPS = Amplop surat rahasia │ │ → Isi surat tidak bisa dibaca orang lain │ │ │ │ 🧹 SANITIZATION = Membersihkan tamu yang mencurigakan│ │ → Hapus senjata tajam sebelum masuk │ │ │ │ ⏱️ RATE LIMITING = Batasi jumlah tamu │ │ → Cegah kerumunan dan serangan massal │ │ │ └─────────────────────────────────────────────────────────┘
Mengapa Keamanan Backend Penting?
javascript
/** * DAMPAK KEAMANAN YANG LEMAH * * ❌ TANPA ENV VARIABLE: * const DB_PASSWORD = "admin123" // Hardcoded di GitHub! * Semua orang bisa lihat password database * * ❌ TANPA CORS: * Website jahat bisa akses API kita dari domain manapun * Script di situs judi bisa curi data user kita * * ❌ TANPA HELMET: * Header X-Powered-By: Express (kasi tau musuh kita pake apa) * Bisa kena XSS, clickjacking, MIME sniffing * * ❌ TANPA VALIDASI: * User bisa input <script>alert('hacked')</script> * Kode jahat jalan di browser admin * * ❌ TANPA RATE LIMITING: * Attacker bisa spam 10.000 request/detik * Server down, semua user tidak bisa akses */
2. Alat dan Bahan
a. Perangkat Lunak
- Node.js & npm - Runtime dan package manager
- Express.js - Framework utama
- dotenv - Environment variables management
- cors - Cross-Origin Resource Sharing
- helmet - Security headers middleware
- express-rate-limit - Rate limiting
- express-slow-down - Slow down attacks
- csurf - CSRF protection
- xss-clean - XSS sanitization
- hpp - HTTP Parameter Pollution protection
- compression - Response compression
- express-mongo-sanitize - NoSQL injection prevention
- Postman - Security testing
- OWASP ZAP - Security scanning (optional)
b. Perangkat Keras
- Laptop/PC dengan spesifikasi standar
3. Pembahasan
3.1 Security Layer Architecture
javascript
/** * ARSITEKTUR KEAMANAN BERLAPIS * * ┌─────────────────────────────────────────────────────┐ * │ LAYER 7: HTTPS │ * │ Encrypted Communication │ * └─────────────────────┬───────────────────────────────┘ * │ * ┌─────────────────────▼───────────────────────────────┐ * │ LAYER 6: RATE LIMITING │ * │ Prevent Brute Force & DDoS │ * └─────────────────────┬───────────────────────────────┘ * │ * ┌─────────────────────▼───────────────────────────────┐ * │ LAYER 5: CORS │ * │ Cross-Origin Access Control │ * └─────────────────────┬───────────────────────────────┘ * │ * ┌─────────────────────▼───────────────────────────────┐ * │ LAYER 4: HELMET │ * │ Security Headers (HSTS, CSP, etc) │ * └─────────────────────┬───────────────────────────────┘ * │ * ┌─────────────────────▼───────────────────────────────┐ * │ LAYER 3: CSRF PROTECTION │ * │ Prevent Cross-Site Request Forgery │ * └─────────────────────┬───────────────────────────────┘ * │ * ┌─────────────────────▼───────────────────────────────┐ * │ LAYER 2: INPUT VALIDATION │ * │ XSS, SQL/NoSQL Injection Prevention │ * └─────────────────────┬───────────────────────────────┘ * │ * ┌─────────────────────▼───────────────────────────────┐ * │ LAYER 1: AUTHENTICATION │ * │ JWT, Session, API Key, OAuth │ * └─────────────────────────────────────────────────────┘ * * 🎯 SETIAP LAYER PERTAHANAN = SALAH SATU KEGAGALAN * TIDAK MEMBUAT SELURUH SISTEM RENTAN! */
3.2 Praktik Lengkap: Secure Express.js Boilerplate
Mari kita bangun aplikasi Express.js dengan standar keamanan enterprise:
bash
# 1. Buat folder project mkdir secure-express-api cd secure-express-api # 2. Inisialisasi project npm init -y # 3. Install dependencies keamanan npm install express dotenv cors helmet express-rate-limit npm install express-slow-down csurf cookie-parser npm install xss-clean hpp compression npm install express-mongo-sanitize npm install jsonwebtoken bcryptjs npm install joi celebrate # 4. Install development dependencies npm install -D nodemon # 5. Buat struktur folder mkdir -p src/{config,middleware,routes,controllers,utils}
A. Environment Variables Configuration
.env.example
env
# ==================================== # 🚀 APPLICATION CONFIGURATION # ==================================== # Node Environment NODE_ENV=development PORT=3000 APP_NAME=Secure Express API APP_URL=http://localhost:3000 API_VERSION=v1 # ==================================== # 🔐 SECURITY CONFIGURATION # ==================================== # JWT Configuration JWT_SECRET=your-super-secret-jwt-key-min-32-chars-here JWT_REFRESH_SECRET=your-super-secret-refresh-key-min-32-chars-here JWT_EXPIRES_IN=15m JWT_REFRESH_EXPIRES_IN=7d JWT_ISSUER=secure-api JWT_AUDIENCE=secure-api-client # Password Configuration BCRYPT_SALT_ROUNDS=12 PASSWORD_MIN_LENGTH=8 PASSWORD_MAX_LENGTH=128 # ==================================== # 🗄️ DATABASE CONFIGURATION # ==================================== # MongoDB (example) DB_HOST=localhost DB_PORT=27017 DB_NAME=secure_api DB_USER=admin DB_PASSWORD=your-secure-db-password DB_SSL=true # ==================================== # 🌐 CORS CONFIGURATION # ==================================== # Allowed origins (comma-separated) CORS_ORIGIN=http://localhost:3000,http://localhost:5173,https://yourdomain.com CORS_CREDENTIALS=true CORS_MAX_AGE=86400 # ==================================== # ⏱️ RATE LIMITING CONFIGURATION # ==================================== RATE_LIMIT_WINDOW_MS=900000 RATE_LIMIT_MAX_REQUESTS=100 RATE_LIMIT_MESSAGE=Too many requests, please try again later AUTH_RATE_LIMIT_WINDOW_MS=900000 AUTH_RATE_LIMIT_MAX_REQUESTS=5 # ==================================== # 📧 EMAIL CONFIGURATION # ==================================== SMTP_HOST=smtp.gmail.com SMTP_PORT=587 SMTP_SECURE=false SMTP_USER=your-email@gmail.com SMTP_PASSWORD=your-app-password # ==================================== # ☁️ CLOUD STORAGE (AWS S3 example) # ==================================== AWS_ACCESS_KEY_ID=your-access-key AWS_SECRET_ACCESS_KEY=your-secret-key AWS_REGION=ap-southeast-1 AWS_BUCKET_NAME=your-bucket-name # ==================================== # 📝 LOGGING CONFIGURATION # ==================================== LOG_LEVEL=info LOG_FILE_PATH=logs/app.log LOG_MAX_SIZE=20m LOG_MAX_FILES=14d # ==================================== # 🚨 ALERT CONFIGURATION # ==================================== SLACK_WEBHOOK_URL=https://hooks.slack.com/services/xxx/yyy/zzz ADMIN_EMAIL=admin@yourdomain.com
.env (Jangan commit ke git!)
env
NODE_ENV=development PORT=3000 APP_NAME=Secure Express API # Ganti dengan secret yang kuat! JWT_SECRET=8f7a9d3e1b5c6a7d8e9f0a1b2c3d4e5f6a7b8c9d JWT_REFRESH_SECRET=e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4 BCRYPT_SALT_ROUNDS=12 CORS_ORIGIN=http://localhost:3000,http://localhost:5173
.gitignore
gitignore
# Environment variables .env .env.local .env.development.local .env.test.local .env.production.local # Dependencies node_modules/ # Logs logs/ *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Runtime data pids *.pid *.seed *.pid.lock # Coverage coverage .nyc_output # Build dist build # IDE .vscode/ .idea/ *.swp *.swo *~ .DS_Store # AWS .aws # Secrets *.pem *.key *.crt
B. Security Configuration Module
src/config/security.js
javascript
/** * SECURITY CONFIGURATION * Semua konfigurasi keamanan dalam satu tempat */ const crypto = require('crypto'); module.exports = { // ==================================== // 🛡️ HELMET CONFIGURATION // ==================================== helmet: { contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'"], imgSrc: ["'self'", "data:", "https:"], connectSrc: ["'self'", process.env.APP_URL], fontSrc: ["'self'", "https:", "data:"], objectSrc: ["'none'"], mediaSrc: ["'self'"], frameSrc: ["'none'"], sandbox: ['allow-forms', 'allow-scripts', 'allow-same-origin'], reportUri: '/api/v1/security/csp-violation', upgradeInsecureRequests: process.env.NODE_ENV === 'production' ? [] : null } }, hsts: { maxAge: 31536000, // 1 year includeSubDomains: true, preload: true }, referrerPolicy: { policy: 'strict-origin-when-cross-origin' }, noSniff: true, xssFilter: true, hidePoweredBy: true, frameguard: { action: 'deny' }, dnsPrefetchControl: { allow: false }, ieNoOpen: true, permittedCrossDomainPolicies: { permittedPolicies: 'none' } }, // ==================================== // 🌐 CORS CONFIGURATION // ==================================== cors: { origin: (origin, callback) => { const whitelist = process.env.CORS_ORIGIN?.split(',') || []; // Allow requests with no origin (like mobile apps, curl, Postman) if (!origin || whitelist.includes(origin) || process.env.NODE_ENV !== 'production') { callback(null, true); } else { callback(new Error('CORS policy violation'), false); } }, credentials: true, methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], allowedHeaders: [ 'Content-Type', 'Authorization', 'X-Request-ID', 'X-API-Key', 'X-CSRF-Token', 'Accept', 'Origin', 'Cache-Control' ], exposedHeaders: [ 'X-RateLimit-Limit', 'X-RateLimit-Remaining', 'X-RateLimit-Reset', 'X-Request-ID' ], maxAge: parseInt(process.env.CORS_MAX_AGE) || 86400, preflightContinue: false, optionsSuccessStatus: 204 }, // ==================================== // ⏱️ RATE LIMITING CONFIGURATION // ==================================== rateLimit: { // General API rate limit api: { windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000, // 15 minutes max: parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) || 100, message: { success: false, error: { code: 'RATE_LIMIT_EXCEEDED', message: process.env.RATE_LIMIT_MESSAGE || 'Too many requests, please try again later', retryAfter: '15 minutes' } }, standardHeaders: true, legacyHeaders: false, skipSuccessfulRequests: false, keyGenerator: (req) => { // Use user ID if authenticated, otherwise IP return req.user?.id || req.ip; }, handler: (req, res) => { res.status(429).json({ success: false, error: { code: 'RATE_LIMIT_EXCEEDED', message: 'Too many requests, please try again later', retryAfter: Math.ceil(req.rateLimit.resetTime / 1000), limit: req.rateLimit.limit, remaining: req.rateLimit.remaining } }); }, skip: (req) => { // Skip rate limiting for health checks return req.path === '/health' || req.path === '/ready'; } }, // Strict rate limit for authentication endpoints auth: { windowMs: 15 * 60 * 1000, // 15 minutes max: 5, // 5 attempts skipSuccessfulRequests: true, // Only count failed attempts message: { success: false, error: { code: 'AUTH_RATE_LIMIT_EXCEEDED', message: 'Too many authentication attempts. Please try again later.', retryAfter: '15 minutes' } } }, // Very strict for password reset passwordReset: { windowMs: 60 * 60 * 1000, // 1 hour max: 3, // 3 attempts message: { success: false, error: { code: 'PASSWORD_RESET_LIMIT_EXCEEDED', message: 'Too many password reset attempts. Please try again in 1 hour.', retryAfter: '1 hour' } } }, // Strict for registration registration: { windowMs: 60 * 60 * 1000, // 1 hour max: 5, // 5 registrations per IP per hour keyGenerator: (req) => req.ip, message: { success: false, error: { code: 'REGISTRATION_LIMIT_EXCEEDED', message: 'Too many registration attempts from your IP. Please try again later.', retryAfter: '1 hour' } } } }, // ==================================== // 🔐 JWT CONFIGURATION // ==================================== jwt: { access: { secret: process.env.JWT_SECRET, expiresIn: process.env.JWT_EXPIRES_IN || '15m', algorithm: 'HS256', issuer: process.env.JWT_ISSUER || 'secure-api', audience: process.env.JWT_AUDIENCE || 'secure-api-client' }, refresh: { secret: process.env.JWT_REFRESH_SECRET, expiresIn: process.env.JWT_REFRESH_EXPIRES_IN || '7d', algorithm: 'HS256', issuer: process.env.JWT_ISSUER || 'secure-api', audience: process.env.JWT_AUDIENCE || 'secure-api-client' } }, // ==================================== // 🔑 PASSWORD CONFIGURATION // ==================================== password: { minLength: parseInt(process.env.PASSWORD_MIN_LENGTH) || 8, maxLength: parseInt(process.env.PASSWORD_MAX_LENGTH) || 128, saltRounds: parseInt(process.env.BCRYPT_SALT_ROUNDS) || 12, requirements: { uppercase: true, lowercase: true, numbers: true, special: true }, specialChars: '!@#$%^&*()_+-=[]{};\':",./<>?', passwordHistory: 5, // Remember last 5 passwords expiryDays: 90 // Password expires after 90 days }, // ==================================== // 🍪 COOKIE CONFIGURATION // ==================================== cookie: { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict', maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days domain: process.env.COOKIE_DOMAIN || undefined, path: '/', signed: true // Sign cookies to prevent tampering }, // ==================================== // 🧹 INPUT SANITIZATION // ==================================== sanitization: { // Allowlisted HTML tags (for rich text) allowedHtmlTags: [ 'b', 'i', 'em', 'strong', 'p', 'br', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote' ], // Allowlisted HTML attributes allowedHtmlAttributes: ['href', 'title', 'target'], // Maximum length for text fields maxStringLength: 5000, // Maximum depth for nested objects maxDepth: 10 }, // ==================================== // 🚨 SECURITY ALERTS // ==================================== alerts: { slackWebhook: process.env.SLACK_WEBHOOK_URL, adminEmail: process.env.ADMIN_EMAIL, thresholds: { failedLogins: 10, // Alert after 10 failed logins serverErrors: 50, // Alert after 50 server errors in 1 hour rateLimitViolations: 100 // Alert after 100 rate limit violations } } };
C. Security Middleware Suite
src/middleware/security.middleware.js
javascript
/** * SECURITY MIDDLEWARE SUITE * Semua middleware keamanan dalam satu file */ const helmet = require('helmet'); const cors = require('cors'); const rateLimit = require('express-rate-limit'); const slowDown = require('express-slow-down'); const xss = require('xss-clean'); const hpp = require('hpp'); const mongoSanitize = require('express-mongo-sanitize'); const csrf = require('csurf'); const compression = require('compression'); const crypto = require('crypto'); const config = require('../config/security'); const logger = require('../utils/logger'); class SecurityMiddleware { /** * ============= 1. HELMET (SECURITY HEADERS) ============= * Melindungi dari XSS, clickjacking, MIME sniffing, dll */ helmet() { return helmet(config.helmet); } /** * ============= 2. CORS (CROSS-ORIGIN RESOURCE SHARING) ============= * Mengontrol domain mana yang bisa mengakses API */ cors() { return cors(config.cors); } /** * ============= 3. RATE LIMITING ============= * Mencegah brute force dan DDoS attacks */ rateLimiter(type = 'api') { return rateLimit(config.rateLimit[type]); } /** * ============= 4. SLOW DOWN ============= * Perlahan-lahan memperlambat request setelah batas tertentu */ slowDown() { return slowDown({ windowMs: 15 * 60 * 1000, // 15 minutes delayAfter: 50, // Allow 50 requests without delay delayMs: (hits) => hits * 100, // Add 100ms delay per hit above limit maxDelayMs: 10000, // Max 10 second delay skip: (req) => req.user?.role === 'admin' }); } /** * ============= 5. XSS CLEAN ============= * Membersihkan input dari XSS attacks */ xssClean() { return xss(); } /** * ============= 6. HPP (HTTP PARAMETER POLLUTION) ============= * Mencegah parameter pollution attack */ hpp() { return hpp(); } /** * ============= 7. NoSQL INJECTION PREVENTION ============= * Membersihkan input dari NoSQL injection */ mongoSanitize() { return mongoSanitize({ replaceWith: '_', allowDots: true, onSanitize: ({ req, key }) => { logger.warn('NoSQL injection attempt detected', { ip: req.ip, path: req.path, key }); } }); } /** * ============= 8. CSRF PROTECTION ============= * Mencegah Cross-Site Request Forgery */ csrfProtection() { return csrf({ cookie: { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict', key: '_csrf' } }); } /** * ============= 9. COMPRESSION ============= * Kompres response untuk performa */ compression() { return compression({ filter: (req, res) => { if (req.headers['x-no-compression']) { return false; } return compression.filter(req, res); }, level: 6 // Compression level (1-9) }); } /** * ============= 10. REQUEST ID ============= * Generate unique ID untuk setiap request (tracing) */ requestId() { return (req, res, next) => { req.id = req.headers['x-request-id'] || `req-${crypto.randomBytes(16).toString('hex')}`; res.setHeader('X-Request-ID', req.id); next(); }; } /** * ============= 11. CSP VIOLATION HANDLER ============= * Menangani laporan CSP violation */ cspViolationHandler() { return (req, res) => { const report = req.body; logger.warn('CSP Violation', { 'document-uri': report['csp-report']?.['document-uri'], 'blocked-uri': report['csp-report']?.['blocked-uri'], 'violated-directive': report['csp-report']?.['violated-directive'], 'original-policy': report['csp-report']?.['original-policy'], ip: req.ip, userAgent: req.headers['user-agent'] }); res.status(204).end(); }; } /** * ============= 12. SSL REDIRECT ============= * Redirect HTTP ke HTTPS di production */ sslRedirect() { return (req, res, next) => { if (process.env.NODE_ENV === 'production' && !req.secure) { return res.redirect(`https://${req.headers.host}${req.url}`); } next(); }; } /** * ============= 13. HIDE SENSITIVE HEADERS ============= * Hapus header yang tidak perlu */ hideSensitiveHeaders() { return (req, res, next) => { res.removeHeader('X-Powered-By'); res.removeHeader('Server'); res.removeHeader('X-AspNet-Version'); res.removeHeader('X-AspNetMvc-Version'); next(); }; } /** * ============= 14. JSON PARSER WITH SIZE LIMIT ============= * Batasi ukuran request body */ jsonParser() { return (req, res, next) => { express.json({ limit: '10mb', verify: (req, res, buf) => { try { JSON.parse(buf); } catch (e) { res.status(400).json({ success: false, error: { code: 'INVALID_JSON', message: 'Invalid JSON payload' } }); throw new Error('Invalid JSON'); } } })(req, res, next); }; } /** * ============= 15. URL ENCODED PARSER ============= * Parse URL-encoded bodies dengan size limit */ urlEncodedParser() { return express.urlencoded({ extended: true, limit: '10mb' }); } /** * ============= 16. API KEY AUTHENTICATION ============= * Validasi API key untuk service-to-service communication */ apiKeyAuth() { return (req, res, next) => { const apiKey = req.headers['x-api-key']; const validApiKeys = process.env.API_KEYS?.split(',') || []; // Skip untuk internal routes if (req.path.startsWith('/internal')) { if (!apiKey || !validApiKeys.includes(apiKey)) { logger.warn('Invalid API key attempt', { ip: req.ip, path: req.path, apiKey: apiKey?.substring(0, 8) }); return res.status(401).json({ success: false, error: { code: 'INVALID_API_KEY', message: 'Invalid API key' } }); } } next(); }; } /** * ============= 17. CONTENT SECURITY POLICY LOGGER ============= * Log CSP violations untuk monitoring */ static logCspViolation(req, res, next) { res.on('finish', () => { if (res.statusCode === 403 && res.getHeader('X-CSP-Violation')) { logger.warn('CSP violation blocked', { url: req.url, ip: req.ip, userAgent: req.headers['user-agent'] }); } }); next(); } /** * ============= 18. SECURE COOKIE SETTER ============= * Set cookie dengan konfigurasi aman */ static setSecureCookie(res, name, value, options = {}) { const defaultOptions = { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict', path: '/', maxAge: 7 * 24 * 60 * 60 * 1000, signed: true }; res.cookie(name, value, { ...defaultOptions, ...options }); } /** * ============= 19. SECURITY AUDIT LOGGER ============= * Log semua event keamanan penting */ static logSecurityEvent(event, req, details = {}) { const logData = { event, timestamp: new Date().toISOString(), ip: req.ip, userAgent: req.headers['user-agent'], userId: req.user?.id, email: req.user?.email, path: req.path, method: req.method, requestId: req.id, ...details }; // Log ke file khusus security logger.security(logData); // Kirim alert untuk event kritis if (event.includes('FAILED') || event.includes('VIOLATION')) { this.sendSecurityAlert(event, logData); } } /** * ============= 20. SECURITY ALERT SENDER ============= * Kirim alert ke Slack/Email untuk event kritis */ static async sendSecurityAlert(event, data) { // Skip di development if (process.env.NODE_ENV === 'development') return; // Send to Slack if (process.env.SLACK_WEBHOOK_URL) { try { await fetch(process.env.SLACK_WEBHOOK_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: `🚨 *Security Alert: ${event}*\n`, attachments: [{ color: 'danger', fields: Object.entries(data).map(([key, value]) => ({ title: key, value: String(value), short: true })) }] }) }); } catch (error) { logger.error('Failed to send Slack alert', error); } } } } module.exports = new SecurityMiddleware();
D. Environment Variable Validator
src/config/envValidator.js
javascript
/** * ENVIRONMENT VARIABLE VALIDATOR * Validasi semua env variable saat startup */ const logger = require('../utils/logger'); class EnvValidator { constructor() { this.errors = []; this.warnings = []; } /** * Validasi semua environment variables */ validate() { this.validateNodeEnv(); this.validateJwtSecrets(); this.validateDatabaseConfig(); this.validateCorsConfig(); this.validateRateLimits(); if (this.errors.length > 0) { logger.error('❌ Environment validation failed!'); this.errors.forEach(error => logger.error(` • ${error}`)); throw new Error('Environment validation failed'); } if (this.warnings.length > 0) { logger.warn('⚠️ Environment warnings:'); this.warnings.forEach(warning => logger.warn(` • ${warning}`)); } logger.info('✅ Environment validation passed'); return true; } /** * Validasi NODE_ENV */ validateNodeEnv() { const validEnvs = ['development', 'test', 'production']; if (!process.env.NODE_ENV) { process.env.NODE_ENV = 'development'; this.warnings.push('NODE_ENV not set, defaulting to "development"'); } else if (!validEnvs.includes(process.env.NODE_ENV)) { this.errors.push(`NODE_ENV must be one of: ${validEnvs.join(', ')}`); } } /** * Validasi JWT secrets */ validateJwtSecrets() { // JWT Secret if (!process.env.JWT_SECRET) { this.errors.push('JWT_SECRET is required'); } else if (process.env.JWT_SECRET.length < 32) { this.errors.push('JWT_SECRET must be at least 32 characters long'); } // JWT Refresh Secret if (!process.env.JWT_REFRESH_SECRET) { if (process.env.NODE_ENV === 'production') { this.errors.push('JWT_REFRESH_SECRET is required in production'); } else { process.env.JWT_REFRESH_SECRET = process.env.JWT_SECRET; this.warnings.push('JWT_REFRESH_SECRET not set, using JWT_SECRET (development only)'); } } // JWT Expiration if (!process.env.JWT_EXPIRES_IN) { process.env.JWT_EXPIRES_IN = '15m'; this.warnings.push('JWT_EXPIRES_IN not set, defaulting to "15m"'); } if (!process.env.JWT_REFRESH_EXPIRES_IN) { process.env.JWT_REFRESH_EXPIRES_IN = '7d'; this.warnings.push('JWT_REFRESH_EXPIRES_IN not set, defaulting to "7d"'); } } /** * Validasi database configuration */ validateDatabaseConfig() { // Skip if no DB config (might be file-based) if (!process.env.DB_HOST && !process.env.DATABASE_URL) { this.warnings.push('No database configuration found'); return; } // Database URL if (process.env.DATABASE_URL) { if (!process.env.DATABASE_URL.startsWith('postgresql://') && !process.env.DATABASE_URL.startsWith('mongodb://') && !process.env.DATABASE_URL.startsWith('mysql://')) { this.warnings.push('DATABASE_URL format may be invalid'); } return; } // Individual DB config if (!process.env.DB_HOST) { this.errors.push('DB_HOST is required when using individual DB config'); } if (!process.env.DB_NAME) { this.errors.push('DB_NAME is required when using individual DB config'); } if (!process.env.DB_USER) { this.warnings.push('DB_USER not set, may cause issues'); } if (!process.env.DB_PASSWORD && process.env.NODE_ENV === 'production') { this.errors.push('DB_PASSWORD is required in production'); } } /** * Validasi CORS configuration */ validateCorsConfig() { if (!process.env.CORS_ORIGIN) { if (process.env.NODE_ENV === 'production') { this.errors.push('CORS_ORIGIN is required in production'); } else { process.env.CORS_ORIGIN = 'http://localhost:3000'; this.warnings.push('CORS_ORIGIN not set, defaulting to "http://localhost:3000"'); } } // Validate CORS origins format if (process.env.CORS_ORIGIN) { const origins = process.env.CORS_ORIGIN.split(','); origins.forEach(origin => { origin = origin.trim(); if (origin !== '*' && !origin.startsWith('http://') && !origin.startsWith('https://')) { this.warnings.push(`CORS origin "${origin}" should start with http:// or https://`); } }); } } /** * Validasi rate limiting */ validateRateLimits() { if (!process.env.RATE_LIMIT_MAX_REQUESTS) { process.env.RATE_LIMIT_MAX_REQUESTS = '100'; this.warnings.push('RATE_LIMIT_MAX_REQUESTS not set, defaulting to 100'); } const maxRequests = parseInt(process.env.RATE_LIMIT_MAX_REQUESTS); if (isNaN(maxRequests) || maxRequests < 1) { this.errors.push('RATE_LIMIT_MAX_REQUESTS must be a positive number'); } if (!process.env.RATE_LIMIT_WINDOW_MS) { process.env.RATE_LIMIT_WINDOW_MS = '900000'; // 15 minutes this.warnings.push('RATE_LIMIT_WINDOW_MS not set, defaulting to 15 minutes'); } } /** * Validasi bcrypt salt rounds */ validateBcryptSaltRounds() { if (!process.env.BCRYPT_SALT_ROUNDS) { process.env.BCRYPT_SALT_ROUNDS = '12'; this.warnings.push('BCRYPT_SALT_ROUNDS not set, defaulting to 12'); } const saltRounds = parseInt(process.env.BCRYPT_SALT_ROUNDS); if (isNaN(saltRounds) || saltRounds < 10 || saltRounds > 20) { this.warnings.push('BCRYPT_SALT_ROUNDS should be between 10 and 20'); } } /** * Validasi port */ validatePort() { if (!process.env.PORT) { process.env.PORT = '3000'; this.warnings.push('PORT not set, defaulting to 3000'); } const port = parseInt(process.env.PORT); if (isNaN(port) || port < 1 || port > 65535) { this.errors.push('PORT must be a valid port number (1-65535)'); } } } module.exports = new EnvValidator();
E. Complete Secure Express Application
src/app.js
javascript
/** * SECURE EXPRESS APPLICATION * Enterprise-grade security configuration */ const express = require('express'); const cookieParser = require('cookie-parser'); const path = require('path'); // Security middleware const securityMiddleware = require('./middleware/security.middleware'); const envValidator = require('./config/envValidator'); const config = require('./config/security'); const logger = require('./utils/logger'); // Load environment variables require('dotenv').config(); // Validate environment variables envValidator.validate(); const app = express(); // ==================================== // 🎯 GLOBAL MIDDLEWARE - ORDER IS CRITICAL! // ==================================== // 1. Request ID (must be first for tracing) app.use(securityMiddleware.requestId()); // 2. SSL Redirect (production only) if (process.env.NODE_ENV === 'production') { app.use(securityMiddleware.sslRedirect()); } // 3. Security Headers app.use(securityMiddleware.helmet()); // 4. CORS app.use(securityMiddleware.cors()); // 5. Compression app.use(securityMiddleware.compression()); // 6. Rate Limiting (apply early) app.use('/api', securityMiddleware.rateLimiter('api')); app.use('/api/auth', securityMiddleware.rateLimiter('auth')); app.use('/api/auth/forgot-password', securityMiddleware.rateLimiter('passwordReset')); app.use('/api/auth/register', securityMiddleware.rateLimiter('registration')); // 7. Slow Down (gradual throttling) app.use(securityMiddleware.slowDown()); // 8. Body Parsers with size limits app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true, limit: '10mb' })); app.use(cookieParser(process.env.JWT_SECRET)); // 9. API Key Authentication (for internal services) app.use(securityMiddleware.apiKeyAuth()); // 10. Security sanitization app.use(securityMiddleware.xssClean()); app.use(securityMiddleware.hpp()); app.use(securityMiddleware.mongoSanitize()); // 11. Remove sensitive headers app.use(securityMiddleware.hideSensitiveHeaders()); // 12. CSP Violation endpoint app.post('/api/v1/security/csp-violation', express.json({ type: 'application/csp-report' }), securityMiddleware.cspViolationHandler() ); // ==================================== // 📊 HEALTH CHECKS // ==================================== // Public health check (no rate limit) app.get('/health', (req, res) => { res.json({ status: 'healthy', timestamp: new Date().toISOString(), uptime: process.uptime(), environment: process.env.NODE_ENV, version: process.env.npm_package_version || '1.0.0', requestId: req.id }); }); // Readiness probe (for Kubernetes) app.get('/ready', (req, res) => { // Check database connection, etc. const isReady = true; // Implement actual checks if (isReady) { res.json({ status: 'ready', requestId: req.id }); } else { res.status(503).json({ status: 'not ready', requestId: req.id }); } }); // ==================================== // 🚀 API ROUTES // ==================================== app.use('/api/v1', require('./routes')); // ==================================== // 📁 STATIC FILES (if needed) // ==================================== if (process.env.NODE_ENV === 'production') { app.use(express.static(path.join(__dirname, '../public'))); app.get('*', (req, res) => { res.sendFile(path.join(__dirname, '../public/index.html')); }); } // ==================================== // 🚨 404 HANDLER // ==================================== app.use((req, res) => { logger.warn('404 Not Found', { requestId: req.id, method: req.method, url: req.originalUrl, ip: req.ip }); res.status(404).json({ success: false, error: { code: 'NOT_FOUND', message: `Cannot ${req.method} ${req.originalUrl}`, requestId: req.id } }); }); // ==================================== // 🔥 GLOBAL ERROR HANDLER // ==================================== app.use((err, req, res, next) => { // Log error logger.error('Unhandled error:', { requestId: req.id, error: err.message, stack: err.stack, code: err.code, statusCode: err.statusCode || 500 }); // Security: Don't expose error details in production const isProduction = process.env.NODE_ENV === 'production'; res.status(err.statusCode || 500).json({ success: false, error: { code: err.code || 'INTERNAL_SERVER_ERROR', message: isProduction ? 'Internal server error' : err.message, requestId: req.id, ...(isProduction ? {} : { stack: err.stack }) } }); }); // ==================================== // 🛡️ SECURITY AUDIT LOGGING // ==================================== // Log all requests in production if (process.env.NODE_ENV === 'production') { app.use((req, res, next) => { res.on('finish', () => { if (res.statusCode >= 400) { securityMiddleware.logSecurityEvent('HTTP_ERROR', req, { statusCode: res.statusCode, responseTime: res.getHeader('X-Response-Time') }); } }); next(); }); } module.exports = app;
F. CSRF Protection Implementation
src/middleware/csrf.middleware.js
javascript
/** * CSRF PROTECTION MIDDLEWARE * Mencegah Cross-Site Request Forgery attacks */ const csrf = require('csurf'); const { ValidationError } = require('../errors/AppError'); const logger = require('../utils/logger'); class CsrfMiddleware { constructor() { this.protection = csrf({ cookie: { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'strict', key: '_csrf', maxAge: 3600 // 1 hour }, value: (req) => { // Check in header first, then body return req.headers['x-csrf-token'] || req.headers['xsrf-token'] || req.body._csrf; } }); } /** * CSRF Protection middleware */ protect() { return (req, res, next) => { // Skip CSRF for GET, HEAD, OPTIONS if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) { return next(); } // Skip CSRF for API routes that use JWT if (req.path.startsWith('/api/') && req.headers.authorization) { return next(); } this.protection(req, res, (err) => { if (err) { logger.warn('CSRF attack detected', { requestId: req.id, ip: req.ip, path: req.path, method: req.method, userId: req.user?.id }); return next(new ValidationError('Invalid CSRF token', [ { field: '_csrf', message: 'CSRF token is invalid or missing' } ])); } next(); }); }; } /** * Generate CSRF token */ generateToken(req, res) { return { _csrf: req.csrfToken() }; } /** * CSRF Error handler */ errorHandler(err, req, res, next) { if (err.code === 'EBADCSRFTOKEN') { return res.status(403).json({ success: false, error: { code: 'INVALID_CSRF_TOKEN', message: 'Invalid CSRF token', requestId: req.id } }); } next(err); } } module.exports = new CsrfMiddleware();
G. Input Validation & Sanitization
src/utils/sanitizer.js
javascript
/** * INPUT SANITIZATION UTILITY * Membersihkan input dari karakter berbahaya */ const sanitizeHtml = require('sanitize-html'); class Sanitizer { /** * Sanitize string untuk mencegah XSS */ static xss(input) { if (typeof input !== 'string') return input; return sanitizeHtml(input, { allowedTags: [], // No HTML tags allowed allowedAttributes: {}, disallowedTagsMode: 'discard', textFilter: (text) => { // Escape any remaining HTML entities return text .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, ''') .replace(/\//g, '/'); } }); } /** * Sanitize object recursively */ static deepSanitize(obj) { if (!obj || typeof obj !== 'object') { return this.xss(obj); } if (Array.isArray(obj)) { return obj.map(item => this.deepSanitize(item)); } const sanitized = {}; for (const [key, value] of Object.entries(obj)) { sanitized[key] = this.deepSanitize(value); } return sanitized; } /** * Sanitize email */ static email(email) { if (typeof email !== 'string') return ''; return email .trim() .toLowerCase() .replace(/\s+/g, '') .replace(/[<>()\[\]\\,;:%#^$@!~&*?]/g, ''); // Remove dangerous chars } /** * Sanitize phone number */ static phone(phone) { if (typeof phone !== 'string') return ''; // Remove all non-digit characters except + return phone.replace(/[^\d+]/g, ''); } /** * Sanitize URL */ static url(url) { if (typeof url !== 'string') return ''; try { const parsed = new URL(url); // Only allow HTTP and HTTPS if (!['http:', 'https:'].includes(parsed.protocol)) { return ''; } return parsed.toString(); } catch { return ''; } } /** * Sanitize filename */ static filename(filename) { if (typeof filename !== 'string') return ''; // Remove path traversal attempts let clean = filename.replace(/\.\./g, '') .replace(/[/\\]/g, ''); // Remove non-printable characters clean = clean.replace(/[\x00-\x1f\x80-\x9f]/g, ''); // Limit length return clean.slice(0, 255); } /** * Sanitize SQL input (basic) */ static sql(input) { if (typeof input !== 'string') return input; // Remove SQL keywords (case insensitive) const sqlKeywords = [ 'SELECT', 'INSERT', 'UPDATE', 'DELETE', 'DROP', 'UNION', 'ALTER', 'CREATE', 'WHERE', 'FROM', 'JOIN', '--', ';' ]; let sanitized = input; sqlKeywords.forEach(keyword => { const regex = new RegExp(keyword, 'gi'); sanitized = sanitized.replace(regex, ''); }); return sanitized; } /** * Sanitize MongoDB input */ static mongodb(input) { if (typeof input === 'object') { // Remove MongoDB operators const sanitized = {}; for (const [key, value] of Object.entries(input)) { if (!key.startsWith('$')) { sanitized[key] = value; } } return sanitized; } return input; } } module.exports = Sanitizer;
3.3 Security Headers Explanation
javascript
/** * SECURITY HEADERS EXPLANATION * * ┌─────────────────────────────────────────────────────────────┐ * │ 1. Content-Security-Policy (CSP) │ * ├─────────────────────────────────────────────────────────────┤ * │ Apa: Browser directive untuk sumber daya yang diizinkan │ * │ Cegah: XSS, data injection, clickjacking │ * │ Contoh: default-src 'self'; script-src 'self'; │ * └─────────────────────────────────────────────────────────────┘ * * ┌─────────────────────────────────────────────────────────────┐ * │ 2. Strict-Transport-Security (HSTS) │ * ├─────────────────────────────────────────────────────────────┤ * │ Apa: Memaksa browser selalu menggunakan HTTPS │ * │ Cegah: SSL stripping, man-in-the-middle │ * │ Contoh: max-age=31536000; includeSubDomains │ * └─────────────────────────────────────────────────────────────┘ * * ┌─────────────────────────────────────────────────────────────┐ * │ 3. X-Frame-Options │ * ├─────────────────────────────────────────────────────────────┤ * │ Apa: Mencegah website di-frame oleh situs lain │ * │ Cegah: Clickjacking attacks │ * │ Contoh: DENY nebo SAMEORIGIN │ * └─────────────────────────────────────────────────────────────┘ * * ┌─────────────────────────────────────────────────────────────┐ * │ 4. X-Content-Type-Options │ * ├─────────────────────────────────────────────────────────────┤ * │ Apa: Mencegah browser "menebak" content type │ * │ Cegah: MIME sniffing attacks │ * │ Contoh: nosniff │ * └─────────────────────────────────────────────────────────────┘ * * ┌─────────────────────────────────────────────────────────────┐ * │ 5. Referrer-Policy │ * ├─────────────────────────────────────────────────────────────┤ * │ Apa: Mengontrol informasi referrer yang dikirim │ * │ Cegah: Information leakage │ * │ Contoh: strict-origin-when-cross-origin │ * └─────────────────────────────────────────────────────────────┘ * * ┌─────────────────────────────────────────────────────────────┐ * │ 6. Permissions-Policy │ * ├─────────────────────────────────────────────────────────────┤ * │ Apa: Mengontrol fitur browser yang bisa digunakan │ * │ Cegah: Unwanted access to camera, mic, location │ * │ Contoh: geolocation=(self), microphone=() │ * └─────────────────────────────────────────────────────────────┘ * * ┌─────────────────────────────────────────────────────────────┐ * │ 7. X-XSS-Protection │ * ├─────────────────────────────────────────────────────────────┤ * │ Apa: Mengaktifkan XSS filter di browser │ * │ Cegah: Reflected XSS attacks │ * │ Contoh: 1; mode=block │ * └─────────────────────────────────────────────────────────────┘ */
3.4 Common Vulnerabilities & Prevention
javascript
/** * COMMON WEB VULNERABILITIES & PREVENTION * Berdasarkan OWASP Top 10 */ const owaspPrevention = { /** * 1. SQL INJECTION * Attack: ' OR '1'='1' -- * Prevent: Parameterized queries, ORM, input validation */ sqlInjection: { vulnerable: ` // ❌ JANGAN PERNAH LAKUKAN INI! const query = \`SELECT * FROM users WHERE email = '${email}'\`; db.query(query); `, secure: ` // ✅ SELALU GUNAKAN PARAMETERIZED QUERIES const query = 'SELECT * FROM users WHERE email = ?'; db.query(query, [email]); // Atau gunakan ORM User.findOne({ where: { email } }); ` }, /** * 2. CROSS-SITE SCRIPTING (XSS) * Attack: <script>alert('hacked')</script> * Prevent: Escape output, sanitize input, CSP */ xss: { vulnerable: ` // ❌ JANGAN PERNAK LAKUKAN INI! res.send(\`<div>${userInput}</div>\`); `, secure: ` // ✅ SELALU ESCAPE OUTPUT res.send(\`<div>\${escapeHtml(userInput)}</div>\`); // Atau gunakan template engine yang auto-escape res.render('template', { userInput }); // Dan sanitize input const clean = xss(input); ` }, /** * 3. CROSS-SITE REQUEST FORGERY (CSRF) * Attack: <img src="http://bank.com/transfer?to=hacker&amount=1000000"> * Prevent: CSRF tokens, SameSite cookies, Double submit */ csrf: { vulnerable: ` // ❌ JANGAN LAKUKAN INI UNTUK STATE-CHANGING OPERATIONS app.post('/transfer', (req, res) => { // No CSRF protection! }); `, secure: ` // ✅ SELALU GUNAKAN CSRF PROTECTION app.use(csrf()); app.post('/transfer', csrfProtection, (req, res) => { // Valid CSRF token required }); ` }, /** * 4. INSECURE DIRECT OBJECT REFERENCES (IDOR) * Attack: /api/users/123 -> ubah jadi /api/users/456 * Prevent: Authorization check, ownership validation */ idor: { vulnerable: ` // ❌ JANGAN LAKUKAN INI! app.get('/api/users/:id', (req, res) => { const user = User.findById(req.params.id); res.json(user); // No authorization check! }); `, secure: ` // ✅ SELALU CEK AUTHORIZATION app.get('/api/users/:id', authenticate, (req, res) => { const user = User.findById(req.params.id); // Cek ownership atau role if (req.user.id !== user.id && req.user.role !== 'admin') { throw new ForbiddenError(); } res.json(user); }); ` }, /** * 5. SECURITY MISCONFIGURATION * Attack: Default credentials, verbose errors, outdated software * Prevent: Hardening, env variables, security headers */ misconfiguration: { vulnerable: ` // ❌ JANGAN LAKUKAN INI! app.use(express.json()); // No helmet, no rate limiting, no CORS // X-Powered-By: Express (exposed!) // Stack traces in production! `, secure: ` // ✅ SELALU KONFIGURASI DENGAN AMAN app.use(helmet()); app.use(cors({ origin: whitelist, credentials: true })); app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 })); app.disable('x-powered-by'); // Environment variables untuk secrets const secret = process.env.JWT_SECRET; ` }, /** * 6. SENSITIVE DATA EXPOSURE * Attack: Steal passwords, credit cards, personal data * Prevent: Encryption, HTTPS, no sensitive data in JWT */ sensitiveData: { vulnerable: ` // ❌ JANGAN LAKUKAN INI! const user = { id: 1, email: 'user@email.com', password: 'plaintext123', // ⚠️ creditCard: '4111-1111-1111-1111', // ⚠️ ssn: '123-45-6789' // ⚠️ }; // Jangan simpan password plaintext! // Jangan include di JWT! `, secure: ` // ✅ SELALU ENKRIPSI DATA SENSITIF const hashedPassword = await bcrypt.hash(password, 12); // Jangan pernah kirim password ke client const { password, ...userWithoutPassword } = user; // HTTPS di production // Encrypt data at rest ` }, /** * 7. INSUFFICIENT LOGGING & MONITORING * Attack: Attackers can probe system without detection * Prevent: Comprehensive logging, alerts, audit trails */ logging: { vulnerable: ` // ❌ JANGAN LAKUKAN INI! app.post('/login', (req, res) => { // No logging of failed attempts! if (loginSuccessful) { res.json({ success: true }); } }); `, secure: ` // ✅ SELALU LOG EVENT PENTING app.post('/login', (req, res) => { if (!loginSuccessful) { logger.warn('Failed login attempt', { email: req.body.email, ip: req.ip, userAgent: req.headers['user-agent'] }); // Alert after X attempts if (attempts > 5) { alertService.send('Multiple failed logins'); } } }); ` }, /** * 8. DEPENDENCY VULNERABILITIES * Attack: Exploit known vulnerabilities in packages * Prevent: Regular updates, npm audit, SCA tools */ dependencies: { vulnerable: ` # ❌ JANGAN GUNAKAN VERSI LAMA "express": "4.16.0", // Vulnerable version # Tidak menjalankan npm audit `, secure: ` # ✅ SELALU UPDATE DAN AUDIT npm update npm audit fix "express": "^4.18.2", // Latest stable # Use SCA tools: Snyk, Dependabot ` } };
3.5 Security Testing Tools
javascript
/** * SECURITY TESTING TOOLS */ const securityTesting = { /** * 1. STATIC ANALYSIS * Scan code for vulnerabilities */ staticAnalysis: [ 'ESLint with security plugins', 'SonarQube', 'Snyk Code', 'GitHub CodeQL', 'npm audit', 'yarn audit' ], /** * 2. DYNAMIC ANALYSIS * Test running application */ dynamicAnalysis: [ 'OWASP ZAP', 'Burp Suite', 'Postman Security Tests', 'Fiddler', 'Nikto' ], /** * 3. DEPENDENCY SCANNING * Check packages for vulnerabilities */ dependencyScanning: [ 'npm audit', 'Snyk', 'Dependabot', 'Retire.js', 'OWASP Dependency-Check' ], /** * 4. API SECURITY TESTING * Specialized API testing */ apiSecurity: [ 'Postman', 'Insomnia', 'SoapUI', 'RESTler (Microsoft)', 'APISecurity.io' ], /** * 5. PENETRATION TESTING * Manual security assessment */ pentesting: [ 'OWASP Testing Guide', 'PTES (Penetration Testing Execution Standard)', 'OSSTMM' ], /** * npm script untuk security testing */ npmScripts: { audit: 'npm audit', auditFix: 'npm audit fix', snyk: 'snyk test', snykMonitor: 'snyk monitor', zap: 'zap-cli quick-scan --self-contained --ajax-spider --spider http://localhost:3000', securityScan: 'npm run audit && npm run snyk', securityFix: 'npm run audit-fix && npm update' } };
3.6 Production Security Checklist
javascript
/** * PRODUCTION SECURITY CHECKLIST * Jalankan sebelum deploy ke production! */ const productionChecklist = { // ==================================== // 🔐 ENVIRONMENT & CONFIGURATION // ==================================== environment: [ '✓ NODE_ENV=production', '✓ No hardcoded secrets in code', '✓ All secrets in environment variables', '✓ JWT_SECRET: min 32 chars, random', '✓ Strong password for database', '✓ .env in .gitignore', '✓ No debug/console.log statements' ], // ==================================== // 🛡️ EXPRESS SECURITY // ==================================== express: [ '✓ Helmet.js configured', '✓ CORS whitelist defined', '✓ Rate limiting enabled', '✓ CSRF protection (for forms)', '✓ XSS protection enabled', '✓ SQL/NoSQL injection prevention', '✓ HPP protection', '✓ Compression enabled', '✓ Body parser size limits', '✓ app.disable("x-powered-by")' ], // ==================================== // 🔑 AUTHENTICATION & AUTHORIZATION // ==================================== auth: [ '✓ Passwords hashed with bcrypt (rounds >= 12)', '✓ JWT short expiration (15m-1h)', '✓ Refresh tokens implemented', '✓ Rate limiting on auth endpoints', '✓ Account lockout after failed attempts', '✓ Password complexity requirements', '✓ Secure password reset flow', '✓ 2FA for admin accounts', '✓ Session management' ], // ==================================== // 🌐 HTTPS & SSL/TLS // ==================================== https: [ '✓ Valid SSL certificate', '✓ Force HTTPS (HSTS)', '✓ Secure protocols (TLS 1.2+)', '✓ Weak ciphers disabled', '✓ Perfect Forward Secrecy', '✓ SSL Labs test: A+', '✓ HTTP/2 enabled' ], // ==================================== // 🗄️ DATABASE SECURITY // ==================================== database: [ '✓ Database not exposed to internet', '✓ Strong credentials', '✓ Least privilege principle', '✓ Encrypted connections (SSL/TLS)', '✓ Regular backups', '✓ Encrypted at rest', '✓ No SQL injection vulnerabilities' ], // ==================================== // 📦 DEPENDENCIES // ==================================== dependencies: [ '✓ npm audit passed', '✓ All dependencies updated', '✓ No deprecated packages', '✓ No known vulnerabilities', '✓ Dependabot/Snyk configured', '✓ package-lock.json committed' ], // ==================================== // 📝 LOGGING & MONITORING // ==================================== logging: [ '✓ No sensitive data in logs', '✓ Log rotation configured', '✓ Centralized logging', '✓ Error tracking (Sentry, etc.)', '✓ Performance monitoring (APM)', '✓ Security event logging', '✓ Audit trail for sensitive operations' ], // ==================================== // 🚨 ERROR HANDLING // ==================================== errorHandling: [ '✓ No stack traces in production', '✓ Custom error pages', '✓ Global error handler', '✓ 404 handler', '✓ Graceful degradation' ], // ==================================== // 📁 FILE UPLOADS // ==================================== fileUploads: [ '✓ File type validation', '✓ File size limits', '✓ Scan for malware', '✓ Store outside webroot', '✓ Random filenames', '✓ No execution permissions', '✓ Virus scanning', '✓ Content-Disposition header' ], // ==================================== // 🚀 DEPLOYMENT // ==================================== deployment: [ '✓ Running as non-root user', '✓ Minimal privileges', '✓ Firewall configured', '✓ SSH key authentication', '✓ Fail2ban configured', '✓ Regular security updates', '✓ Backup & recovery plan', '✓ Incident response plan' ], // ==================================== // 🧪 PENETRATION TESTING // ==================================== penetrationTesting: [ '✓ OWASP Top 10 tested', '✓ API security tested', '✓ Authentication bypass tested', '✓ IDOR vulnerabilities tested', '✓ SQL injection tested', '✓ XSS tested', '✓ CSRF tested', '✓ Rate limiting tested' ] };
3.7 Latihan Praktikum
Exercise 1: Implement Security Headers
javascript
/** * TODO: Implement custom security headers * 1. Feature-Policy / Permissions-Policy * 2. Expect-CT * 3. Report-To (for CSP reporting) * 4. Clear-Site-Data for logout */ class SecurityHeaders { static permissionsPolicy() { // Implementasi } static expectCT() { // Implementasi } static reportTo() { // Implementasi } static clearSiteData() { // Implementasi } }
Exercise 2: Advanced Rate Limiting
javascript
/** * TODO: Implement advanced rate limiting * 1. Redis store for distributed systems * 2. Per-user + per-IP combined limits * 3. Dynamic limits based on user role * 4. Burst allowance * 5. Custom key generators */ class AdvancedRateLimiter { constructor(redis) { this.redis = redis; } async checkLimit(key, limit, window) { // Implementasi } middleware(options) { // Implementasi } }
Exercise 3: Security Audit Logger
javascript
/** * TODO: Implement comprehensive security audit logging * 1. Log all authentication attempts * 2. Log all permission changes * 3. Log all sensitive data access * 4. Log all configuration changes * 5. Log all security events */ class SecurityAuditLogger { logAuthAttempt(email, success, req) { // Implementasi } logPermissionChange(userId, changedBy, oldPermissions, newPermissions) { // Implementasi } logSensitiveDataAccess(userId, resourceType, resourceId) { // Implementasi } logConfigChange(changedBy, changes) { // Implementasi } }
Exercise 4: Input Sanitization Library
javascript
/** * TODO: Build comprehensive sanitization library * 1. HTML sanitization (allow safe tags) * 2. URL sanitization * 3. Email sanitization * 4. Phone number formatting * 5. SQL injection prevention * 6. NoSQL injection prevention */ class SanitizationLibrary { static html(html, options) { // Implementasi } static url(url) { // Implementasi } static email(email) { // Implementasi } static phone(phone) { // Implementasi } static sql(input) { // Implementasi } static nosql(input) { // Implementasi } }
4. Daftar Pustaka
- Express.js Documentation (n.d). Security Best Practices: Security. https://expressjs.com
- Escape (2024). How to secure APIs build with Express.js. https://escape.tech
- TatvaSoft (2025). Node Js Best Practices and Security. https://www.tatvasoft.com
- Treeindev (2020). Best Practices in Express (Node.js). https://treeindev.net
- Belajar Koding (2024). Express.js Cheat Sheets. https://belajarkoding.com/cheat-sheets/express

0 Komentar