Expert-level application security, OWASP Top 10, penetration testing, and security best practices
Expert guidance for application security, vulnerability assessment, penetration testing, OWASP Top 10, secure coding practices, and security architecture.
const crypto = require('crypto');
const ALGORITHM = 'aes-256-gcm';
const KEY = Buffer.from(process.env.ENCRYPTION_KEY, 'hex'); // 32 bytes
function encrypt(plaintext) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(ALGORITHM, KEY, iv);
let encrypted = cipher.update(plaintext, 'utf8', 'hex');
encrypted += cipher.final('hex');
const authTag = cipher.getAuthTag();
return {
iv: iv.toString('hex'),
encrypted,
authTag: authTag.toString('hex'),
};
}
function decrypt(iv, encrypted, authTag) {
const decipher = crypto.createDecipheriv(
ALGORITHM,
KEY,
Buffer.from(iv, 'hex')
);
decipher.setAuthTag(Buffer.from(authTag, 'hex'));
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
// Store sensitive data
async function storeSensitiveData(userId, data) {
const { iv, encrypted, authTag } = encrypt(JSON.stringify(data));
await db.sensitiveData.create({
userId,
iv,
encrypted,
authTag,
});
}
// ā
Cryptographically secure random
const crypto = require('crypto');
function generateSecureToken(length = 32) {
return crypto.randomBytes(length).toString('hex');
}
function generateSecureId() {
return crypto.randomUUID();
}
// ā Don't use Math.random() for security
const insecureToken = Math.random().toString(36); // Predictable!
const { body, validationResult } = require('express-validator');
app.post(
'/api/users',
// Validation rules
body('email').isEmail().normalizeEmail(),
body('name').trim().isLength({ min: 2, max: 100 }).escape(),
body('age').optional().isInt({ min: 18, max: 120 }),
body('website').optional().isURL(),
async (req, res) => {
// Check validation results
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Process validated data
const user = await User.create(req.body);
res.status(201).json(user);
}
);
// File upload validation
const multer = require('multer');
const upload = multer({
limits: {
fileSize: 5 * 1024 * 1024, // 5MB max
},
fileFilter: (req, file, cb) => {
// Check file type
const allowedMimes = ['image/jpeg', 'image/png', 'image/gif'];
if (!allowedMimes.includes(file.mimetype)) {
return cb(new Error('Invalid file type'));
}
// Check file extension
const ext = path.extname(file.originalname).toLowerCase();
if (!['.jpg', '.jpeg', '.png', '.gif'].includes(ext)) {
return cb(new Error('Invalid file extension'));
}
cb(null, true);
},
});
app.post('/upload', upload.single('image'), (req, res) => {
// Verify file content matches extension
// Store with random filename to prevent path traversal
const filename = `${crypto.randomUUID()}${path.extname(req.file.originalname)}`;
// Save file...
});
const helmet = require('helmet');
app.use(
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"], // Avoid unsafe-inline in production
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:', 'https:'],
connectSrc: ["'self'"],
fontSrc: ["'self'"],
objectSrc: ["'none'"],
mediaSrc: ["'self'"],
frameSrc: ["'none'"],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true,
},
frameguard: {
action: 'deny',
},
noSniff: true,
xssFilter: true,
})
);
// Additional 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');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
res.setHeader(
'Permissions-Policy',
'geolocation=(), microphone=(), camera=()'
);
next();
});
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, details) {
logger.warn('Security Event', {
event,
...details,
timestamp: new Date().toISOString(),
});
}
// Failed login attempts
app.post('/login', async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user || !(await verifyPassword(password, user.passwordHash))) {
logSecurityEvent('failed_login', {
email,
ip: req.ip,
userAgent: req.headers['user-agent'],
});
return res.status(401).json({ error: 'Invalid credentials' });
}
// Success
logSecurityEvent('successful_login', {
userId: user.id,
ip: req.ip,
});
// Generate tokens...
});
// ā Don't log sensitive data
logger.info('User data', user); // May contain passwordHash, tokens
// ā
Sanitize before logging
logger.info('User data', {
id: user.id,
email: user.email,
// Omit sensitive fields
});
ā Storing passwords in plaintext: Always hash with bcrypt/argon2 ā Rolling your own crypto: Use established libraries ā Trusting user input: Validate and sanitize everything ā Exposing sensitive errors: Use generic error messages ā No rate limiting: Implement rate limiting on all endpoints ā Weak session management: Use secure, httpOnly cookies ā No logging: Log security events for monitoring ā Hardcoded secrets: Use environment variables
Detailed material lives alongside this skill and is read on demand: