Security and Best Practices

1. Input Validation and Sanitization

Common Input Threats

Threat Description Prevention
SQL Injection Malicious SQL in input Parameterized queries, ORM
XSS (Cross-Site Scripting) Malicious scripts in content Escape output, CSP headers
Command Injection OS commands in input Avoid shell execution, whitelist
Path Traversal Access unauthorized files Validate paths, use basedir
LDAP Injection Malicious LDAP queries Escape special chars, whitelist

Validation Strategies

Strategy When to Use Example
Whitelist Known valid values Enum values, file extensions
Blacklist Last resort only Block specific patterns
Type Checking Enforce data types typeof, instanceof, schema
Length Limits Prevent overflow/DoS Max string/array length
Format Validation Structured data Email, URL, phone regex

Sanitization Techniques

Technique Purpose Library
HTML Escaping Prevent XSS DOMPurify, he
URL Encoding Safe URL parameters encodeURIComponent()
SQL Escaping Prevent SQL injection ORM, prepared statements
Trim/Normalize Consistent format trim(), normalize()

Example: Input validation and sanitization

// Email validation
function isValidEmail(email) {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(email);
}

// Strong password validation
function isStrongPassword(password) {
    // At least 8 chars, 1 uppercase, 1 lowercase, 1 number, 1 special char
    const minLength = password.length >= 8;
    const hasUpper = /[A-Z]/.test(password);
    const hasLower = /[a-z]/.test(password);
    const hasNumber = /\d/.test(password);
    const hasSpecial = /[!@#$%^&*(),.?":{}|<>]/.test(password);
    
    return minLength && hasUpper && hasLower && hasNumber && hasSpecial;
}

// Comprehensive input validator
class InputValidator {
    static validateString(value, options = {}) {
        const {
            minLength = 0,
            maxLength = Infinity,
            pattern = null,
            allowEmpty = false
        } = options;
        
        if (!value && !allowEmpty) {
            throw new Error('Value is required');
        }
        
        if (typeof value !== 'string') {
            throw new Error('Value must be a string');
        }
        
        if (value.length < minLength) {
            throw new Error(`Value must be at least ${minLength} characters`);
        }
        
        if (value.length > maxLength) {
            throw new Error(`Value must not exceed ${maxLength} characters`);
        }
        
        if (pattern && !pattern.test(value)) {
            throw new Error('Value does not match required pattern');
        }
        
        return true;
    }
    
    static validateNumber(value, options = {}) {
        const {
            min = -Infinity,
            max = Infinity,
            integer = false
        } = options;
        
        const num = Number(value);
        
        if (isNaN(num)) {
            throw new Error('Value must be a number');
        }
        
        if (integer && !Number.isInteger(num)) {
            throw new Error('Value must be an integer');
        }
        
        if (num < min) {
            throw new Error(`Value must be at least ${min}`);
        }
        
        if (num > max) {
            throw new Error(`Value must not exceed ${max}`);
        }
        
        return true;
    }
    
    static validateEnum(value, allowedValues) {
        if (!allowedValues.includes(value)) {
            throw new Error(
                `Value must be one of: ${allowedValues.join(', ')}`
            );
        }
        return true;
    }
    
    static validateArray(value, options = {}) {
        const {
            minLength = 0,
            maxLength = Infinity,
            itemValidator = null
        } = options;
        
        if (!Array.isArray(value)) {
            throw new Error('Value must be an array');
        }
        
        if (value.length < minLength) {
            throw new Error(`Array must have at least ${minLength} items`);
        }
        
        if (value.length > maxLength) {
            throw new Error(`Array must not exceed ${maxLength} items`);
        }
        
        if (itemValidator) {
            value.forEach((item, index) => {
                try {
                    itemValidator(item);
                } catch (error) {
                    throw new Error(`Item at index ${index}: ${error.message}`);
                }
            });
        }
        
        return true;
    }
}

// Usage examples
try {
    InputValidator.validateString('hello', {minLength: 3, maxLength: 10});
    InputValidator.validateNumber(25, {min: 0, max: 100});
    InputValidator.validateEnum('active', ['active', 'inactive', 'pending']);
    console.log('All validations passed');
} catch (error) {
    console.error('Validation error:', error.message);
}

// HTML escaping to prevent XSS
function escapeHtml(text) {
    const map = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&#039;'
    };
    return text.replace(/[&<>"']/g, m => map[m]);
}

// Safe HTML rendering
function safeRender(userInput) {
    const escaped = escapeHtml(userInput);
    document.getElementById('output').textContent = escaped;
    // or use textContent instead of innerHTML
}

// URL parameter sanitization
function sanitizeUrlParam(param) {
    return encodeURIComponent(param);
}

// Build safe URL
function buildUrl(base, params) {
    const queryString = Object.entries(params)
        .map(([key, value]) => 
            `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
        )
        .join('&');
    
    return `${base}?${queryString}`;
}

const url = buildUrl('https://api.example.com/search', {
    q: 'user input <script>',
    page: 1
});
// Result: https://api.example.com/search?q=user%20input%20%3Cscript%3E&page=1

// Filename sanitization (prevent path traversal)
function sanitizeFilename(filename) {
    // Remove path separators and null bytes
    return filename
        .replace(/[\/\\]/g, '')
        .replace(/\0/g, '')
        .replace(/\.\./g, '')
        .slice(0, 255); // Limit length
}

// Path traversal prevention
function safePath(userPath, baseDir) {
    const path = require('path');
    const resolvedPath = path.resolve(baseDir, userPath);
    
    // Ensure resolved path is within baseDir
    if (!resolvedPath.startsWith(path.resolve(baseDir))) {
        throw new Error('Path traversal detected');
    }
    
    return resolvedPath;
}

// Schema validation with custom validator
const userSchema = {
    username: {
        type: 'string',
        minLength: 3,
        maxLength: 20,
        pattern: /^[a-zA-Z0-9_]+$/
    },
    email: {
        type: 'string',
        validator: isValidEmail
    },
    age: {
        type: 'number',
        min: 18,
        max: 120,
        integer: true
    },
    role: {
        type: 'enum',
        values: ['user', 'admin', 'moderator']
    }
};

function validateSchema(data, schema) {
    const errors = [];
    
    for (const [field, rules] of Object.entries(schema)) {
        const value = data[field];
        
        try {
            if (rules.type === 'string') {
                InputValidator.validateString(value, rules);
            } else if (rules.type === 'number') {
                InputValidator.validateNumber(value, rules);
            } else if (rules.type === 'enum') {
                InputValidator.validateEnum(value, rules.values);
            }
            
            if (rules.validator && !rules.validator(value)) {
                errors.push(`${field}: Custom validation failed`);
            }
        } catch (error) {
            errors.push(`${field}: ${error.message}`);
        }
    }
    
    return {
        valid: errors.length === 0,
        errors
    };
}

// Usage
const userData = {
    username: 'john_doe',
    email: 'john@example.com',
    age: 25,
    role: 'user'
};

const result = validateSchema(userData, userSchema);
if (result.valid) {
    console.log('User data is valid');
} else {
    console.error('Validation errors:', result.errors);
}

// SQL injection prevention (using parameterized queries)
// BAD: Vulnerable to SQL injection
function getUserBad(userId) {
    const query = `SELECT * FROM users WHERE id = ${userId}`;
    // If userId is "1 OR 1=1", returns all users!
    return db.query(query);
}

// GOOD: Parameterized query
function getUserGood(userId) {
    const query = 'SELECT * FROM users WHERE id = ?';
    return db.query(query, [userId]);
}

// ORM approach (safer)
function getUserORM(userId) {
    return User.findById(userId);
}

// Command injection prevention
// BAD: Vulnerable to command injection
function processBad(filename) {
    const exec = require('child_process').exec;
    exec(`cat ${filename}`, (error, stdout) => {
        console.log(stdout);
    });
    // If filename is "file.txt; rm -rf /", disaster!
}

// GOOD: Use array syntax, avoid shell
function processGood(filename) {
    const {execFile} = require('child_process');
    execFile('cat', [filename], (error, stdout) => {
        console.log(stdout);
    });
}

// Rate limiting for API endpoints
class RateLimiter {
    constructor(maxRequests, windowMs) {
        this.maxRequests = maxRequests;
        this.windowMs = windowMs;
        this.requests = new Map();
    }
    
    isAllowed(identifier) {
        const now = Date.now();
        const userRequests = this.requests.get(identifier) || [];
        
        // Remove old requests outside window
        const validRequests = userRequests.filter(
            time => now - time < this.windowMs
        );
        
        if (validRequests.length >= this.maxRequests) {
            return false;
        }
        
        validRequests.push(now);
        this.requests.set(identifier, validRequests);
        return true;
    }
}

// Usage
const limiter = new RateLimiter(100, 60000); // 100 requests per minute

function handleRequest(req, res) {
    const userId = req.userId;
    
    if (!limiter.isAllowed(userId)) {
        return res.status(429).json({error: 'Too many requests'});
    }
    
    // Process request
}

// Content Security Policy (CSP) nonce generation
function generateNonce() {
    const crypto = require('crypto');
    return crypto.randomBytes(16).toString('base64');
}
Key Points: Always validate input on server side (client-side validation is for UX only). Use whitelist validation when possible. Escape HTML to prevent XSS. Use parameterized queries to prevent SQL injection. Sanitize filenames to prevent path traversal. Implement rate limiting to prevent DoS. Never trust user input. Validate type, length, format, and range. Use established libraries for validation (Joi, Yup, validator.js).

2. XSS Prevention and Content Security Policy

XSS Attack Types

Type Description Example
Reflected XSS Script in URL/input reflected immediately ?q=<script>alert(1)</script>
Stored XSS Script stored in database, served to users Comment with malicious script
DOM-based XSS Client-side script manipulation document.write(location.hash)
Mutation XSS Browser mutates sanitized HTML Nested tags, encoding tricks

XSS Prevention Techniques

Technique How It Works Implementation
Output Encoding Escape HTML entities &lt; &gt; &amp; &quot;
Use textContent Automatically escapes content element.textContent = data
Avoid innerHTML Prevents script execution Use DOM methods instead
CSP Headers Restrict script sources Content-Security-Policy header
DOMPurify Sanitize HTML safely Library for HTML cleaning

CSP Directives

Directive Purpose Example Value
default-src Fallback for all sources 'self'
script-src JavaScript sources 'self' 'nonce-xyz123'
style-src CSS sources 'self' 'unsafe-inline'
img-src Image sources 'self' data: https:
connect-src XHR/WebSocket/EventSource 'self' https://api.example.com
font-src Font sources 'self' https://fonts.gstatic.com
frame-ancestors Who can embed in iframe 'none' or 'self'

Example: XSS prevention

// XSS vulnerable code examples
// BAD: innerHTML with user input
function displayUsernameBad(username) {
    document.getElementById('greeting').innerHTML = 
        `<h1>Welcome, ${username}!</h1>`;
    // If username is "<img src=x onerror=alert('XSS')>", XSS occurs!
}

// GOOD: Use textContent
function displayUsernameGood(username) {
    const h1 = document.createElement('h1');
    h1.textContent = `Welcome, ${username}!`;
    document.getElementById('greeting').appendChild(h1);
}

// BAD: eval with user input
function executeBad(userCode) {
    eval(userCode); // NEVER DO THIS!
}

// GOOD: Use safe alternatives
function executeGood(userCode) {
    // If you must parse JSON
    const data = JSON.parse(userCode);
    
    // For calculations, use Function constructor with validation
    // But still be very careful!
}

// BAD: Direct DOM manipulation from URL
function displayFromUrlBad() {
    const params = new URLSearchParams(window.location.search);
    document.getElementById('content').innerHTML = params.get('message');
}

// GOOD: Sanitize before display
function displayFromUrlGood() {
    const params = new URLSearchParams(window.location.search);
    const message = params.get('message');
    document.getElementById('content').textContent = message;
}

// HTML escaping function
function escapeHtml2(unsafe) {
    return unsafe
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#039;');
}

// JavaScript escaping (for embedding in <script> tags)
function escapeJs(unsafe) {
    return unsafe
        .replace(/\\/g, '\\\\')
        .replace(/'/g, "\\'")
        .replace(/"/g, '\\"')
        .replace(/\n/g, '\\n')
        .replace(/\r/g, '\\r')
        .replace(/</g, '\\x3c')
        .replace(/>/g, '\\x3e');
}

// URL encoding
function escapeUrl(unsafe) {
    return encodeURIComponent(unsafe);
}

// Context-aware escaping
function safeDomInsert(element, content, context) {
    switch (context) {
        case 'text':
            element.textContent = content;
            break;
            
        case 'attribute':
            element.setAttribute('data-value', escapeHtml2(content));
            break;
            
        case 'url':
            element.href = escapeUrl(content);
            break;
            
        case 'html':
            // Use DOMPurify if you must allow HTML
            // element.innerHTML = DOMPurify.sanitize(content);
            break;
            
        default:
            throw new Error('Invalid context');
    }
}

// DOMPurify usage (safe HTML sanitization)
// import DOMPurify from 'dompurify';

function renderSafeHtml(userHtml) {
    const clean = DOMPurify.sanitize(userHtml, {
        ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p'],
        ALLOWED_ATTR: ['href']
    });
    
    document.getElementById('content').innerHTML = clean;
}

// CSP Implementation
// Setting CSP via HTTP header (server-side)
/*
Content-Security-Policy: 
    default-src 'self'; 
    script-src 'self' 'nonce-xyz123' https://trusted.cdn.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; 
    frame-ancestors 'none'; 
    base-uri 'self'; 
    form-action 'self'
*/

// Setting CSP via meta tag
const cspMeta = document.createElement('meta');
cspMeta.httpEquiv = 'Content-Security-Policy';
cspMeta.content = "default-src 'self'; script-src 'self' 'unsafe-inline'";
document.head.appendChild(cspMeta);

// Nonce-based CSP
// Server generates nonce
function generateCspNonce() {
    const crypto = require('crypto');
    return crypto.randomBytes(16).toString('base64');
}

// In Express.js middleware
function cspMiddleware(req, res, next) {
    const nonce = generateCspNonce();
    res.locals.nonce = nonce;
    
    res.setHeader(
        'Content-Security-Policy',
        `script-src 'self' 'nonce-${nonce}'`
    );
    
    next();
}

// In HTML template
// <script nonce="<%= nonce %>">
//   console.log('This script is allowed');
// </script>

// CSP violation reporting
const cspWithReporting = `
    default-src 'self'; 
    script-src 'self'; 
    report-uri /csp-violation-report-endpoint; 
    report-to csp-endpoint
`;

// Report-To header
const reportTo = JSON.stringify({
    group: 'csp-endpoint',
    max_age: 10886400,
    endpoints: [{url: 'https://example.com/csp-reports'}]
});

// Handle CSP violation reports
function handleCspReport(req, res) {
    const report = req.body;
    console.log('CSP Violation:', {
        documentUri: report['document-uri'],
        violatedDirective: report['violated-directive'],
        blockedUri: report['blocked-uri'],
        sourceFile: report['source-file'],
        lineNumber: report['line-number']
    });
    
    res.status(204).send();
}

// Trusted Types API (modern XSS prevention)
if (window.trustedTypes && trustedTypes.createPolicy) {
    const policy = trustedTypes.createPolicy('myPolicy', {
        createHTML: (string) => {
            // Sanitize string
            return DOMPurify.sanitize(string);
        },
        createScriptURL: (string) => {
            // Validate script URL
            const url = new URL(string, window.location.href);
            if (url.origin === window.location.origin) {
                return string;
            }
            throw new TypeError('Invalid script URL');
        }
    });
    
    // Usage with Trusted Types
    const html = policy.createHTML('<b>Safe HTML</b>');
    element.innerHTML = html; // No XSS!
}

// XSS protection headers
function setSecurityHeaders(res) {
    // XSS Protection (legacy browsers)
    res.setHeader('X-XSS-Protection', '1; mode=block');
    
    // Prevent MIME sniffing
    res.setHeader('X-Content-Type-Options', 'nosniff');
    
    // Frame options (clickjacking prevention)
    res.setHeader('X-Frame-Options', 'DENY');
    
    // Referrer policy
    res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
}

// Safe JSON embedding in HTML
function safeJsonEmbed(data) {
    return JSON.stringify(data)
        .replace(/</g, '\\u003c')
        .replace(/>/g, '\\u003e')
        .replace(/&/g, '\\u0026');
}

// In HTML:
// <script>
//   const data = <%= safeJsonEmbed(userData) %>;
// </script>

// Safe event handler binding
// BAD: Inline event handlers
// <button onclick="userFunction()">Click</button>

// GOOD: addEventListener
const button = document.getElementById('myButton');
button.addEventListener('click', function(e) {
    e.preventDefault();
    // Safe handler
});

// Sanitize URLs to prevent javascript: protocol
function isSafeUrl(url) {
    try {
        const parsed = new URL(url, window.location.href);
        return ['http:', 'https:', 'mailto:'].includes(parsed.protocol);
    } catch {
        return false;
    }
}

function setSafeHref(element, url) {
    if (isSafeUrl(url)) {
        element.href = url;
    } else {
        console.error('Unsafe URL blocked:', url);
        element.href = '#';
    }
}
Key Points: Never use innerHTML with user input - use textContent or DOM methods. Never use eval() or Function() with user input. Implement CSP headers to restrict script sources. Use nonce or hash for inline scripts. DOMPurify for safe HTML sanitization. Trusted Types API for modern browsers. Context-aware escaping (HTML, JavaScript, URL, CSS). Validate URLs to prevent javascript: protocol. Report CSP violations for monitoring.

3. Secure Coding Practices and Code Review

OWASP Top 10 Web Vulnerabilities

Vulnerability Risk Prevention
Broken Access Control Unauthorized access to resources Enforce authorization checks
Cryptographic Failures Sensitive data exposure Encrypt data at rest and transit
Injection SQL/Command/LDAP injection Parameterized queries, validation
Insecure Design Missing security controls Threat modeling, secure design
Security Misconfiguration Default configs, verbose errors Harden configs, disable defaults
Vulnerable Components Outdated libraries with CVEs Regular updates, dependency scanning
Authentication Failures Weak auth, session management Strong passwords, MFA, secure sessions
Data Integrity Failures Insecure deserialization Validate serialized data, use safe formats
Logging Failures Insufficient monitoring Comprehensive logging, alerting
SSRF Server-side request forgery Whitelist URLs, validate destinations

Secure Coding Principles

Principle Description Example
Defense in Depth Multiple security layers Input validation + CSP + WAF
Least Privilege Minimum necessary permissions Read-only DB user for queries
Fail Securely Deny by default on errors Default to unauthorized
Complete Mediation Check every access Validate auth on each request
Open Design Security through design, not obscurity Public algorithms, secret keys
Separation of Duties Split critical operations Approver ≠ Requester

Code Review Security Checklist

Category What to Check Red Flags
Input Validation All inputs validated and sanitized Direct use of user input
Authentication Proper auth checks, session management Missing auth, weak passwords
Authorization Access control enforced Missing permission checks
Cryptography Strong algorithms, proper key management Hardcoded secrets, weak ciphers
Error Handling No sensitive info in errors Stack traces to users
Logging Security events logged Passwords in logs
Dependencies Up-to-date, no known vulnerabilities Outdated libraries

Example: Secure coding practices

// Secure configuration management
// BAD: Hardcoded secrets
const API_KEY = 'sk_live_abc123xyz'; // NEVER DO THIS!
const DB_PASSWORD = 'mypassword123';

// GOOD: Environment variables
const API_KEY2 = process.env.API_KEY;
const DB_PASSWORD2 = process.env.DB_PASSWORD;

// BETTER: Use a secrets manager
const {SecretManagerServiceClient} = require('@google-cloud/secret-manager');
const client = new SecretManagerServiceClient();

async function getSecret(secretName) {
    const [version] = await client.accessSecretVersion({
        name: secretName
    });
    return version.payload.data.toString();
}

// Error handling - don't leak sensitive info
// BAD: Verbose error messages
function handleErrorBad(error, res) {
    res.status(500).json({
        error: error.message,
        stack: error.stack, // Exposes internal structure!
        query: error.sql    // Exposes SQL queries!
    });
}

// GOOD: Generic error messages
function handleErrorGood(error, res) {
    console.error('Internal error:', error); // Log for debugging
    
    res.status(500).json({
        error: 'An error occurred. Please try again later.',
        requestId: generateRequestId() // For support tracking
    });
}

// Secure session management
const session = require('express-session');
const RedisStore = require('connect-redis')(session);

app.use(session({
    store: new RedisStore({client: redisClient}),
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: false,
    cookie: {
        secure: true,        // HTTPS only
        httpOnly: true,      // No JavaScript access
        sameSite: 'strict',  // CSRF protection
        maxAge: 3600000     // 1 hour
    },
    name: 'sessionId'       // Don't use default name
}));

// Authorization middleware
function requireAuth(req, res, next) {
    if (!req.session.userId) {
        return res.status(401).json({error: 'Unauthorized'});
    }
    next();
}

function requireRole(role) {
    return async (req, res, next) => {
        const user = await User.findById(req.session.userId);
        
        if (!user || user.role !== role) {
            return res.status(403).json({error: 'Forbidden'});
        }
        
        next();
    };
}

// Usage
app.get('/admin', requireAuth, requireRole('admin'), (req, res) => {
    // Admin-only endpoint
});

// Secure file uploads
const multer = require('multer');
const path = require('path');

const storage = multer.diskStorage({
    destination: './uploads/',
    filename: (req, file, cb) => {
        // Generate safe filename
        const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
        cb(null, uniqueSuffix + path.extname(file.originalname));
    }
});

const upload = multer({
    storage: storage,
    limits: {
        fileSize: 5 * 1024 * 1024 // 5MB limit
    },
    fileFilter: (req, file, cb) => {
        // Whitelist file types
        const allowedTypes = /jpeg|jpg|png|pdf/;
        const extname = allowedTypes.test(
            path.extname(file.originalname).toLowerCase()
        );
        const mimetype = allowedTypes.test(file.mimetype);
        
        if (extname && mimetype) {
            cb(null, true);
        } else {
            cb(new Error('Invalid file type'));
        }
    }
});

// Resource access control
class ResourceAccessControl {
    static canAccess(user, resource, action) {
        // Check if user owns resource
        if (resource.userId === user.id) {
            return true;
        }
        
        // Check role-based permissions
        const permissions = {
            admin: ['read', 'write', 'delete'],
            moderator: ['read', 'write'],
            user: ['read']
        };
        
        const userPermissions = permissions[user.role] || [];
        return userPermissions.includes(action);
    }
}

// Usage in endpoint
app.delete('/posts/:id', requireAuth, async (req, res) => {
    const post = await Post.findById(req.params.id);
    const user = await User.findById(req.session.userId);
    
    if (!ResourceAccessControl.canAccess(user, post, 'delete')) {
        return res.status(403).json({error: 'Forbidden'});
    }
    
    await post.delete();
    res.json({success: true});
});

// Secure random number generation
// BAD: Predictable
const badRandom = Math.random();

// GOOD: Cryptographically secure
const crypto = require('crypto');
const goodRandom = crypto.randomBytes(32).toString('hex');

// Generate secure token
function generateSecureToken() {
    return crypto.randomBytes(32).toString('base64url');
}

// Time-constant string comparison (prevent timing attacks)
// BAD: Vulnerable to timing attacks
function compareTokenBad(a, b) {
    return a === b; // Early return leaks info
}

// GOOD: Constant-time comparison
function compareTokenGood(a, b) {
    return crypto.timingSafeEqual(
        Buffer.from(a),
        Buffer.from(b)
    );
}

// Dependency scanning
// package.json scripts
{
    "scripts": {
        "audit": "npm audit",
        "audit:fix": "npm audit fix",
        "audit:check": "npm audit --audit-level=high"
    }
}

// Automated security checks in CI/CD
// .github/workflows/security.yml
/*
name: Security Scan
on: [push, pull_request]
jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Run npm audit
        run: npm audit --audit-level=moderate
      - name: Run Snyk
        run: npx snyk test
*/

// Secure API key storage
class ApiKeyManager {
    constructor() {
        this.keys = new Map();
    }
    
    async storeKey(userId, apiKey) {
        const crypto = require('crypto');
        
        // Hash the key (don't store plain text)
        const hash = crypto
            .createHash('sha256')
            .update(apiKey)
            .digest('hex');
        
        await db.apiKeys.insert({
            userId,
            keyHash: hash,
            createdAt: new Date()
        });
        
        // Return key once to user, then forget it
        return apiKey;
    }
    
    async validateKey(apiKey) {
        const hash = crypto
            .createHash('sha256')
            .update(apiKey)
            .digest('hex');
        
        const record = await db.apiKeys.findOne({keyHash: hash});
        return !!record;
    }
}

// Logging security events
class SecurityLogger {
    static logAuthSuccess(userId, ip) {
        logger.info('Auth success', {userId, ip, event: 'LOGIN'});
    }
    
    static logAuthFailure(username, ip, reason) {
        logger.warn('Auth failure', {username, ip, reason, event: 'LOGIN_FAIL'});
    }
    
    static logAccessDenied(userId, resource, action) {
        logger.warn('Access denied', {
            userId,
            resource,
            action,
            event: 'ACCESS_DENIED'
        });
    }
    
    static logSensitiveOperation(userId, operation, details) {
        logger.info('Sensitive operation', {
            userId,
            operation,
            details,
            event: 'SENSITIVE_OP'
        });
    }
}

// Never log sensitive data
// BAD
logger.info('User login', {password: userPassword}); // NEVER!

// GOOD
logger.info('User login', {userId: user.id, timestamp: new Date()});

// Secure headers middleware
function securityHeaders(req, res, next) {
    // Prevent clickjacking
    res.setHeader('X-Frame-Options', 'DENY');
    
    // Prevent MIME sniffing
    res.setHeader('X-Content-Type-Options', 'nosniff');
    
    // XSS protection
    res.setHeader('X-XSS-Protection', '1; mode=block');
    
    // HTTPS only
    res.setHeader('Strict-Transport-Security', 
        'max-age=31536000; includeSubDomains; preload');
    
    // Referrer policy
    res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
    
    // Permissions policy
    res.setHeader('Permissions-Policy', 
        'geolocation=(), microphone=(), camera=()');
    
    next();
}

app.use(securityHeaders);
Key Points: Never hardcode secrets - use environment variables or secrets manager. Don't expose detailed errors to users. Implement proper authorization checks. Use secure session management (httpOnly, secure, sameSite cookies). Whitelist file uploads by type and size. Use cryptographically secure random for tokens. Constant-time comparison for sensitive strings. Run npm audit regularly. Log security events without sensitive data. Apply security headers. Follow OWASP Top 10. Code review for security issues. Defense in depth approach.

4. Data Encryption and Hashing

Encryption vs Hashing

Feature Encryption Hashing
Reversible Yes (with key) No (one-way)
Purpose Protect data confidentiality Verify data integrity
Use Case Store credit cards, encrypt messages Store passwords, checksums
Output Variable length ciphertext Fixed length hash digest
Key Required Yes No (salt optional)

Encryption Algorithms

Algorithm Type Key Size Use Case
AES Symmetric 128, 192, 256 bits General purpose encryption
RSA Asymmetric 2048, 4096 bits Key exchange, digital signatures
ChaCha20 Symmetric stream 256 bits Mobile devices, TLS
ECC Asymmetric 256, 384 bits Smaller keys, IoT devices

Hashing Algorithms

Algorithm Output Size Status Use Case
bcrypt 60 chars ✓ Secure Password hashing (recommended)
scrypt Variable ✓ Secure Password hashing, key derivation
Argon2 Variable ✓ Secure (newest) Password hashing (best)
SHA-256 256 bits ✓ Secure Checksums, NOT passwords
SHA-1 160 bits ✗ Broken Deprecated
MD5 128 bits ✗ Broken Never use

Example: Encryption and hashing

const crypto = require('crypto');

// === PASSWORD HASHING ===

// BAD: Plain MD5 (broken, too fast)
function hashPasswordBad(password) {
    return crypto.createHash('md5').update(password).digest('hex');
    // Vulnerable to rainbow tables and brute force
}

// GOOD: bcrypt with salt
const bcrypt = require('bcrypt');

async function hashPassword(password) {
    const saltRounds = 12; // Higher = more secure but slower
    const hash = await bcrypt.hash(password, saltRounds);
    return hash;
}

async function verifyPassword(password, hash) {
    return await bcrypt.compare(password, hash);
}

// Usage
async function registerUser(username, password) {
    const passwordHash = await hashPassword(password);
    
    await db.users.insert({
        username,
        passwordHash, // Store hash, never plain password
        createdAt: new Date()
    });
}

async function loginUser(username, password) {
    const user = await db.users.findOne({username});
    
    if (!user) {
        return false;
    }
    
    const isValid = await verifyPassword(password, user.passwordHash);
    return isValid ? user : null;
}

// === SYMMETRIC ENCRYPTION (AES) ===

// Encrypt data with AES-256-GCM
function encrypt(plaintext, key) {
    // Generate random IV (initialization vector)
    const iv = crypto.randomBytes(16);
    
    // Create cipher
    const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
    
    // Encrypt
    let encrypted = cipher.update(plaintext, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    
    // Get authentication tag
    const authTag = cipher.getAuthTag();
    
    // Return IV + authTag + encrypted data
    return {
        iv: iv.toString('hex'),
        authTag: authTag.toString('hex'),
        encrypted: encrypted
    };
}

function decrypt(encryptedData, key) {
    // Create decipher
    const decipher = crypto.createDecipheriv(
        'aes-256-gcm',
        key,
        Buffer.from(encryptedData.iv, 'hex')
    );
    
    // Set authentication tag
    decipher.setAuthTag(Buffer.from(encryptedData.authTag, 'hex'));
    
    // Decrypt
    let decrypted = decipher.update(encryptedData.encrypted, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    
    return decrypted;
}

// Usage
const encryptionKey = crypto.randomBytes(32); // 256 bits
const message = 'Sensitive data';

const encrypted = encrypt(message, encryptionKey);
console.log('Encrypted:', encrypted);

const decrypted = decrypt(encrypted, encryptionKey);
console.log('Decrypted:', decrypted); // 'Sensitive data'

// === KEY DERIVATION ===

// Derive encryption key from password using PBKDF2
function deriveKey(password, salt) {
    return crypto.pbkdf2Sync(
        password,
        salt,
        100000,  // iterations
        32,      // key length (256 bits)
        'sha256' // hash algorithm
    );
}

// Encrypt with password
function encryptWithPassword(plaintext, password) {
    const salt = crypto.randomBytes(16);
    const key = deriveKey(password, salt);
    const encrypted = encrypt(plaintext, key);
    
    return {
        salt: salt.toString('hex'),
        ...encrypted
    };
}

function decryptWithPassword(encryptedData, password) {
    const salt = Buffer.from(encryptedData.salt, 'hex');
    const key = deriveKey(password, salt);
    
    return decrypt({
        iv: encryptedData.iv,
        authTag: encryptedData.authTag,
        encrypted: encryptedData.encrypted
    }, key);
}

// === ASYMMETRIC ENCRYPTION (RSA) ===

// Generate RSA key pair
function generateKeyPair() {
    const {publicKey, privateKey} = crypto.generateKeyPairSync('rsa', {
        modulusLength: 4096,
        publicKeyEncoding: {
            type: 'spki',
            format: 'pem'
        },
        privateKeyEncoding: {
            type: 'pkcs8',
            format: 'pem',
            cipher: 'aes-256-cbc',
            passphrase: process.env.KEY_PASSPHRASE
        }
    });
    
    return {publicKey, privateKey};
}

// Encrypt with public key
function rsaEncrypt(plaintext, publicKey) {
    const buffer = Buffer.from(plaintext, 'utf8');
    const encrypted = crypto.publicEncrypt(publicKey, buffer);
    return encrypted.toString('base64');
}

// Decrypt with private key
function rsaDecrypt(encrypted, privateKey, passphrase) {
    const buffer = Buffer.from(encrypted, 'base64');
    const decrypted = crypto.privateDecrypt(
        {
            key: privateKey,
            passphrase: passphrase
        },
        buffer
    );
    return decrypted.toString('utf8');
}

// Digital signature
function sign(message, privateKey, passphrase) {
    const signer = crypto.createSign('sha256');
    signer.update(message);
    signer.end();
    
    return signer.sign({
        key: privateKey,
        passphrase: passphrase
    }, 'base64');
}

function verify(message, signature, publicKey) {
    const verifier = crypto.createVerify('sha256');
    verifier.update(message);
    verifier.end();
    
    return verifier.verify(publicKey, signature, 'base64');
}

// === HMAC (Hash-based Message Authentication Code) ===

function generateHmac(message, secret) {
    return crypto
        .createHmac('sha256', secret)
        .update(message)
        .digest('hex');
}

function verifyHmac(message, hmac, secret) {
    const calculated = generateHmac(message, secret);
    return crypto.timingSafeEqual(
        Buffer.from(hmac),
        Buffer.from(calculated)
    );
}

// Use for API request signing
function signRequest(payload, apiSecret) {
    const timestamp = Date.now();
    const message = `${timestamp}.${JSON.stringify(payload)}`;
    const signature = generateHmac(message, apiSecret);
    
    return {
        timestamp,
        payload,
        signature
    };
}

function verifyRequest(request, apiSecret, maxAge = 300000) {
    const {timestamp, payload, signature} = request;
    
    // Check timestamp (prevent replay attacks)
    if (Date.now() - timestamp > maxAge) {
        return false;
    }
    
    // Verify signature
    const message = `${timestamp}.${JSON.stringify(payload)}`;
    return verifyHmac(message, signature, apiSecret);
}

// === SECURE TOKEN GENERATION ===

function generateToken(length = 32) {
    return crypto.randomBytes(length).toString('base64url');
}

// JWT-like token (simplified)
function createToken(payload, secret, expiresIn = 3600) {
    const header = {alg: 'HS256', typ: 'JWT'};
    const claims = {
        ...payload,
        iat: Math.floor(Date.now() / 1000),
        exp: Math.floor(Date.now() / 1000) + expiresIn
    };
    
    const encodedHeader = Buffer.from(JSON.stringify(header)).toString('base64url');
    const encodedPayload = Buffer.from(JSON.stringify(claims)).toString('base64url');
    const signature = generateHmac(`${encodedHeader}.${encodedPayload}`, secret);
    
    return `${encodedHeader}.${encodedPayload}.${signature}`;
}

function verifyToken(token, secret) {
    const [encodedHeader, encodedPayload, signature] = token.split('.');
    
    // Verify signature
    const expectedSignature = generateHmac(
        `${encodedHeader}.${encodedPayload}`,
        secret
    );
    
    if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) {
        throw new Error('Invalid signature');
    }
    
    // Decode and check expiration
    const payload = JSON.parse(Buffer.from(encodedPayload, 'base64url').toString());
    
    if (payload.exp < Math.floor(Date.now() / 1000)) {
        throw new Error('Token expired');
    }
    
    return payload;
}

// === ENCRYPTING DATA AT REST ===

class EncryptedStorage {
    constructor(key) {
        this.key = key;
    }
    
    set(key, value) {
        const encrypted = encrypt(JSON.stringify(value), this.key);
        localStorage.setItem(key, JSON.stringify(encrypted));
    }
    
    get(key) {
        const stored = localStorage.getItem(key);
        if (!stored) return null;
        
        const encrypted = JSON.parse(stored);
        const decrypted = decrypt(encrypted, this.key);
        return JSON.parse(decrypted);
    }
    
    remove(key) {
        localStorage.removeItem(key);
    }
}

// Usage
const masterKey = deriveKey(userPassword, userSalt);
const storage = new EncryptedStorage(masterKey);

storage.set('sensitiveData', {ssn: '123-45-6789', account: '9876543210'});
const data = storage.get('sensitiveData');

// === WEB CRYPTO API (Browser) ===

async function generateAesKey() {
    return await window.crypto.subtle.generateKey(
        {
            name: 'AES-GCM',
            length: 256
        },
        true,  // extractable
        ['encrypt', 'decrypt']
    );
}

async function encryptBrowser(plaintext, key) {
    const iv = window.crypto.getRandomValues(new Uint8Array(12));
    const encoded = new TextEncoder().encode(plaintext);
    
    const ciphertext = await window.crypto.subtle.encrypt(
        {
            name: 'AES-GCM',
            iv: iv
        },
        key,
        encoded
    );
    
    return {
        iv: Array.from(iv),
        ciphertext: Array.from(new Uint8Array(ciphertext))
    };
}

async function decryptBrowser(encrypted, key) {
    const decrypted = await window.crypto.subtle.decrypt(
        {
            name: 'AES-GCM',
            iv: new Uint8Array(encrypted.iv)
        },
        key,
        new Uint8Array(encrypted.ciphertext)
    );
    
    return new TextDecoder().decode(decrypted);
}

// === KEY MANAGEMENT BEST PRACTICES ===

// Store keys securely
// - Use environment variables
// - Use a secrets manager (AWS Secrets Manager, HashiCorp Vault)
// - Never commit keys to version control
// - Rotate keys regularly
// - Use different keys for different environments

// Key rotation example
class KeyRotation {
    constructor() {
        this.currentKey = null;
        this.oldKeys = [];
    }
    
    rotateKey() {
        if (this.currentKey) {
            this.oldKeys.push(this.currentKey);
        }
        this.currentKey = crypto.randomBytes(32);
        
        // Keep only last 3 old keys
        if (this.oldKeys.length > 3) {
            this.oldKeys.shift();
        }
    }
    
    encrypt(data) {
        return encrypt(data, this.currentKey);
    }
    
    decrypt(encryptedData) {
        // Try current key first
        try {
            return decrypt(encryptedData, this.currentKey);
        } catch (error) {
            // Try old keys
            for (const oldKey of this.oldKeys) {
                try {
                    return decrypt(encryptedData, oldKey);
                } catch {
                    continue;
                }
            }
            throw new Error('Failed to decrypt with any key');
        }
    }
}
Key Points: Use bcrypt/Argon2 for password hashing, never plain MD5/SHA. Use AES-256-GCM for symmetric encryption. Use RSA/ECC for asymmetric encryption. Always use random IV for each encryption. HMAC for message authentication. Derive keys from passwords with PBKDF2/scrypt. Store keys securely (never hardcode). Rotate keys regularly. Use Web Crypto API in browsers. Encryption protects confidentiality, hashing verifies integrity. Salt passwords to prevent rainbow tables.

5. Authentication and Authorization Patterns

Authentication Methods

Method How It Works Use Case
Session-based Server stores session, client has session ID Traditional web apps
Token-based (JWT) Stateless token with claims APIs, SPAs, mobile apps
OAuth 2.0 Delegated authorization Third-party login
OpenID Connect OAuth 2.0 + identity layer SSO, enterprise auth
API Keys Static key per client Server-to-server
MFA/2FA Multiple authentication factors High security accounts

Authorization Models

Model Description Example
RBAC Role-Based Access Control Admin, User, Guest roles
ABAC Attribute-Based Access Control Rules based on attributes
ACL Access Control List Per-resource permissions
PBAC Policy-Based Access Control Complex business rules

JWT Structure

Part Content Purpose
Header Algorithm, token type Specify signing method
Payload Claims (user ID, exp, etc.) Carry user information
Signature HMAC or RSA signature Verify token integrity

Example: Authentication and authorization

// === JWT AUTHENTICATION ===

const jwt = require('jsonwebtoken');
const JWT_SECRET = process.env.JWT_SECRET;

// Generate JWT token
function generateJWT(user) {
    const payload = {
        userId: user.id,
        email: user.email,
        role: user.role
    };
    
    const token = jwt.sign(
        payload,
        JWT_SECRET,
        {
            expiresIn: '1h',
            issuer: 'myapp.com',
            audience: 'myapp-users'
        }
    );
    
    return token;
}

// Verify JWT token
function verifyJWT(token) {
    try {
        const decoded = jwt.verify(token, JWT_SECRET, {
            issuer: 'myapp.com',
            audience: 'myapp-users'
        });
        return decoded;
    } catch (error) {
        if (error.name === 'TokenExpiredError') {
            throw new Error('Token expired');
        } else if (error.name === 'JsonWebTokenError') {
            throw new Error('Invalid token');
        }
        throw error;
    }
}

// Authentication middleware
function authenticate(req, res, next) {
    const authHeader = req.headers.authorization;
    
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
        return res.status(401).json({error: 'No token provided'});
    }
    
    const token = authHeader.substring(7);
    
    try {
        const decoded = verifyJWT(token);
        req.user = decoded;
        next();
    } catch (error) {
        return res.status(401).json({error: error.message});
    }
}

// === REFRESH TOKEN PATTERN ===

function generateTokenPair(user) {
    const accessToken = jwt.sign(
        {userId: user.id, email: user.email, role: user.role},
        JWT_SECRET,
        {expiresIn: '15m'} // Short-lived
    );
    
    const refreshToken = jwt.sign(
        {userId: user.id, type: 'refresh'},
        process.env.REFRESH_TOKEN_SECRET,
        {expiresIn: '7d'} // Long-lived
    );
    
    return {accessToken, refreshToken};
}

async function refreshAccessToken(refreshToken) {
    try {
        const decoded = jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET);
        
        if (decoded.type !== 'refresh') {
            throw new Error('Invalid token type');
        }
        
        // Check if refresh token is revoked
        const isRevoked = await checkTokenRevocation(refreshToken);
        if (isRevoked) {
            throw new Error('Token revoked');
        }
        
        // Generate new access token
        const user = await User.findById(decoded.userId);
        const accessToken = generateJWT(user);
        
        return accessToken;
    } catch (error) {
        throw new Error('Invalid refresh token');
    }
}

// === ROLE-BASED ACCESS CONTROL (RBAC) ===

const ROLES = {
    ADMIN: 'admin',
    MODERATOR: 'moderator',
    USER: 'user',
    GUEST: 'guest'
};

const PERMISSIONS = {
    READ_POSTS: 'read:posts',
    WRITE_POSTS: 'write:posts',
    DELETE_POSTS: 'delete:posts',
    MANAGE_USERS: 'manage:users',
    VIEW_ANALYTICS: 'view:analytics'
};

const ROLE_PERMISSIONS = {
    [ROLES.ADMIN]: [
        PERMISSIONS.READ_POSTS,
        PERMISSIONS.WRITE_POSTS,
        PERMISSIONS.DELETE_POSTS,
        PERMISSIONS.MANAGE_USERS,
        PERMISSIONS.VIEW_ANALYTICS
    ],
    [ROLES.MODERATOR]: [
        PERMISSIONS.READ_POSTS,
        PERMISSIONS.WRITE_POSTS,
        PERMISSIONS.DELETE_POSTS
    ],
    [ROLES.USER]: [
        PERMISSIONS.READ_POSTS,
        PERMISSIONS.WRITE_POSTS
    ],
    [ROLES.GUEST]: [
        PERMISSIONS.READ_POSTS
    ]
};

function hasPermission(userRole, permission) {
    const permissions = ROLE_PERMISSIONS[userRole] || [];
    return permissions.includes(permission);
}

// Authorization middleware
function requirePermission(permission) {
    return (req, res, next) => {
        if (!req.user) {
            return res.status(401).json({error: 'Unauthorized'});
        }
        
        if (!hasPermission(req.user.role, permission)) {
            return res.status(403).json({error: 'Forbidden'});
        }
        
        next();
    };
}

// Usage
app.delete('/posts/:id', 
    authenticate, 
    requirePermission(PERMISSIONS.DELETE_POSTS),
    async (req, res) => {
        // Delete post
    }
);

// === ATTRIBUTE-BASED ACCESS CONTROL (ABAC) ===

class AbacPolicy {
    static canAccess(subject, resource, action, context) {
        // Subject: who is trying to access (user)
        // Resource: what is being accessed (document, post, etc.)
        // Action: what operation (read, write, delete)
        // Context: environmental factors (time, location, etc.)
        
        // Rule 1: Users can read their own posts
        if (action === 'read' && resource.authorId === subject.userId) {
            return true;
        }
        
        // Rule 2: Admins can do anything
        if (subject.role === 'admin') {
            return true;
        }
        
        // Rule 3: Users can edit posts during business hours only
        if (action === 'write' && resource.authorId === subject.userId) {
            const hour = new Date().getHours();
            return hour >= 9 && hour < 17; // 9 AM - 5 PM
        }
        
        // Rule 4: Moderators can delete flagged posts
        if (action === 'delete' && subject.role === 'moderator' && resource.flagged) {
            return true;
        }
        
        // Rule 5: Premium users can access premium content
        if (resource.type === 'premium' && subject.subscription === 'premium') {
            return true;
        }
        
        return false;
    }
}

// Usage
app.put('/posts/:id', authenticate, async (req, res) => {
    const post = await Post.findById(req.params.id);
    const context = {timestamp: Date.now()};
    
    if (!AbacPolicy.canAccess(req.user, post, 'write', context)) {
        return res.status(403).json({error: 'Forbidden'});
    }
    
    // Update post
});

// === OAUTH 2.0 IMPLEMENTATION ===

// Authorization code flow
app.get('/auth/google', (req, res) => {
    const params = new URLSearchParams({
        client_id: process.env.GOOGLE_CLIENT_ID,
        redirect_uri: 'http://localhost:3000/auth/google/callback',
        response_type: 'code',
        scope: 'openid email profile',
        state: generateSecureToken() // CSRF protection
    });
    
    res.redirect(`https://accounts.google.com/o/oauth2/v2/auth?${params}`);
});

app.get('/auth/google/callback', async (req, res) => {
    const {code, state} = req.query;
    
    // Verify state (CSRF protection)
    if (!verifyState(state)) {
        return res.status(400).json({error: 'Invalid state'});
    }
    
    // Exchange code for token
    const response = await fetch('https://oauth2.googleapis.com/token', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({
            code,
            client_id: process.env.GOOGLE_CLIENT_ID,
            client_secret: process.env.GOOGLE_CLIENT_SECRET,
            redirect_uri: 'http://localhost:3000/auth/google/callback',
            grant_type: 'authorization_code'
        })
    });
    
    const tokens = await response.json();
    
    // Get user info
    const userResponse = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
        headers: {Authorization: `Bearer ${tokens.access_token}`}
    });
    
    const googleUser = await userResponse.json();
    
    // Create or update user in database
    let user = await User.findOne({googleId: googleUser.id});
    
    if (!user) {
        user = await User.create({
            googleId: googleUser.id,
            email: googleUser.email,
            name: googleUser.name,
            avatar: googleUser.picture
        });
    }
    
    // Generate JWT for our app
    const appToken = generateJWT(user);
    
    res.json({token: appToken});
});

