# Security Guide This document outlines the security measures and best practices for Deathwatch Roller. ## Authentication ### Session Management - Sessions use HTTP-only cookies - Sessions expire after inactivity - Passwords are hashed with bcrypt - XSS protection headers enabled ### Session Configuration ```javascript // Example session config session: { secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false, cookie: { httpOnly: true, secure: process.env.NODE_ENV === 'production', maxAge: 24 * 60 * 60 * 1000 // 24 hours } } ``` ## Database Security ### Password Hashing ```typescript // Password hashing import bcrypt from 'bcrypt'; const hashedPassword = await bcrypt.hash(password, 12); const isMatch = await bcrypt.compare(inputPassword, hashedPassword); ``` ### Prepared Statements All database queries use parameterized statements to prevent SQL injection: ```typescript // ✅ GOOD - Using parameterized query const player = await Player.findOne({ where: { email: email }, raw: true }); // ❌ BAD - String concatenation const badQuery = `SELECT * FROM players WHERE email = '${email}'`; ``` ## Input Validation ### Request Validation All API requests are validated: ```typescript import { body, validationResult } from 'express-validator'; router.post('/players', [ body('name').isLength({ min: 1, max: 50 }).trim(), body('email').isEmail().normalizeEmail(), body('xp').isInt({ min: 0 }) ], async (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } // Process request }); ``` ## CORS Configuration ```typescript // CORS setup import cors from 'cors'; app.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(','), credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization'] })); ``` ## Rate Limiting ```typescript // Rate limiter import rateLimit from 'express-rate-limit'; const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // limit each IP to 100 requests per windowMs message: 'Too many requests from this IP, please try again later.' }); app.use('/api/', limiter); ``` ## Security Headers ```typescript import helmet from 'helmet'; app.use(helmet()); // Custom headers app.use((req, res, next) => { res.setHeader('X-Content-Type-Options', 'nosniff'); res.setHeader('X-Frame-Options', 'DENY'); res.setHeader('X-XSS-Protection', '1; mode=block'); next(); }); ``` ## Environment Variables Store sensitive data in environment variables: ```bash # .env file SESSION_SECRET=your-secure-random-string X_GM_SECRET=your-gm-secret-key DATABASE_PASSWORD=your-database-password ``` Never commit `.env` files: ```bash # .gitignore .env .env.* !/.env.example ``` ## API Security ### GM Authentication GM endpoints require special authentication: ```typescript // GM secret header app.use('/api/players/gm/*', (req, res, next) => { const secret = req.headers['x-gm-secret']; if (!secret || secret !== process.env.X_GM_SECRET) { return res.status(403).json({ error: 'Forbidden' }); } next(); }); ``` ### API Rate Limits ```typescript const apiLimiter = rateLimit({ windowMs: 60 * 1000, // 1 minute max: 100, keyGenerator: (req) => req.ip }); app.use('/api/', apiLimiter); ``` ## File Upload Security ```typescript import multer from 'multer'; const upload = multer({ storage: multer.diskStorage({ destination: (req, file, cb) => cb(null, 'uploads/'), filename: (req, file, cb) => { const uniqueName = `${Date.now()}-${Math.random()}-${file.originalname}`; cb(null, uniqueName); } }), fileFilter: (req, file, cb) => { const allowedTypes = ['image/jpeg', 'image/png', 'image/gif']; if (allowedTypes.includes(file.mimetype)) { cb(null, true); } else { cb(new Error('Invalid file type')); } }, limits: { fileSize: 10 * 1024 * 1024 // 10MB } }); ``` ## Database Backups ```bash # Backup script #!/bin/bash mysqldump -u user -p database > backup_$(date +%Y%m%d_%H%M%S).sql # Rotate old backups find . -name "backup_*.sql" -mtime +30 -delete ``` ## Monitoring Set up monitoring for security events: ```typescript // Audit logging import auditlog from 'express-auditlog'; app.use(auditlog()); ``` ## Security Checklist - [ ] Session cookies are HTTP-only - [ ] Passwords are hashed with bcrypt - [ ] SQL queries use parameterized statements - [ ] CORS is properly configured - [ ] Rate limiting is enabled - [ ] Security headers are set - [ ] Environment variables are not committed - [ ] Input is validated on all endpoints - [ ] File uploads are sanitized - [ ] Dependencies are up to date ## Common Vulnerabilities ### SQL Injection Prevention ```typescript // ✅ SAFE User.findOne({ where: { email: email } }); // ❌ UNSAFE User.findAll({ where: `email = '${email}'` }); ``` ### XSS Prevention ```typescript // ✅ SAFE - Sanitize user input const displayName = sanitize(userInput); // ❌ UNSAFE - Direct output res.send(userInput); ``` ## Dependencies Keep dependencies updated: ```bash # Check for updates npm outdated # Update all npm update # Update specific package npm update express ``` ## Security Audits ```bash # Run security audit npm audit # Fix vulnerabilities npm audit fix ``` ## Best Practices 1. **Principle of Least Privilege**: Use minimal permissions 2. **Defense in Depth**: Multiple security layers 3. **Fail Securely**: Default to safe responses 4. **Log Security Events**: Monitor for suspicious activity 5. **Regular Audits**: Periodic security reviews