Security Overview
Security is a fundamental aspect of any application. This guide covers security best practices, common vulnerabilities, and how to implement robust security measures in Solverhood applications.
Security Principles
Core Principles
- Defense in Depth: Multiple layers of security controls
- Principle of Least Privilege: Grant minimum necessary permissions
- Fail Securely: Default to secure state when errors occur
- Security by Design: Build security into the development process
- Regular Updates: Keep dependencies and systems updated
Security Model
Our security model follows the CIA triad:
- Confidentiality: Protect sensitive data from unauthorized access
- Integrity: Ensure data accuracy and consistency
- Availability: Maintain system availability for authorized users
Common Security Vulnerabilities
1. Injection Attacks
SQL Injection Prevention
// ❌ Vulnerable - Direct string concatenation
const query = `SELECT * FROM users WHERE id = ${userId}`;
// ✅ Secure - Parameterized queries
const query = 'SELECT * FROM users WHERE id = $1';
const result = await db.query(query, [userId]);
// ✅ Secure - Using an ORM
const user = await User.findByPk(userId);
NoSQL Injection Prevention
// ❌ Vulnerable - Direct object injection
const user = await User.findOne({ email: req.body.email });
// ✅ Secure - Validate and sanitize input
const { email } = req.body;
if (!isValidEmail(email)) {
throw new Error('Invalid email format');
}
const user = await User.findOne({ email });
2. Cross-Site Scripting (XSS)
Prevention Strategies
// ❌ Vulnerable - Direct HTML injection
app.get('/search', (req, res) => {
const query = req.query.q;
res.send(`<h1>Search results for: ${query}</h1>`);
});
// ✅ Secure - HTML escaping
const escapeHtml = (text) => {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
};
app.get('/search', (req, res) => {
const query = escapeHtml(req.query.q);
res.send(`<h1>Search results for: ${query}</h1>`);
});
// ✅ Secure - Using template engines with auto-escaping
app.get('/search', (req, res) => {
res.render('search', { query: req.query.q });
});
3. Cross-Site Request Forgery (CSRF)
CSRF Protection
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
// Apply CSRF protection to all routes
app.use(csrfProtection);
// Generate CSRF token for forms
app.get('/form', (req, res) => {
res.render('form', { csrfToken: req.csrfToken() });
});
// Verify CSRF token on form submission
app.post('/submit', csrfProtection, (req, res) => {
// Process form data
res.json({ success: true });
});
4. Authentication Vulnerabilities
Secure Password Handling
const bcrypt = require('bcrypt');
// ✅ Secure password hashing
async function hashPassword(password) {
const saltRounds = 12;
return await bcrypt.hash(password, saltRounds);
}
// ✅ Secure password verification
async function verifyPassword(password, hash) {
return await bcrypt.compare(password, hash);
}
// ✅ Password strength validation
function validatePassword(password) {
const minLength = 8;
const hasUpperCase = /[A-Z]/.test(password);
const hasLowerCase = /[a-z]/.test(password);
const hasNumbers = /\d/.test(password);
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(password);
if (password.length < minLength) {
throw new Error('Password must be at least 8 characters long');
}
if (!hasUpperCase || !hasLowerCase) {
throw new Error('Password must contain both uppercase and lowercase letters');
}
if (!hasNumbers) {
throw new Error('Password must contain at least one number');
}
if (!hasSpecialChar) {
throw new Error('Password must contain at least one special character');
}
}
Authentication & Authorization
1. JWT Security
Secure JWT Implementation
const jwt = require('jsonwebtoken');
// ✅ Secure JWT configuration
const jwtConfig = {
secret: process.env.JWT_SECRET,
expiresIn: '15m', // Short expiration
issuer: 'solverhood',
audience: 'solverhood-users',
};
// Generate JWT
function generateToken(user) {
return jwt.sign(
{
userId: user.id,
email: user.email,
role: user.role,
},
jwtConfig.secret,
{
expiresIn: jwtConfig.expiresIn,
issuer: jwtConfig.issuer,
audience: jwtConfig.audience,
}
);
}
// Verify JWT
function verifyToken(token) {
try {
return jwt.verify(token, jwtConfig.secret, {
issuer: jwtConfig.issuer,
audience: jwtConfig.audience,
});
} catch (error) {
throw new Error('Invalid token');
}
}
2. Role-Based Access Control (RBAC)
// Define roles and permissions
const roles = {
ADMIN: ['read', 'write', 'delete', 'manage_users'],
MODERATOR: ['read', 'write', 'moderate'],
USER: ['read', 'write'],
GUEST: ['read'],
};
// Authorization middleware
function authorize(requiredPermission) {
return (req, res, next) => {
const userRole = req.user.role;
const userPermissions = roles[userRole] || [];
if (!userPermissions.includes(requiredPermission)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
// Usage
app.delete('/api/users/:id', authenticateToken, authorize('manage_users'), deleteUser);
Data Protection
1. Input Validation
const { body, validationResult } = require('express-validator');
// Comprehensive input validation
const validateUser = [
body('email').isEmail().normalizeEmail().withMessage('Valid email is required'),
body('password')
.isLength({ min: 8 })
.matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])/)
.withMessage('Password must be at least 8 characters with uppercase, lowercase, number, and special character'),
body('name').trim().isLength({ min: 2, max: 50 }).escape().withMessage('Name must be between 2 and 50 characters'),
(req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
},
];
2. Data Encryption
const crypto = require('crypto');
// Encrypt sensitive data
function encryptData(data, key) {
const algorithm = 'aes-256-gcm';
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipher(algorithm, key);
let encrypted = cipher.update(data, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return {
encrypted,
iv: iv.toString('hex'),
authTag: authTag.toString('hex'),
};
}
// Decrypt sensitive data
function decryptData(encryptedData, key) {
const algorithm = 'aes-256-gcm';
const decipher = crypto.createDecipher(algorithm, key);
decipher.setAuthTag(Buffer.from(encryptedData.authTag, 'hex'));
let decrypted = decipher.update(encryptedData.encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
API Security
1. Rate Limiting
const rateLimit = require('express-rate-limit');
// General rate limiting
const generalLimiter = 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.',
standardHeaders: true,
legacyHeaders: false,
});
// Stricter rate limiting for authentication
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // limit each IP to 5 requests per windowMs
message: 'Too many authentication attempts, please try again later.',
skipSuccessfulRequests: true,
});
app.use('/api/', generalLimiter);
app.use('/api/auth', authLimiter);
2. API Key Management
// API key validation middleware
function validateApiKey(req, res, next) {
const apiKey = req.headers['x-api-key'];
if (!apiKey) {
return res.status(401).json({ error: 'API key required' });
}
// Validate API key format
if (!/^sk_[a-zA-Z0-9]{32}$/.test(apiKey)) {
return res.status(401).json({ error: 'Invalid API key format' });
}
// Check if API key exists and is active
const key = await ApiKey.findOne({ key: apiKey, active: true });
if (!key) {
return res.status(401).json({ error: 'Invalid or inactive API key' });
}
// Check rate limits
const usage = await checkApiKeyUsage(key.id);
if (usage.exceeded) {
return res.status(429).json({ error: 'Rate limit exceeded' });
}
req.apiKey = key;
next();
}
Infrastructure Security
1. Environment Variables
// Secure environment variable management
const requiredEnvVars = ['DATABASE_URL', 'JWT_SECRET', 'API_SECRET_KEY', 'REDIS_URL'];
// Validate required environment variables
function validateEnvironment() {
const missing = requiredEnvVars.filter((varName) => !process.env[varName]);
if (missing.length > 0) {
throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
}
// Validate JWT secret strength
if (process.env.JWT_SECRET.length < 32) {
throw new Error('JWT_SECRET must be at least 32 characters long');
}
}
// Call validation on startup
validateEnvironment();
2. Security Headers
const helmet = require('helmet');
// Comprehensive security headers
app.use(
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", 'data:', 'https:'],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
noSniff: true,
referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
})
);
Security Testing
1. Automated Security Testing
// Security test suite
describe('Security Tests', () => {
test('should prevent SQL injection', async () => {
const maliciousInput = "'; DROP TABLE users; --";
const response = await request(app).get('/api/users').query({ search: maliciousInput });
expect(response.status).not.toBe(500);
expect(response.body).not.toContain('DROP TABLE');
});
test('should prevent XSS attacks', async () => {
const maliciousInput = '<script>alert("xss")</script>';
const response = await request(app).post('/api/comments').send({ content: maliciousInput });
expect(response.body.content).not.toContain('<script>');
});
test('should enforce rate limiting', async () => {
const requests = Array(101)
.fill()
.map(() => request(app).get('/api/data'));
const responses = await Promise.all(requests);
const rateLimited = responses.filter((r) => r.status === 429);
expect(rateLimited.length).toBeGreaterThan(0);
});
});
2. Dependency Scanning
{
"scripts": {
"security:audit": "npm audit",
"security:fix": "npm audit fix",
"security:check": "snyk test",
"security:monitor": "snyk monitor"
},
"devDependencies": {
"snyk": "^1.1000.0"
}
}
Incident Response
1. Security Incident Plan
// Security incident logging
const securityLogger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [new winston.transports.File({ filename: 'security.log' })],
});
// Log security events
function logSecurityEvent(event, details) {
securityLogger.info('Security Event', {
timestamp: new Date().toISOString(),
event,
details,
ip: req.ip,
userAgent: req.get('User-Agent'),
userId: req.user?.id,
});
}
// Usage
app.post('/api/login', async (req, res) => {
try {
const user = await authenticateUser(req.body);
if (!user) {
logSecurityEvent('failed_login', { email: req.body.email });
return res.status(401).json({ error: 'Invalid credentials' });
}
// ... success logic
} catch (error) {
logSecurityEvent('login_error', { error: error.message });
res.status(500).json({ error: 'Authentication error' });
}
});
Compliance
1. GDPR Compliance
// Data privacy utilities
class DataPrivacy {
static async anonymizeUser(userId) {
const user = await User.findByPk(userId);
if (!user) return;
// Anonymize personal data
await user.update({
email: `deleted_${Date.now()}@deleted.com`,
name: 'Deleted User',
phone: null,
address: null,
});
// Log the action
await PrivacyLog.create({
action: 'anonymize',
userId,
timestamp: new Date(),
});
}
static async exportUserData(userId) {
const user = await User.findByPk(userId, {
include: [UserProjects, UserSettings],
});
return {
personalData: {
name: user.name,
email: user.email,
createdAt: user.createdAt,
},
projects: user.UserProjects,
settings: user.UserSettings,
};
}
}
Best Practices
1. Development Security
- Code Review: All code changes must be reviewed for security
- Dependency Management: Regularly update dependencies
- Secret Management: Use secure secret management systems
- Environment Separation: Separate development, staging, and production
2. Operational Security
- Access Control: Implement least privilege access
- Monitoring: Monitor for security events and anomalies
- Backup Security: Encrypt backups and test restoration
- Incident Response: Have a documented incident response plan
3. User Security
- Multi-Factor Authentication: Implement MFA for sensitive operations
- Session Management: Implement secure session handling
- Password Policies: Enforce strong password requirements
- Account Lockout: Implement account lockout after failed attempts
Next Steps
- Authentication Security - Coming soon
- API Security - Coming soon
- Data Protection - Coming soon
- Incident Response - Coming soon