Security First - Shield Your Code
🎯 When to Use This Skill
Use BEFORE:
-
Deploying to production
-
Handling sensitive data
-
Opening API endpoints
-
Processing user input
-
Storing passwords
-
Accepting file uploads
-
Going live with payments
⚡ 5-Minute Security Audit
WITH MCP (Security Scanner):
"Run complete security audit on my codebase" "Find and fix all OWASP Top 10 vulnerabilities"
WITHOUT MCP - Quick Scan:
1. Find hardcoded secrets (CRITICAL!)
grep -r "password|secret|token|api[_-]key" --include=".js" --include=".env" | grep -v ".example"
2. Check for SQL injection
grep -r "query.+|query.${" --include="*.js"
3. Find eval/exec usage
grep -r "eval(|exec(|Function(" --include="*.js"
4. Check dependencies
npm audit # or pip check, bundle audit
5. Find unvalidated input
grep -r "req.body|req.query|req.params" --include="*.js" | grep -v "validate|sanitize"
🛡️ OWASP Top 10 Checklist (2024)
- Injection (SQL, NoSQL, Command) 💉
Vulnerable Code:
// ❌ NEVER DO THIS
const query = SELECT * FROM users WHERE id = ${req.params.id};
db.query(query); // SQL Injection!
// ❌ Command injection
exec(ping ${userInput}); // Dangerous!
Secure Code:
// ✅ Parameterized queries const query = 'SELECT * FROM users WHERE id = ?'; db.query(query, [req.params.id]);
// ✅ For MongoDB User.findOne({ _id: sanitize(req.params.id) });
// ✅ Command execution const { spawn } = require('child_process'); spawn('ping', [userInput], { shell: false });
- Broken Authentication 🔐
Security Checklist:
// ✅ Strong password requirements function validatePassword(password) { const requirements = { minLength: 12, hasUpperCase: /[A-Z]/.test(password), hasLowerCase: /[a-z]/.test(password), hasNumbers: /\d/.test(password), hasSpecialChar: /[!@#$%^&*]/.test(password), notCommon: !commonPasswords.includes(password), };
return Object.values(requirements).every(req => req); }
// ✅ Secure session management app.use( session({ secret: process.env.SESSION_SECRET, // From environment resave: false, saveUninitialized: false, cookie: { secure: true, // HTTPS only httpOnly: true, // No JS access maxAge: 3600000, // 1 hour sameSite: 'strict', // CSRF protection }, }) );
// ✅ Rate limiting const rateLimit = require('express-rate-limit'); const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 5, // 5 attempts message: 'Too many login attempts', }); app.post('/login', loginLimiter, loginHandler);
- Sensitive Data Exposure 🔒
Never Store in Code:
// ❌ WRONG const API_KEY = 'sk_live_abcd1234'; const DB_PASSWORD = 'admin123';
// ✅ CORRECT - Use environment variables const API_KEY = process.env.API_KEY; const DB_PASSWORD = process.env.DB_PASSWORD;
// ✅ Use .env file (never commit!) require('dotenv').config();
// ✅ Encrypt sensitive data at rest const crypto = require('crypto'); const algorithm = 'aes-256-gcm';
function encrypt(text) { const key = Buffer.from(process.env.ENCRYPTION_KEY, 'hex'); const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv(algorithm, key, iv);
let encrypted = cipher.update(text, 'utf8', 'hex'); encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return { encrypted, iv: iv.toString('hex'), authTag: authTag.toString('hex'), }; }
- XML External Entities (XXE) 📄
// ❌ Vulnerable XML parsing const libxmljs = require('libxmljs'); const doc = libxmljs.parseXml(userInput); // XXE vulnerable!
// ✅ Safe XML parsing const parser = new DOMParser(); const doc = parser.parseFromString(userInput, 'text/xml');
// ✅ Or disable external entities const options = { xmlMode: true, recognizeSelfClosing: true, decodeEntities: false, // Disable entity expansion };
- Broken Access Control 🚪
// ❌ No authorization check app.get('/api/user/:id', (req, res) => { const user = User.findById(req.params.id); res.json(user); // Anyone can see any user! });
// ✅ Proper authorization app.get('/api/user/:id', authenticate, (req, res) => { // Check if user can access this resource if (req.user.id !== req.params.id && !req.user.isAdmin) { return res.status(403).json({ error: 'Forbidden' }); }
const user = User.findById(req.params.id); res.json(user); });
// ✅ Role-based access control (RBAC) const authorize = roles => { return (req, res, next) => { if (!roles.includes(req.user.role)) { return res.status(403).json({ error: 'Insufficient permissions' }); } next(); }; };
app.delete('/api/users/:id', authenticate, authorize(['admin']), deleteUser);
- Security Misconfiguration ⚙️
✅ Security headers
app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'"], imgSrc: ["'self'", "data:", "https:"], }, }, hsts: { maxAge: 31536000, includeSubDomains: true, preload: true } }));
✅ Disable unnecessary features
app.disable('x-powered-by'); # Hide Express
✅ Error handling (don't leak info)
app.use((err, req, res, next) => { console.error(err.stack); // Log full error
// Send generic message to client res.status(500).json({ error: 'Internal server error', // Don't send: err.stack or err.message }); });
- Cross-Site Scripting (XSS) 🎭
// ❌ Vulnerable to XSS
app.get('/search', (req, res) => {
res.send(Results for: ${req.query.q}); // XSS!
});
// ✅ Sanitize output const DOMPurify = require('isomorphic-dompurify');
app.get('/search', (req, res) => {
const clean = DOMPurify.sanitize(req.query.q);
res.send(Results for: ${clean});
});
// ✅ React automatically escapes <div>{userInput}</div> // Safe
// ❌ But dangerouslySetInnerHTML is dangerous <div dangerouslySetInnerHTML={{__html: userInput}} /> // XSS!
// ✅ Content-Type headers res.set('Content-Type', 'text/plain'); // Not HTML res.set('X-Content-Type-Options', 'nosniff');
- Insecure Deserialization 📦
// ❌ Dangerous deserialization const userData = JSON.parse(req.body.data); eval(userData.code); // Code execution!
// ✅ Validate before deserializing const schema = Joi.object({ name: Joi.string().required(), age: Joi.number().min(0).max(120), });
const { error, value } = schema.validate(JSON.parse(req.body.data)); if (error) return res.status(400).json({ error });
// ✅ Never deserialize untrusted data into code // Use JSON.parse() only, never eval() or Function()
- Components with Known Vulnerabilities 📚
✅ Regular dependency checks
Add to package.json
{ "scripts": { "security": "npm audit && npm outdated", "security:fix": "npm audit fix", "preinstall": "npm audit" } }
✅ Automated updates (GitHub)
.github/dependabot.yml
version: 2 updates:
- package-ecosystem: "npm" directory: "/" schedule: interval: "daily" open-pull-requests-limit: 10
- Insufficient Logging & Monitoring 📊
// ✅ Comprehensive logging const winston = require('winston');
const logger = winston.createLogger({ level: 'info', format: winston.format.json(), transports: [ new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'combined.log' }), ], });
// ✅ Log security events function logSecurityEvent(event, user, details) { logger.warn({ type: 'SECURITY', event, user: user?.id, ip: user?.ip, timestamp: new Date().toISOString(), details, }); }
// Usage logSecurityEvent('FAILED_LOGIN', req.user, { attempts: failedAttempts, ip: req.ip, });
logSecurityEvent('UNAUTHORIZED_ACCESS', req.user, { resource: req.path, method: req.method, });
🔐 Password Security
// ✅ NEVER store plain text passwords! const bcrypt = require('bcrypt');
// Hashing async function hashPassword(password) { const saltRounds = 12; // Higher = more secure but slower return await bcrypt.hash(password, saltRounds); }
// Verifying async function verifyPassword(password, hash) { return await bcrypt.compare(password, hash); }
// ✅ Password reset flow async function resetPassword(email) { // 1. Generate secure token const token = crypto.randomBytes(32).toString('hex');
// 2. Store hashed token with expiry await storeResetToken(email, hashToken(token), Date.now() + 3600000);
// 3. Send unhashed token via email
await sendEmail(email, Reset link: ${BASE_URL}/reset?token=${token});
}
🚪 API Security
// ✅ API Security Checklist const apiSecurity = { // 1. Authentication authentication: 'Bearer token (JWT)',
// 2. Rate limiting rateLimit: { windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // requests per window },
// 3. Input validation validateInput: (data, schema) => { return Joi.validate(data, schema); },
// 4. CORS configuration cors: { origin: process.env.ALLOWED_ORIGINS?.split(',') || false, credentials: true, },
// 5. API versioning versioning: '/api/v1/',
// 6. Request size limit bodyLimit: '10mb',
// 7. Timeout timeout: 30000, // 30 seconds
// 8. HTTPS only httpsOnly: true, };
📋 Security Deployment Checklist
Pre-Deployment Security Checklist
Code Security
- No hardcoded secrets
- All inputs validated
- SQL queries parameterized
- XSS protection enabled
- CSRF tokens implemented
- Authentication required
- Authorization checks present
- Rate limiting configured
Dependencies
-
npm auditshows 0 vulnerabilities - All packages from trusted sources
- Lock file committed
- Licenses reviewed
Configuration
- Environment variables used
- HTTPS enforced
- Security headers set
- CORS configured
- Error messages sanitized
- Debug mode disabled
- Source maps disabled in production
Data Protection
- Passwords hashed (bcrypt)
- Sensitive data encrypted
- PII fields marked
- Data retention policy set
- Backups encrypted
Infrastructure
- Firewall rules configured
- Ports minimized
- SSH keys only (no passwords)
- Monitoring enabled
- Logging configured
- Incident response plan ready
🚨 Incident Response Plan
// security-incident.js class SecurityIncident { async respond(incident) { // 1. Detect this.log('INCIDENT_DETECTED', incident);
// 2. Contain
await this.blockIP(incident.sourceIP);
await this.disableAccount(incident.userId);
// 3. Investigate
const logs = await this.gatherLogs(incident);
// 4. Remediate
await this.patchVulnerability(incident.vulnerability);
// 5. Recover
await this.restoreService();
// 6. Lessons Learned
await this.documentIncident(incident);
// 7. Notify
await this.notifyStakeholders(incident);
} }
💡 Security Quick Wins
1. Add security.txt
echo "Contact: security@example.com" > public/.well-known/security.txt
2. Enable Dependabot
gh api repos/:owner/:repo --method PUT --field security_and_analysis[secret_scanning][status]=enabled
3. Add pre-commit hooks
npm install --save-dev husky npx husky add .husky/pre-commit "npm audit"
4. Security headers test
curl -I https://yoursite.com | grep -i "strict-transport|content-security|x-frame"
Remember: Security is not a feature, it's a requirement! 🛡️