// === MULTI-FACTOR AUTHENTICATION (MFA) ===

const speakeasy = require('speakeasy');
const QRCode = require('qrcode');

// Setup MFA
async function setupMFA(user) {
    const secret = speakeasy.generateSecret({
        name: `MyApp (${user.email})`,
        issuer: 'MyApp'
    });
    
    // Generate QR code
    const qrCodeUrl = await QRCode.toDataURL(secret.otpauth_url);
    
    // Store secret in database (encrypted!)
    await User.update(user.id, {
        mfaSecret: encrypt(secret.base32, encryptionKey),
        mfaEnabled: false // Not enabled until verified
    });
    
    return {
        secret: secret.base32,
        qrCode: qrCodeUrl
    };
}

// Verify MFA token
function verifyMFA(token, secret) {
    return speakeasy.totp.verify({
        secret: secret,
        encoding: 'base32',
        token: token,
        window: 2 // Allow 2 time steps before/after
    });
}

// Login with MFA
async function loginWithMFA(email, password, mfaToken) {
    // Verify password
    const user = await User.findOne({email});
    const passwordValid = await verifyPassword(password, user.passwordHash);
    
    if (!passwordValid) {
        throw new Error('Invalid credentials');
    }
    
    // If MFA enabled, verify token
    if (user.mfaEnabled) {
        const secret = decrypt(user.mfaSecret, encryptionKey);
        const mfaValid = verifyMFA(mfaToken, secret);
        
        if (!mfaValid) {
            throw new Error('Invalid MFA token');
        }
    }
    
    return generateJWT(user);
}

