Cybersecurity Fundamentals
Introduction
Cybersecurity is critical for protecting applications and data. This guide covers OWASP Top 10 vulnerabilities, authentication best practices, encryption, secure coding, XSS/CSRF/SQL injection prevention, and penetration testing basics.
1. OWASP Top 10 Vulnerabilities
# OWASP Top 10 2021
1. Broken Access Control
2. Cryptographic Failures
3. Injection
4. Insecure Design
5. Security Misconfiguration
6. Vulnerable and Outdated Components
7. Identification and Authentication Failures
8. Software and Data Integrity Failures
9. Security Logging and Monitoring Failures
10. Server-Side Request Forgery (SSRF)
# 1. Broken Access Control
// ❌ BAD - No authorization check
app.get('/api/users/:id', async (req, res) => {
const user = await User.findById(req.params.id);
res.json(user);
});
// ✅ GOOD - Verify user can access resource
app.get('/api/users/:id', auth, async (req, res) => {
if (req.user.id !== req.params.id && !req.user.isAdmin) {
return res.status(403).json({ error: 'Forbidden' });
}
const user = await User.findById(req.params.id);
res.json(user);
});
# 2. Cryptographic Failures
// ❌ BAD - Storing passwords in plain text
await User.create({ email, password });
// ✅ GOOD - Hash passwords
import bcrypt from 'bcrypt';
const hashedPassword = await bcrypt.hash(password, 10);
await User.create({ email, password: hashedPassword });
// Verify password
const isValid = await bcrypt.compare(inputPassword, user.password);
# 3. SQL Injection
// ❌ BAD - Direct string concatenation
const query = `SELECT * FROM users WHERE email = '${email}'`;
db.query(query);
// ✅ GOOD - Parameterized queries
const query = 'SELECT * FROM users WHERE email = ?';
db.query(query, [email]);
// ORM (Sequelize)
const user = await User.findOne({ where: { email } });
2. Authentication & Authorization
// JWT Authentication
import jwt from 'jsonwebtoken';
// Generate token
function generateToken(user) {
return jwt.sign(
{ id: user.id, email: user.email, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
}
// Verify token middleware
function authMiddleware(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token provided' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
return res.status(401).json({ error: 'Invalid token' });
}
}
// Role-based access control
function requireRole(...roles) {
return (req, res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
// Usage
app.get('/api/admin', authMiddleware, requireRole('admin'), (req, res) => {
res.json({ message: 'Admin dashboard' });
});
// Multi-factor authentication
import speakeasy from 'speakeasy';
import QRCode from 'qrcode';
// Generate secret
const secret = speakeasy.generateSecret({
name: 'MyApp (user@example.com)'
});
// Generate QR code
const qrCode = await QRCode.toDataURL(secret.otpauth_url);
// Verify token
const verified = speakeasy.totp.verify({
secret: secret.base32,
encoding: 'base32',
token: userProvidedToken,
window: 2
});
// OAuth 2.0 with Passport
import passport from 'passport';
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: '/auth/google/callback'
},
async (accessToken, refreshToken, profile, done) => {
let user = await User.findOne({ googleId: profile.id });
if (!user) {
user = await User.create({
googleId: profile.id,
email: profile.emails[0].value,
name: profile.displayName
});
}
return done(null, user);
}
));
app.get('/auth/google',
passport.authenticate('google', { scope: ['profile', 'email'] })
);
app.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
(req, res) => {
res.redirect('/dashboard');
}
);
3. XSS Prevention
// Cross-Site Scripting (XSS) Prevention
// ❌ BAD - Direct HTML injection
app.get('/search', (req, res) => {
res.send(`Results for: ${req.query.q}
`);
});
// Vulnerable to: /search?q=
// ✅ GOOD - Escape output
import escapeHtml from 'escape-html';
app.get('/search', (req, res) => {
const safe = escapeHtml(req.query.q);
res.send(`Results for: ${safe}
`);
});
// React automatically escapes (safe by default)
function SearchResults({ query }) {
return Results for: {query}
; // Automatically escaped
}
// Dangerous HTML (use with caution)
function UnsafeContent({ html }) {
return ;
}
// DOMPurify for sanitizing HTML
import DOMPurify from 'dompurify';
function SafeContent({ html }) {
const clean = DOMPurify.sanitize(html);
return ;
}
// Content Security Policy (CSP)
app.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; " +
"script-src 'self' 'unsafe-inline' https://cdn.example.com; " +
"style-src 'self' 'unsafe-inline'; " +
"img-src 'self' data: https:; " +
"font-src 'self' https://fonts.gstatic.com; " +
"connect-src 'self' https://api.example.com"
);
next();
});
// Helmet.js for security headers
import helmet from 'helmet';
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"]
}
}
}));
4. CSRF Protection
// Cross-Site Request Forgery (CSRF) Prevention
// CSRF tokens with csurf
import csrf from 'csurf';
import cookieParser from 'cookie-parser';
app.use(cookieParser());
app.use(csrf({ cookie: true }));
// Send token to client
app.get('/form', (req, res) => {
res.render('form', { csrfToken: req.csrfToken() });
});
// Verify token on POST
app.post('/submit', (req, res) => {
// Token automatically verified by middleware
res.json({ success: true });
});
// HTML form
// Fetch API with CSRF token
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
fetch('/api/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken
},
body: JSON.stringify(data)
});
// SameSite cookies
res.cookie('sessionId', session.id, {
httpOnly: true,
secure: true,
sameSite: 'strict' // Prevents CSRF
});
// Double Submit Cookie pattern
function generateCSRFToken() {
const token = crypto.randomBytes(32).toString('hex');
res.cookie('csrf-token', token, { httpOnly: false });
return token;
}
function verifyCSRFToken(req) {
const cookieToken = req.cookies['csrf-token'];
const headerToken = req.headers['x-csrf-token'];
return cookieToken === headerToken;
}
5. Secure Password Storage
// Password hashing with bcrypt
import bcrypt from 'bcrypt';
// Hash password
async function hashPassword(password) {
const saltRounds = 10;
return await bcrypt.hash(password, saltRounds);
}
// Verify password
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);
const errors = [];
if (password.length < minLength) {
errors.push(`Password must be at least ${minLength} characters`);
}
if (!hasUpperCase) errors.push('Must contain uppercase letter');
if (!hasLowerCase) errors.push('Must contain lowercase letter');
if (!hasNumbers) errors.push('Must contain number');
if (!hasSpecialChar) errors.push('Must contain special character');
return {
valid: errors.length === 0,
errors
};
}
// Rate limiting login attempts
import rateLimit from 'express-rate-limit';
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 attempts
message: 'Too many login attempts, please try again later',
standardHeaders: true,
legacyHeaders: false
});
app.post('/login', loginLimiter, async (req, res) => {
// Login logic
});
// Account lockout after failed attempts
class AuthService {
async login(email, password) {
const user = await User.findOne({ email });
if (!user) {
return { success: false, error: 'Invalid credentials' };
}
// Check if account is locked
if (user.lockUntil && user.lockUntil > Date.now()) {
return {
success: false,
error: 'Account temporarily locked. Try again later.'
};
}
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
user.failedLoginAttempts += 1;
if (user.failedLoginAttempts >= 5) {
user.lockUntil = Date.now() + 30 * 60 * 1000; // 30 minutes
}
await user.save();
return { success: false, error: 'Invalid credentials' };
}
// Reset failed attempts on successful login
user.failedLoginAttempts = 0;
user.lockUntil = null;
await user.save();
const token = generateToken(user);
return { success: true, token };
}
}
6. Encryption & HTTPS
// Data encryption at rest
import crypto from 'crypto';
class Encryption {
constructor(secretKey) {
this.algorithm = 'aes-256-gcm';
this.key = crypto.scryptSync(secretKey, 'salt', 32);
}
encrypt(text) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(this.algorithm, this.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')
};
}
decrypt(encrypted, iv, authTag) {
const decipher = crypto.createDecipheriv(
this.algorithm,
this.key,
Buffer.from(iv, 'hex')
);
decipher.setAuthTag(Buffer.from(authTag, 'hex'));
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
}
// Usage
const encryption = new Encryption(process.env.ENCRYPTION_KEY);
// Encrypt sensitive data before storing
const sensitiveData = 'Credit card: 1234-5678-9012-3456';
const { encrypted, iv, authTag } = encryption.encrypt(sensitiveData);
await db.query(
'INSERT INTO secure_data (data, iv, auth_tag) VALUES (?, ?, ?)',
[encrypted, iv, authTag]
);
// HTTPS setup with Express
import https from 'https';
import fs from 'fs';
const options = {
key: fs.readFileSync('private-key.pem'),
cert: fs.readFileSync('certificate.pem')
};
https.createServer(options, app).listen(443);
// Force HTTPS redirect
app.use((req, res, next) => {
if (req.protocol !== 'https') {
return res.redirect('https://' + req.headers.host + req.url);
}
next();
});
// HSTS (HTTP Strict Transport Security)
app.use((req, res, next) => {
res.setHeader(
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains; preload'
);
next();
});
7. Input Validation & Sanitization
// Input validation with Joi
import Joi from 'joi';
const userSchema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string().min(8).max(128).required(),
age: Joi.number().integer().min(18).max(120),
website: Joi.string().uri()
});
app.post('/register', async (req, res) => {
try {
const validated = await userSchema.validateAsync(req.body);
// Proceed with validated data
} catch (error) {
return res.status(400).json({ error: error.details[0].message });
}
});
// Express-validator
import { body, validationResult } from 'express-validator';
app.post('/api/users',
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 8 }).trim().escape(),
body('age').isInt({ min: 18, max: 120 }),
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Safe to proceed
}
);
// Path traversal prevention
import path from 'path';
function safeFilePath(userInput) {
const normalized = path.normalize(userInput);
const resolved = path.resolve(normalized);
const baseDir = path.resolve('./uploads');
if (!resolved.startsWith(baseDir)) {
throw new Error('Invalid file path');
}
return resolved;
}
// Command injection prevention
// ❌ BAD
const { exec } = require('child_process');
exec(`ping ${userInput}`); // Dangerous!
// ✅ GOOD - Use libraries or validate strictly
import { execFile } from 'child_process';
execFile('ping', ['-c', '4', validatedHost], (error, stdout) => {
// Safer - arguments are passed separately
});
8. Security Testing
# Security scanning tools
# npm audit for dependencies
npm audit
npm audit fix
# Snyk for vulnerability scanning
npm install -g snyk
snyk auth
snyk test
snyk monitor
# OWASP ZAP for penetration testing
docker run -t owasp/zap2docker-stable zap-baseline.py \
-t https://myapp.com
# Burp Suite for manual testing
# Download from portswigger.net
# SQLMap for SQL injection testing
sqlmap -u "https://myapp.com/api/users?id=1" --batch
# Nikto web server scanner
nikto -h https://myapp.com
# Security headers check
curl -I https://myapp.com
# Expected headers:
Strict-Transport-Security: max-age=31536000
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Content-Security-Policy: default-src 'self'
# Automated security tests
// Jest security test
describe('Security', () => {
test('should not expose sensitive data in errors', async () => {
const res = await request(app)
.post('/login')
.send({ email: 'test@example.com', password: 'wrong' });
expect(res.body).not.toHaveProperty('stackTrace');
expect(res.body).not.toContain('database');
});
test('should prevent SQL injection', async () => {
const malicious = "' OR '1'='1";
const res = await request(app)
.get(`/users?search=${malicious}`);
expect(res.status).toBe(200);
expect(res.body).not.toContain('syntax error');
});
});
9. Secure Logging
// Winston with sensitive data filtering
import winston from 'winston';
const sensitiveFields = ['password', 'token', 'apiKey', 'ssn', 'creditCard'];
const sanitizeFormat = winston.format((info) => {
const sanitize = (obj) => {
if (typeof obj !== 'object' || obj === null) return obj;
const sanitized = Array.isArray(obj) ? [] : {};
for (const [key, value] of Object.entries(obj)) {
if (sensitiveFields.includes(key)) {
sanitized[key] = '***REDACTED***';
} else if (typeof value === 'object') {
sanitized[key] = sanitize(value);
} else {
sanitized[key] = value;
}
}
return sanitized;
};
return sanitize(info);
});
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
sanitizeFormat(),
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'app.log' })
]
});
// Usage
logger.info('User login attempt', {
email: 'user@example.com',
password: 'secret123', // Will be redacted
ip: '192.168.1.1'
});
// Security event logging
class SecurityLogger {
static logAuthFailure(email, ip, reason) {
logger.warn('Authentication failure', {
event: 'auth_failure',
email,
ip,
reason,
timestamp: new Date()
});
}
static logSuspiciousActivity(userId, activity, details) {
logger.error('Suspicious activity detected', {
event: 'suspicious_activity',
userId,
activity,
details,
timestamp: new Date()
});
}
static logAccessDenied(userId, resource, ip) {
logger.warn('Access denied', {
event: 'access_denied',
userId,
resource,
ip,
timestamp: new Date()
});
}
}
10. Best Practices
✓ Cybersecurity Best Practices:
- ✓ Keep all dependencies updated
- ✓ Use HTTPS everywhere
- ✓ Implement proper authentication and authorization
- ✓ Never store passwords in plain text
- ✓ Validate and sanitize all inputs
- ✓ Use parameterized queries to prevent SQL injection
- ✓ Escape output to prevent XSS
- ✓ Implement CSRF protection
- ✓ Set secure HTTP headers (CSP, HSTS, etc.)
- ✓ Use rate limiting to prevent brute force
- ✓ Encrypt sensitive data at rest and in transit
- ✓ Implement security logging and monitoring
- ✓ Regular security audits and penetration testing
- ✓ Principle of least privilege
- ✓ Security awareness training for team
Conclusion
Cybersecurity requires constant vigilance and best practices. Understand OWASP Top 10, implement secure authentication, prevent injection attacks, use encryption properly, and test regularly. Security is not a one-time effort but an ongoing process integrated into development lifecycle.
💡 Pro Tip: Implement security at every layer: network (firewalls, DDoS protection), application (input validation, authentication), data (encryption), and infrastructure (container security, IAM). Use defense in depth strategy - multiple security layers ensure that if one fails, others provide protection. Automate security scanning in CI/CD pipelines.