// === API KEY AUTHENTICATION ===

async function generateApiKey(userId) {
    const apiKey = `sk_${generateSecureToken(32)}`;
    const hashedKey = crypto.createHash('sha256').update(apiKey).digest('hex');
    
    await db.apiKeys.insert({
        userId,
        keyHash: hashedKey,
        createdAt: new Date(),
        lastUsed: null
    });
    
    return apiKey; // Show once, then forget
}

async function authenticateApiKey(apiKey) {
    const hashedKey = crypto.createHash('sha256').update(apiKey).digest('hex');
    const record = await db.apiKeys.findOne({keyHash: hashedKey});
    
    if (!record) {
        return null;
    }
    
    // Update last used
    await db.apiKeys.update(record.id, {lastUsed: new Date()});
    
    return await User.findById(record.userId);
}

// API key middleware
async function authenticateApi(req, res, next) {
    const apiKey = req.headers['x-api-key'];
    
    if (!apiKey) {
        return res.status(401).json({error: 'API key required'});
    }
    
    const user = await authenticateApiKey(apiKey);
    
    if (!user) {
        return res.status(401).json({error: 'Invalid API key'});
    }
    
    req.user = user;
    next();
}
Key Points: JWT for stateless auth - include expiration and issuer claims. Use refresh tokens for long-lived sessions. RBAC for simple permission models. ABAC for complex rules. OAuth 2.0 for third-party login. MFA for high-security accounts. Hash API keys before storage. Validate tokens on every request. Use short-lived access tokens (15 min) with refresh tokens (7 days). Store refresh tokens securely, support revocation. Always use HTTPS for auth.

6. Security Headers and HTTPS Integration

Essential Security Headers

Header Purpose Recommended Value
Strict-Transport-Security Force HTTPS max-age=31536000; includeSubDomains
Content-Security-Policy Prevent XSS, injection default-src 'self'; script-src 'self'
X-Frame-Options Prevent clickjacking DENY or SAMEORIGIN
X-Content-Type-Options Prevent MIME sniffing nosniff
Referrer-Policy Control referrer info strict-origin-when-cross-origin
Permissions-Policy Control browser features geolocation=(), camera=()
X-XSS-Protection XSS filter (legacy) 1; mode=block

CORS Headers

Header Purpose Example Value
Access-Control-Allow-Origin Allowed origins https://example.com
Access-Control-Allow-Methods Allowed HTTP methods GET, POST, PUT, DELETE
Access-Control-Allow-Headers Allowed request headers Content-Type, Authorization
Access-Control-Allow-Credentials Allow cookies true
Access-Control-Max-Age Preflight cache time 86400 (24 hours)

HTTPS Best Practices

Practice Description Benefit
TLS 1.3 Use latest TLS version Faster, more secure
Strong Ciphers Disable weak ciphers Prevent downgrade attacks
HSTS Force HTTPS always Prevent protocol downgrade
Certificate Pinning Pin expected certificates Prevent MITM with fake certs
OCSP Stapling Include cert status Faster cert validation

Example: Security headers and HTTPS

// === SECURITY HEADERS MIDDLEWARE ===

// Using helmet (recommended)
const helmet = require('helmet');

app.use(helmet({
    contentSecurityPolicy: {
        directives: {
            defaultSrc: ["'self'"],
            scriptSrc: ["'self'", "'unsafe-inline'", "https://cdn.example.com"],
            styleSrc: ["'self'", "'unsafe-inline'"],
            imgSrc: ["'self'", "data:", "https:"],
            connectSrc: ["'self'", "https://api.example.com"],
            fontSrc: ["'self'", "https://fonts.gstatic.com"],
            objectSrc: ["'none'"],
            mediaSrc: ["'self'"],
            frameSrc: ["'none'"]
        }
    },
    hsts: {
        maxAge: 31536000,
        includeSubDomains: true,
        preload: true
    },
    frameguard: {
        action: 'deny'
    },
    noSniff: true,
    xssFilter: true,
    referrerPolicy: {
        policy: 'strict-origin-when-cross-origin'
    },
    permissionsPolicy: {
        features: {
            geolocation: ["'none'"],
            microphone: ["'none'"],
            camera: ["'none'"],
            payment: ["'self'"]
        }
    }
}));

// Manual security headers
function securityHeaders2(req, res, next) {
    // HTTPS enforcement
    res.setHeader(
        'Strict-Transport-Security',
        'max-age=31536000; includeSubDomains; preload'
    );
    
    // XSS Protection
    res.setHeader('X-XSS-Protection', '1; mode=block');
    
    // Prevent clickjacking
    res.setHeader('X-Frame-Options', 'DENY');
    
    // Prevent MIME sniffing
    res.setHeader('X-Content-Type-Options', 'nosniff');
    
    // Referrer policy
    res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
    
    // Content Security Policy
    res.setHeader(
        'Content-Security-Policy',
        "default-src 'self'; script-src 'self' 'unsafe-inline'"
    );
    
    // Permissions Policy
    res.setHeader(
        'Permissions-Policy',
        'geolocation=(), microphone=(), camera=()'
    );
    
    next();
}

// === CORS CONFIGURATION ===

const cors = require('cors');

// Simple CORS
app.use(cors({
    origin: 'https://example.com',
    credentials: true
}));

// Advanced CORS with whitelist
const allowedOrigins = [
    'https://example.com',
    'https://app.example.com',
    'http://localhost:3000'
];

app.use(cors({
    origin: function(origin, callback) {
        // Allow requests with no origin (mobile apps, curl, etc.)
        if (!origin) return callback(null, true);
        
        if (allowedOrigins.includes(origin)) {
            callback(null, true);
        } else {
            callback(new Error('Not allowed by CORS'));
        }
    },
    credentials: true,
    methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
    allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
    exposedHeaders: ['X-Total-Count', 'X-Page-Number'],
    maxAge: 86400 // 24 hours
}));

// Manual CORS middleware
function corsMiddleware(req, res, next) {
    const origin = req.headers.origin;
    
    if (allowedOrigins.includes(origin)) {
        res.setHeader('Access-Control-Allow-Origin', origin);
        res.setHeader('Access-Control-Allow-Credentials', 'true');
    }
    
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    res.setHeader('Access-Control-Max-Age', '86400');
    
    // Handle preflight
    if (req.method === 'OPTIONS') {
        return res.status(204).end();
    }
    
    next();
}

// === HTTPS ENFORCEMENT ===

// Redirect HTTP to HTTPS
function forceHttps(req, res, next) {
    if (req.secure || req.headers['x-forwarded-proto'] === 'https') {
        return next();
    }
    
    res.redirect(301, `https://${req.hostname}${req.url}`);
}

app.use(forceHttps);

// Create HTTPS server
const https = require('https');
const fs = require('fs');

const httpsOptions = {
    key: fs.readFileSync('./certs/private-key.pem'),
    cert: fs.readFileSync('./certs/certificate.pem'),
    ca: fs.readFileSync('./certs/ca-bundle.pem'),
    
    // TLS configuration
    minVersion: 'TLSv1.3',
    ciphers: [
        'TLS_AES_128_GCM_SHA256',
        'TLS_AES_256_GCM_SHA384',
        'TLS_CHACHA20_POLY1305_SHA256'
    ].join(':'),
    
    // Prefer server cipher order
    honorCipherOrder: true,
    
    // Enable OCSP stapling
    // requestOCSP: true
};

https.createServer(httpsOptions, app).listen(443, () => {
    console.log('HTTPS server running on port 443');
});

// === SUBRESOURCE INTEGRITY (SRI) ===

// Generate SRI hash
function generateSriHash(content) {
    const hash = crypto.createHash('sha384').update(content).digest('base64');
    return `sha384-${hash}`;
}

// Usage in HTML
// <script 
//   src="https://cdn.example.com/library.js"
//   integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
//   crossorigin="anonymous"
// ></script>

// === CERTIFICATE PINNING (Mobile/Native Apps) ===

// HTTP Public Key Pinning header (deprecated, use cert pinning in app)
// res.setHeader(
//     'Public-Key-Pins',
//     'pin-sha256="base64=="; max-age=5184000; includeSubDomains'
// );

// Certificate pinning in Node.js
const tls = require('tls');

const pinnedFingerprint = 'AA:BB:CC:DD:EE:FF:...';

function checkCertificate(socket) {
    const cert = socket.getPeerCertificate();
    const fingerprint = cert.fingerprint256;
    
    if (fingerprint !== pinnedFingerprint) {
        throw new Error('Certificate pinning failed');
    }
}

// === RATE LIMITING WITH HEADERS ===

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100, // Limit each IP to 100 requests per windowMs
    standardHeaders: true, // Return rate limit info in headers
    legacyHeaders: false,
    handler: (req, res) => {
        res.status(429).json({
            error: 'Too many requests',
            retryAfter: req.rateLimit.resetTime
        });
    }
});

app.use('/api/', limiter);

// Custom rate limit headers
function rateLimitHeaders(req, res, next) {
    res.setHeader('X-RateLimit-Limit', '100');
    res.setHeader('X-RateLimit-Remaining', '95');
    res.setHeader('X-RateLimit-Reset', Math.floor(Date.now() / 1000) + 900);
    next();
}

// === SECURE COOKIES ===

// Set secure cookie
function setSecureCookie(res, name, value) {
    res.cookie(name, value, {
        httpOnly: true,      // No JavaScript access
        secure: true,        // HTTPS only
        sameSite: 'strict',  // CSRF protection
        maxAge: 3600000,     // 1 hour
        domain: '.example.com',
        path: '/',
        signed: true         // Sign cookie with secret
    });
}

// === SECURITY.TXT FILE ===

// Serve security.txt for responsible disclosure
app.get('/.well-known/security.txt', (req, res) => {
    res.type('text/plain');
    res.send(`
Contact: security@example.com
Expires: 2026-12-31T23:59:59.000Z
Preferred-Languages: en
Canonical: https://example.com/.well-known/security.txt
Policy: https://example.com/security-policy
Acknowledgments: https://example.com/hall-of-fame
    `.trim());
});

// === SECURITY MONITORING ===

// Log security events
function logSecurityEvent(type, details) {
    console.log(JSON.stringify({
        timestamp: new Date().toISOString(),
        type,
        details,
        level: 'security'
    }));
}

// Monitor suspicious activity
function detectSuspiciousActivity(req) {
    // Check for SQL injection patterns
    const sqlInjectionPattern = /(\bunion\b|\bselect\b|\bdrop\b|\binsert\b)/i;
    if (sqlInjectionPattern.test(req.url) || 
        sqlInjectionPattern.test(JSON.stringify(req.body))) {
        logSecurityEvent('SQL_INJECTION_ATTEMPT', {
            ip: req.ip,
            url: req.url,
            body: req.body
        });
    }
    
    // Check for XSS patterns
    const xssPattern = /(<script|javascript:|onerror=|onload=)/i;
    if (xssPattern.test(JSON.stringify(req.body))) {
        logSecurityEvent('XSS_ATTEMPT', {
            ip: req.ip,
            url: req.url
        });
    }
    
    // Check for path traversal
    if (req.url.includes('../') || req.url.includes('..\\')) {
        logSecurityEvent('PATH_TRAVERSAL_ATTEMPT', {
            ip: req.ip,
            url: req.url
        });
    }
}

app.use((req, res, next) => {
    detectSuspiciousActivity(req);
    next();
});
Key Points: Use helmet.js for comprehensive security headers. HSTS forces HTTPS. CSP prevents XSS and injection. X-Frame-Options prevents clickjacking. Configure CORS properly - whitelist origins. Use TLS 1.3 with strong ciphers. Set secure cookie flags (httpOnly, secure, sameSite). Implement rate limiting. Use SRI for CDN resources. Certificate pinning for critical apps. Monitor security events. Serve security.txt for disclosure. HTTPS everywhere - no exceptions.

Section 25 Summary: Security and Best Practices

  • Input Validation: Validate all input server-side, whitelist over blacklist, prevent SQL/command/XSS injection
  • Sanitization: Escape HTML entities, use parameterized queries, sanitize filenames, implement rate limiting
  • XSS Prevention: Never use innerHTML with user input, avoid eval(), implement CSP headers, use DOMPurify
  • CSP Directives: default-src, script-src (use nonces), style-src, img-src, connect-src, frame-ancestors
  • Secure Coding: Never hardcode secrets, follow OWASP Top 10, defense in depth, least privilege principle
  • Code Review: Check auth/authz, validate crypto usage, prevent sensitive data in errors/logs
  • Password Hashing: Use bcrypt/Argon2 with salt, never plain MD5/SHA, use key derivation (PBKDF2)
  • Encryption: AES-256-GCM for symmetric, RSA for asymmetric, random IV per encryption, HMAC for auth
  • Authentication: JWT for stateless auth, refresh tokens for sessions, OAuth 2.0 for third-party, MFA for high security
  • Authorization: RBAC for simple models, ABAC for complex rules, validate permissions on every request
  • Security Headers: HSTS forces HTTPS, CSP prevents XSS, X-Frame-Options prevents clickjacking, use helmet.js
  • HTTPS: Use TLS 1.3, strong ciphers, secure cookies (httpOnly, secure, sameSite), CORS whitelist