# Security Standards Document

## Security Checklist

- [ ] Passwords hashed with bcrypt 12+ rounds ([Password Security](#password-requirements))
- [ ] MFA implemented for sensitive operations ([Multi-Factor Authentication](#multi-factor-authentication-mfa))
- [ ] Sessions expire and rotate appropriately ([Session Management](#session-management))
- [ ] JWT tokens properly configured ([Token Security](#jwt-security))
- [ ] RBAC implemented with least privilege ([Authorization](#authorization-standards))
- [ ] Permission boundaries enforced ([Permission Management](#permission-management))
- [ ] All input validated and sanitized ([Input Validation](#input-validation))
- [ ] SQL injection prevention implemented ([SQL Injection Prevention](#sql-injection-prevention))
- [ ] XSS prevention measures in place ([XSS Prevention](#xss-prevention))
- [ ] CSRF tokens on state-changing operations ([CSRF Protection](#csrf-protection))
- [ ] Security headers configured ([Security Headers](#security-headers))
- [ ] HTTPS enforced everywhere ([TLS Configuration](#tls-configuration))
- [ ] Sensitive data encrypted at rest ([Cryptography Standards](#cryptography-standards))
- [ ] PII properly protected and masked ([Sensitive Data Handling](#sensitive-data-handling))
- [ ] API keys securely generated and stored ([API Key Security](#api-key-security))
- [ ] Rate limiting implemented ([Rate Limiting](#rate-limiting-security))
- [ ] Security monitoring active ([Security Monitoring](#security-monitoring))
- [ ] Incident response plan documented ([Incident Response](#incident-response))
- [ ] Dependencies regularly updated ([Dependency Management](#dependency-security))
- [ ] Security training completed by team ([Security Culture](#security-culture))

## Core Philosophy

Security is not a feature, it's a foundation. Every line of code is a potential vulnerability. Build security in from the start, assume breach, verify everything, trust nothing. Security is everyone's responsibility, not just the security team's.

## Authentication Standards

### Password Requirements
Implement secure password policies that balance security with usability.

**Why:** Weak passwords are the most common entry point for attackers. Strong password policies prevent credential stuffing, brute force attacks, and account takeovers while remaining user-friendly.

```typescript
// Password validation rules
interface PasswordPolicy {
  minLength: 12,                    // NIST recommends 8+, we go higher
  maxLength: 128,                   // Prevent DoS from bcrypt
  requireUppercase: false,          // NIST: Don't require
  requireLowercase: false,          // NIST: Don't require  
  requireNumbers: false,            // NIST: Don't require
  requireSpecialChars: false,       // NIST: Don't require
  preventCommonPasswords: true,     // Check against breach lists
  preventUserInfo: true,            // No email/name in password
  preventSequential: true,          // No "12345" or "abcde"
  preventRepeating: true            // No "aaaa" or "1111"
}

// Password strength validation
async function validatePassword(
  password: string, 
  userContext?: UserContext
): Promise<ValidationResult> {
  const errors: string[] = [];
  
  // Length check
  if (password.length < 12) {
    errors.push('Password must be at least 12 characters');
  }
  
  if (password.length > 128) {
    errors.push('Password too long');
  }
  
  // Check against breach database (HaveIBeenPwned)
  const sha1 = crypto.createHash('sha1').update(password).digest('hex');
  const prefix = sha1.substring(0, 5);
  const suffix = sha1.substring(5).toUpperCase();
  
  const response = await fetch(`https://api.pwnedpasswords.com/range/${prefix}`);
  const hashes = await response.text();
  
  if (hashes.includes(suffix)) {
    errors.push('This password has been found in data breaches');
  }
  
  // Check for user information
  if (userContext) {
    const lowerPassword = password.toLowerCase();
    const email = userContext.email.toLowerCase();
    const name = userContext.name?.toLowerCase();
    
    if (lowerPassword.includes(email.split('@')[0])) {
      errors.push('Password cannot contain your email');
    }
    
    if (name && lowerPassword.includes(name)) {
      errors.push('Password cannot contain your name');
    }
  }
  
  // Check for patterns
  if (/(.)\1{3,}/.test(password)) {
    errors.push('Password cannot contain repeating characters');
  }
  
  if (/012|123|234|345|456|567|678|789|890/.test(password)) {
    errors.push('Password cannot contain sequential numbers');
  }
  
  if (/abc|bcd|cde|def|efg|fgh|ghi|hij|ijk|jkl|klm|lmn|mno|nop|opq|pqr|qrs|rst|stu|tuv|uvw|vwx|wxy|xyz/i.test(password)) {
    errors.push('Password cannot contain sequential letters');
  }
  
  return {
    valid: errors.length === 0,
    errors,
    strength: calculatePasswordStrength(password)
  };
}

// Password hashing with bcrypt
async function hashPassword(password: string): Promise<string> {
  const saltRounds = 12;  // Adjust based on performance needs
  return bcrypt.hash(password, saltRounds);
}

// Store password history to prevent reuse
CREATE TABLE password_history (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    password_hash VARCHAR(255) NOT NULL,
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    
    INDEX idx_password_history_user_id (user_id, created_at DESC)
);

// Check last 5 passwords
async function checkPasswordReuse(userId: string, newPassword: string): Promise<boolean> {
  const history = await db.query(
    'SELECT password_hash FROM password_history WHERE user_id = $1 ORDER BY created_at DESC LIMIT 5',
    [userId]
  );
  
  for (const record of history.rows) {
    if (await bcrypt.compare(newPassword, record.password_hash)) {
      return true;  // Password was recently used
    }
  }
  
  return false;
}
```

### Multi-Factor Authentication (MFA)
Implement robust MFA to add an extra security layer.

**Why:** MFA prevents account takeover even if passwords are compromised. It's the single most effective security control, reducing account compromise by 99.9% according to Microsoft.

```typescript
// TOTP (Time-based One-Time Password) implementation
import * as speakeasy from 'speakeasy';
import * as QRCode from 'qrcode';

// Generate TOTP secret for user
async function setupTOTP(userId: string): Promise<TOTPSetup> {
  const secret = speakeasy.generateSecret({
    length: 32,
    name: `MyApp (${user.email})`,
    issuer: 'MyApp'
  });
  
  // Store encrypted secret
  await db.query(
    `UPDATE users 
     SET totp_secret = $1, 
         totp_enabled = false 
     WHERE id = $2`,
    [encrypt(secret.base32), userId]
  );
  
  // Generate QR code
  const qrCodeUrl = await QRCode.toDataURL(secret.otpauth_url);
  
  return {
    secret: secret.base32,
    qrCode: qrCodeUrl,
    backupCodes: await generateBackupCodes(userId)
  };
}

// Verify TOTP code
async function verifyTOTP(userId: string, token: string): Promise<boolean> {
  const user = await db.query(
    'SELECT totp_secret FROM users WHERE id = $1',
    [userId]
  );
  
  const secret = decrypt(user.rows[0].totp_secret);
  
  // Verify with time window for clock skew
  const verified = speakeasy.totp.verify({
    secret,
    encoding: 'base32',
    token,
    window: 2  // Allow 2 intervals before/after
  });
  
  if (verified) {
    // Prevent replay attacks
    const recentToken = await redis.get(`totp:used:${userId}:${token}`);
    if (recentToken) {
      return false;  // Token already used
    }
    
    // Mark token as used (90 second TTL)
    await redis.setex(`totp:used:${userId}:${token}`, 90, '1');
    
    return true;
  }
  
  return false;
}

// Backup codes for account recovery
CREATE TABLE backup_codes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    code_hash VARCHAR(255) NOT NULL,
    used_at TIMESTAMPTZ,
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    
    INDEX idx_backup_codes_user_id (user_id)
);

async function generateBackupCodes(userId: string, count = 10): Promise<string[]> {
  const codes: string[] = [];
  
  for (let i = 0; i < count; i++) {
    const code = crypto.randomBytes(4).toString('hex').toUpperCase();
    codes.push(code);
    
    await db.query(
      'INSERT INTO backup_codes (user_id, code_hash) VALUES ($1, $2)',
      [userId, await hashPassword(code)]
    );
  }
  
  return codes;
}

// WebAuthn/Passkeys for passwordless authentication
async function registerWebAuthn(userId: string): Promise<PublicKeyCredentialCreationOptions> {
  const user = await getUserById(userId);
  
  const challenge = crypto.randomBytes(32);
  
  const options: PublicKeyCredentialCreationOptions = {
    challenge,
    rp: {
      name: 'MyApp',
      id: 'myapp.com',
    },
    user: {
      id: Buffer.from(userId),
      name: user.email,
      displayName: user.name
    },
    pubKeyCredParams: [
      { alg: -7, type: 'public-key' },   // ES256
      { alg: -257, type: 'public-key' }  // RS256
    ],
    authenticatorSelection: {
      authenticatorAttachment: 'platform',
      userVerification: 'preferred'
    },
    timeout: 60000,
    attestation: 'none'
  };
  
  // Store challenge for verification
  await redis.setex(`webauthn:challenge:${userId}`, 300, challenge.toString('base64'));
  
  return options;
}
```

### Session Management
Implement secure session handling.

**Why:** Poor session management leads to session hijacking, fixation attacks, and unauthorized access. Proper implementation ensures users stay secure throughout their interaction with your application.

```typescript
// Session configuration
interface SessionConfig {
  secret: process.env.SESSION_SECRET,  // Strong random secret
  name: 'sessionId',                   // Don't use default names
  cookie: {
    httpOnly: true,                    // Prevent XSS access
    secure: true,                      // HTTPS only
    sameSite: 'strict',               // CSRF protection
    maxAge: 1000 * 60 * 60 * 2,      // 2 hours
    domain: '.myapp.com'              // Scope to your domain
  },
  rolling: true,                       // Reset expiry on activity
  saveUninitialized: false,
  resave: false
}

// Session storage schema
CREATE TABLE sessions (
    sid VARCHAR(255) PRIMARY KEY,
    sess JSONB NOT NULL,
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    ip_address INET,
    user_agent TEXT,
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    expires_at TIMESTAMPTZ NOT NULL,
    
    INDEX idx_sessions_user_id (user_id),
    INDEX idx_sessions_expires_at (expires_at)
);

// Session invalidation on security events
async function invalidateUserSessions(
  userId: string, 
  reason: string,
  exceptSessionId?: string
): Promise<void> {
  // Log the event
  await auditLog('sessions_invalidated', {
    userId,
    reason,
    exceptSessionId
  });
  
  // Delete all sessions except current (if specified)
  const query = exceptSessionId
    ? 'DELETE FROM sessions WHERE user_id = $1 AND sid != $2'
    : 'DELETE FROM sessions WHERE user_id = $1';
    
  await db.query(query, exceptSessionId ? [userId, exceptSessionId] : [userId]);
  
  // Clear any cached sessions
  await redis.del(`sessions:${userId}:*`);
}

// Concurrent session limiting
async function enforceSessionLimit(userId: string, limit = 3): Promise<void> {
  const sessions = await db.query(
    `SELECT sid, created_at 
     FROM sessions 
     WHERE user_id = $1 
     ORDER BY created_at DESC`,
    [userId]
  );
  
  if (sessions.rows.length > limit) {
    // Remove oldest sessions
    const toRemove = sessions.rows.slice(limit);
    for (const session of toRemove) {
      await db.query('DELETE FROM sessions WHERE sid = $1', [session.sid]);
    }
  }
}

// Session fingerprinting for anomaly detection
function generateSessionFingerprint(req: Request): string {
  const components = [
    req.headers['user-agent'],
    req.headers['accept-language'],
    req.headers['accept-encoding'],
    req.connection.remoteAddress
  ];
  
  return crypto
    .createHash('sha256')
    .update(components.join('|'))
    .digest('hex');
}

// Detect session anomalies
async function detectSessionAnomaly(
  sessionId: string,
  currentFingerprint: string
): Promise<boolean> {
  const session = await db.query(
    'SELECT fingerprint FROM sessions WHERE sid = $1',
    [sessionId]
  );
  
  if (session.rows[0].fingerprint !== currentFingerprint) {
    await auditLog('session_anomaly_detected', {
      sessionId,
      originalFingerprint: session.rows[0].fingerprint,
      currentFingerprint
    });
    
    return true;
  }
  
  return false;
}
```

## Authorization Standards

### Role-Based Access Control (RBAC)
Implement fine-grained access control.

**Why:** RBAC ensures users can only access resources they're authorized for, implements the principle of least privilege, and makes permission management scalable and auditable.

```typescript
// RBAC database schema
CREATE TABLE roles (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(50) UNIQUE NOT NULL,
    description TEXT,
    is_system BOOLEAN DEFAULT false,  // Prevent deletion of system roles
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE permissions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    resource VARCHAR(100) NOT NULL,   -- 'users', 'posts', 'admin'
    action VARCHAR(50) NOT NULL,      -- 'read', 'write', 'delete'
    conditions JSONB,                  -- Additional conditions
    description TEXT,
    
    UNIQUE(resource, action)
);

CREATE TABLE role_permissions (
    role_id UUID REFERENCES roles(id) ON DELETE CASCADE,
    permission_id UUID REFERENCES permissions(id) ON DELETE CASCADE,
    granted_by UUID REFERENCES users(id),
    granted_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    
    PRIMARY KEY (role_id, permission_id)
);

CREATE TABLE user_roles (
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    role_id UUID REFERENCES roles(id) ON DELETE CASCADE,
    granted_by UUID REFERENCES users(id),
    granted_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    expires_at TIMESTAMPTZ,  -- For temporary roles
    
    PRIMARY KEY (user_id, role_id)
);

// Permission checking middleware
async function requirePermission(resource: string, action: string) {
  return async (req: Request, res: Response, next: Next) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Authentication required' });
    }
    
    const hasPermission = await checkPermission(
      req.user.id,
      resource,
      action,
      req.params  // Resource context
    );
    
    if (!hasPermission) {
      await auditLog('permission_denied', {
        userId: req.user.id,
        resource,
        action,
        context: req.params
      });
      
      return res.status(403).json({ error: 'Permission denied' });
    }
    
    next();
  };
}

// Hierarchical permission checking
async function checkPermission(
  userId: string,
  resource: string,
  action: string,
  context?: any
): Promise<boolean> {
  // Check direct permissions
  const result = await db.query(`
    SELECT p.*, rp.role_id, ur.expires_at
    FROM permissions p
    JOIN role_permissions rp ON p.id = rp.permission_id
    JOIN user_roles ur ON rp.role_id = ur.role_id
    WHERE ur.user_id = $1
      AND p.resource = $2
      AND p.action = $3
      AND (ur.expires_at IS NULL OR ur.expires_at > NOW())
  `, [userId, resource, action]);
  
  if (result.rows.length === 0) {
    return false;
  }
  
  // Evaluate conditions if present
  for (const permission of result.rows) {
    if (permission.conditions) {
      if (await evaluateConditions(permission.conditions, context)) {
        return true;
      }
    } else {
      return true;  // No conditions means unrestricted
    }
  }
  
  return false;
}

// Resource ownership checking
async function checkResourceOwnership(
  userId: string,
  resourceType: string,
  resourceId: string
): Promise<boolean> {
  const ownershipChecks: Record<string, string> = {
    'post': 'SELECT 1 FROM posts WHERE id = $1 AND author_id = $2',
    'comment': 'SELECT 1 FROM comments WHERE id = $1 AND user_id = $2',
    'file': 'SELECT 1 FROM user_files WHERE id = $1 AND user_id = $2'
  };
  
  const query = ownershipChecks[resourceType];
  if (!query) return false;
  
  const result = await db.query(query, [resourceId, userId]);
  return result.rows.length > 0;
}
```

### API Key Management
Secure API key generation and management.

**Why:** API keys enable service-to-service authentication and third-party integrations. Proper management prevents key leakage and enables granular access control for external systems.

```typescript
// API key schema
CREATE TABLE api_keys (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    key_hash VARCHAR(255) NOT NULL,
    key_prefix VARCHAR(10) NOT NULL,  -- First 8 chars for identification
    name VARCHAR(100) NOT NULL,
    permissions JSONB,                 -- Scoped permissions
    rate_limit INTEGER DEFAULT 1000,  -- Requests per hour
    
    last_used_at TIMESTAMPTZ,
    last_used_ip INET,
    usage_count INTEGER DEFAULT 0,
    
    expires_at TIMESTAMPTZ,
    revoked_at TIMESTAMPTZ,
    revoked_reason TEXT,
    
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    
    INDEX idx_api_keys_key_prefix (key_prefix),
    INDEX idx_api_keys_user_id (user_id)
);

// Generate secure API key
async function generateApiKey(
  userId: string,
  name: string,
  permissions: string[]
): Promise<ApiKeyResult> {
  // Generate cryptographically secure key
  const keyBytes = crypto.randomBytes(32);
  const apiKey = `sk_live_${keyBytes.toString('base64url')}`;
  const keyHash = await hashPassword(apiKey);
  const keyPrefix = apiKey.substring(0, 8);
  
  // Store key metadata
  const result = await db.query(
    `INSERT INTO api_keys 
     (user_id, key_hash, key_prefix, name, permissions, expires_at)
     VALUES ($1, $2, $3, $4, $5, $6)
     RETURNING id`,
    [userId, keyHash, keyPrefix, name, permissions, 
     new Date(Date.now() + 365 * 24 * 60 * 60 * 1000)]  // 1 year
  );
  
  await auditLog('api_key_created', {
    userId,
    keyId: result.rows[0].id,
    name,
    permissions
  });
  
  // Return key only once
  return {
    id: result.rows[0].id,
    key: apiKey,  // Never stored, only returned once
    prefix: keyPrefix,
    message: 'Store this key securely. It will not be shown again.'
  };
}

// Validate API key with rate limiting
async function validateApiKey(apiKey: string): Promise<ApiKeyValidation> {
  const keyPrefix = apiKey.substring(0, 8);
  
  // Find potential keys by prefix
  const candidates = await db.query(
    `SELECT * FROM api_keys 
     WHERE key_prefix = $1 
     AND revoked_at IS NULL 
     AND (expires_at IS NULL OR expires_at > NOW())`,
    [keyPrefix]
  );
  
  for (const candidate of candidates.rows) {
    if (await bcrypt.compare(apiKey, candidate.key_hash)) {
      // Check rate limit
      const rateLimitKey = `rate_limit:${candidate.id}`;
      const usage = await redis.incr(rateLimitKey);
      
      if (usage === 1) {
        await redis.expire(rateLimitKey, 3600);  // 1 hour window
      }
      
      if (usage > candidate.rate_limit) {
        return { valid: false, error: 'Rate limit exceeded' };
      }
      
      // Update usage stats
      await db.query(
        `UPDATE api_keys 
         SET last_used_at = NOW(), 
             last_used_ip = $1,
             usage_count = usage_count + 1 
         WHERE id = $2`,
        [req.ip, candidate.id]
      );
      
      return {
        valid: true,
        userId: candidate.user_id,
        permissions: candidate.permissions,
        remaining: candidate.rate_limit - usage
      };
    }
  }
  
  return { valid: false, error: 'Invalid API key' };
}
```

## Input Validation

### Input Sanitization Rules
Validate and sanitize all user input.

**Why:** Input validation is the first line of defense against injection attacks, XSS, and data corruption. Never trust user input - validate everything at the boundary.

```typescript
// Comprehensive input validation schema
import { z } from 'zod';

// Email validation with DNS check
const emailSchema = z.string()
  .email('Invalid email format')
  .max(255, 'Email too long')
  .toLowerCase()
  .refine(async (email) => {
    // Check for disposable email providers
    const domain = email.split('@')[1];
    const disposable = await checkDisposableEmail(domain);
    return !disposable;
  }, 'Disposable email addresses not allowed')
  .refine(async (email) => {
    // DNS MX record check
    const domain = email.split('@')[1];
    return await checkMxRecord(domain);
  }, 'Email domain does not exist');

// User registration schema
const userRegistrationSchema = z.object({
  email: emailSchema,
  password: z.string().min(12).max(128),
  name: z.string()
    .min(1, 'Name required')
    .max(100, 'Name too long')
    .regex(/^[a-zA-Z\s\-']+$/, 'Invalid characters in name')
    .transform(val => val.trim()),
  age: z.number()
    .int('Age must be whole number')
    .min(13, 'Must be at least 13')
    .max(150, 'Invalid age'),
  username: z.string()
    .min(3, 'Username too short')
    .max(30, 'Username too long')
    .regex(/^[a-zA-Z0-9_-]+$/, 'Username can only contain letters, numbers, underscore, and dash')
    .refine(async (username) => {
      const exists = await checkUsernameExists(username);
      return !exists;
    }, 'Username already taken'),
  profileUrl: z.string()
    .url('Invalid URL')
    .startsWith('https://', 'URL must use HTTPS')
    .optional(),
  bio: z.string()
    .max(500, 'Bio too long')
    .transform(val => sanitizeHtml(val, { allowedTags: [] }))  // Strip all HTML
    .optional()
});

// File upload validation
const fileUploadSchema = z.object({
  filename: z.string()
    .max(255)
    .regex(/^[a-zA-Z0-9_\-. ]+$/, 'Invalid filename')
    .refine(name => {
      const ext = path.extname(name).toLowerCase();
      const allowed = ['.jpg', '.jpeg', '.png', '.gif', '.pdf', '.doc', '.docx'];
      return allowed.includes(ext);
    }, 'File type not allowed'),
  size: z.number()
    .max(10 * 1024 * 1024, 'File too large (max 10MB)'),
  mimeType: z.enum([
    'image/jpeg',
    'image/png',
    'image/gif',
    'application/pdf',
    'application/msword',
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
  ])
});

// SQL injection prevention
function sanitizeSqlIdentifier(identifier: string): string {
  // Only allow alphanumeric and underscore
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(identifier)) {
    throw new Error('Invalid SQL identifier');
  }
  return identifier;
}

// NoSQL injection prevention (MongoDB)
function sanitizeMongoQuery(query: any): any {
  // Remove dangerous operators
  const dangerous = ['$where', '$regex', '$ne', '$nin', '$exists'];
  
  const clean = (obj: any): any => {
    if (typeof obj !== 'object' || obj === null) return obj;
    
    const cleaned: any = Array.isArray(obj) ? [] : {};
    
    for (const key in obj) {
      if (dangerous.includes(key)) {
        throw new Error('Dangerous operator in query');
      }
      cleaned[key] = clean(obj[key]);
    }
    
    return cleaned;
  };
  
  return clean(query);
}

// XSS prevention with Content Security Policy
const cspDirectives = {
  defaultSrc: ["'self'"],
  scriptSrc: ["'self'", "'unsafe-inline'", 'https://cdn.trusted.com'],
  styleSrc: ["'self'", "'unsafe-inline'"],
  imgSrc: ["'self'", 'data:', 'https:'],
  connectSrc: ["'self'"],
  fontSrc: ["'self'"],
  objectSrc: ["'none'"],
  mediaSrc: ["'self'"],
  frameSrc: ["'none'"],
  sandbox: ['allow-forms', 'allow-scripts', 'allow-same-origin'],
  reportUri: '/api/csp-report',
  upgradeInsecureRequests: []
};

// HTML sanitization for user content
import DOMPurify from 'isomorphic-dompurify';

function sanitizeUserHtml(dirty: string): string {
  return DOMPurify.sanitize(dirty, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
    ALLOWED_ATTR: ['href', 'target'],
    ALLOW_DATA_ATTR: false,
    RETURN_TRUSTED_TYPE: true
  });
}
```

### Request Validation Middleware
Validate requests at the API boundary.

**Why:** Centralizing validation in middleware ensures consistent validation across all endpoints, provides clear error messages, and prevents invalid data from reaching business logic.

```typescript
// Generic validation middleware
function validate(schema: z.ZodSchema) {
  return async (req: Request, res: Response, next: Next) => {
    try {
      const validated = await schema.parseAsync(req.body);
      req.body = validated;  // Replace with validated/transformed data
      next();
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(422).json({
          success: false,
          error: {
            code: 'VALIDATION_ERROR',
            message: 'Invalid input',
            details: error.errors.map(e => ({
              field: e.path.join('.'),
              message: e.message
            }))
          }
        });
      }
      next(error);
    }
  };
}

// Request size limits
app.use(express.json({ limit: '1mb' }));
app.use(express.urlencoded({ extended: true, limit: '1mb' }));

// Parameter pollution prevention
app.use((req: Request, res: Response, next: Next) => {
  // Ensure query params are not arrays unless expected
  for (const [key, value] of Object.entries(req.query)) {
    if (Array.isArray(value) && !ARRAY_PARAMS.includes(key)) {
      return res.status(400).json({
        error: 'Duplicate query parameters not allowed'
      });
    }
  }
  next();
});

// Rate limiting by endpoint
const rateLimits = {
  login: rateLimit({
    windowMs: 15 * 60 * 1000,  // 15 minutes
    max: 5,                     // 5 attempts
    message: 'Too many login attempts',
    standardHeaders: true,
    legacyHeaders: false
  }),
  api: rateLimit({
    windowMs: 15 * 60 * 1000,
    max: 100,
    message: 'Too many requests'
  }),
  upload: rateLimit({
    windowMs: 60 * 60 * 1000,  // 1 hour
    max: 10,                    // 10 uploads per hour
    message: 'Upload limit exceeded'
  })
};

// Apply rate limits
app.use('/api/auth/login', rateLimits.login);
app.use('/api/upload', rateLimits.upload);
app.use('/api', rateLimits.api);
```

## Cryptography Standards

### Encryption at Rest
Protect sensitive data in storage.

**Why:** Encryption at rest protects against data breaches from stolen databases, backups, or hardware. It's often legally required for sensitive data and provides defense in depth.

```typescript
// Field-level encryption for sensitive data
import { createCipheriv, createDecipheriv, randomBytes, scrypt } from 'crypto';

class FieldEncryption {
  private algorithm = 'aes-256-gcm';
  private keyDerivation = 'scrypt';
  
  async deriveKey(password: string, salt: Buffer): Promise<Buffer> {
    return new Promise((resolve, reject) => {
      scrypt(password, salt, 32, (err, derivedKey) => {
        if (err) reject(err);
        else resolve(derivedKey);
      });
    });
  }
  
  async encrypt(text: string, masterKey: string): Promise<EncryptedData> {
    const salt = randomBytes(16);
    const iv = randomBytes(16);
    const key = await this.deriveKey(masterKey, salt);
    
    const cipher = createCipheriv(this.algorithm, key, iv);
    
    let encrypted = cipher.update(text, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    
    const authTag = cipher.getAuthTag();
    
    return {
      encrypted,
      salt: salt.toString('hex'),
      iv: iv.toString('hex'),
      authTag: authTag.toString('hex'),
      algorithm: this.algorithm
    };
  }
  
  async decrypt(data: EncryptedData, masterKey: string): Promise<string> {
    const salt = Buffer.from(data.salt, 'hex');
    const iv = Buffer.from(data.iv, 'hex');
    const authTag = Buffer.from(data.authTag, 'hex');
    const key = await this.deriveKey(masterKey, salt);
    
    const decipher = createDecipheriv(this.algorithm, key, iv);
    decipher.setAuthTag(authTag);
    
    let decrypted = decipher.update(data.encrypted, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    
    return decrypted;
  }
}

// Database encryption with envelope encryption
CREATE TABLE encrypted_data (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID REFERENCES users(id),
    data_key_encrypted BYTEA NOT NULL,    -- DEK encrypted with KEK
    data_encrypted BYTEA NOT NULL,        -- Data encrypted with DEK
    iv BYTEA NOT NULL,
    auth_tag BYTEA NOT NULL,
    key_version INTEGER DEFAULT 1,        -- For key rotation
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

// Key rotation implementation
async function rotateEncryptionKeys(): Promise<void> {
  const oldKeyVersion = 1;
  const newKeyVersion = 2;
  
  // Get all records with old key version
  const records = await db.query(
    'SELECT * FROM encrypted_data WHERE key_version = $1',
    [oldKeyVersion]
  );
  
  for (const record of records.rows) {
    // Decrypt with old key
    const decrypted = await decrypt(record, oldMasterKey);
    
    // Re-encrypt with new key
    const encrypted = await encrypt(decrypted, newMasterKey);
    
    // Update record
    await db.query(
      `UPDATE encrypted_data 
       SET data_encrypted = $1, 
           iv = $2, 
           auth_tag = $3, 
           key_version = $4 
       WHERE id = $5`,
      [encrypted.data, encrypted.iv, encrypted.authTag, newKeyVersion, record.id]
    );
  }
}

// Transparent database encryption (PostgreSQL)
-- Enable encryption for specific columns
CREATE EXTENSION IF NOT EXISTS pgcrypto;

-- Encrypt on insert/update
CREATE OR REPLACE FUNCTION encrypt_sensitive_data()
RETURNS TRIGGER AS $$
BEGIN
    NEW.ssn = pgp_sym_encrypt(NEW.ssn, current_setting('app.encryption_key'));
    NEW.credit_card = pgp_sym_encrypt(NEW.credit_card, current_setting('app.encryption_key'));
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER encrypt_user_data
BEFORE INSERT OR UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION encrypt_sensitive_data();
```

### Encryption in Transit
Secure data during transmission.

**Why:** Encryption in transit prevents man-in-the-middle attacks, eavesdropping, and data tampering. It's required for any sensitive data transmission and builds user trust.

```typescript
// TLS configuration
const tlsOptions = {
  // Use TLS 1.2 minimum
  minVersion: 'TLSv1.2',
  
  // Strong cipher suites only
  ciphers: [
    'ECDHE-RSA-AES128-GCM-SHA256',
    'ECDHE-RSA-AES256-GCM-SHA384',
    'ECDHE-RSA-AES128-SHA256',
    'ECDHE-RSA-AES256-SHA384'
  ].join(':'),
  
  // Prefer server cipher order
  honorCipherOrder: true,
  
  // Certificate configuration
  key: fs.readFileSync('./certs/private-key.pem'),
  cert: fs.readFileSync('./certs/certificate.pem'),
  ca: fs.readFileSync('./certs/ca-certificate.pem'),
  
  // Reject unauthorized connections
  rejectUnauthorized: true
};

// HTTPS server
https.createServer(tlsOptions, app).listen(443);

// Force HTTPS redirect
app.use((req, res, next) => {
  if (req.header('x-forwarded-proto') !== 'https') {
    return res.redirect(`https://${req.header('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();
});

// Certificate pinning for API clients
async function validateCertificatePin(cert: string): Promise<boolean> {
  const pin = crypto
    .createHash('sha256')
    .update(cert)
    .digest('base64');
    
  const validPins = [
    process.env.CERT_PIN_PRIMARY,
    process.env.CERT_PIN_BACKUP
  ];
  
  return validPins.includes(pin);
}

// End-to-end encryption for sensitive operations
async function encryptSensitivePayload(
  data: any,
  recipientPublicKey: string
): Promise<string> {
  // Generate ephemeral key pair
  const { publicKey, privateKey } = await generateKeyPair('rsa', {
    modulusLength: 2048
  });
  
  // Encrypt data with AES
  const aesKey = randomBytes(32);
  const iv = randomBytes(16);
  const cipher = createCipheriv('aes-256-cbc', aesKey, iv);
  
  let encrypted = cipher.update(JSON.stringify(data), 'utf8', 'hex');
  encrypted += cipher.final('hex');
  
  // Encrypt AES key with recipient's public key
  const encryptedKey = crypto.publicEncrypt(
    recipientPublicKey,
    aesKey
  );
  
  return JSON.stringify({
    data: encrypted,
    key: encryptedKey.toString('base64'),
    iv: iv.toString('base64')
  });
}
```

## Security Headers

### HTTP Security Headers
Configure security headers for defense in depth.

**Why:** Security headers provide additional layers of protection against common attacks like XSS, clickjacking, and data injection. They're easy to implement and significantly improve security posture.

```typescript
import helmet from 'helmet';

// Comprehensive security headers
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'", 'https://cdn.trusted.com'],
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", 'data:', 'https:'],
      connectSrc: ["'self'"],
      fontSrc: ["'self'"],
      objectSrc: ["'none'"],
      mediaSrc: ["'self'"],
      frameSrc: ["'none'"],
      sandbox: ['allow-forms', 'allow-scripts', 'allow-same-origin'],
      reportUri: '/api/security/csp-report',
      upgradeInsecureRequests: []
    }
  },
  hsts: {
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
  },
  noSniff: true,  // X-Content-Type-Options
  frameguard: { action: 'deny' },  // X-Frame-Options
  xssFilter: true,  // X-XSS-Protection
  referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
  permittedCrossDomainPolicies: false
}));

// Additional security headers
app.use((req, res, next) => {
  // Permissions Policy (formerly Feature Policy)
  res.setHeader('Permissions-Policy', 
    'geolocation=(), microphone=(), camera=(), payment=()'
  );
  
  // X-Permitted-Cross-Domain-Policies
  res.setHeader('X-Permitted-Cross-Domain-Policies', 'none');
  
  // Clear-Site-Data for logout
  if (req.path === '/logout') {
    res.setHeader('Clear-Site-Data', '"cache", "cookies", "storage"');
  }
  
  next();
});

// CORS configuration
const corsOptions = {
  origin: function (origin: string, callback: Function) {
    const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || [];
    
    if (!origin || allowedOrigins.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
  allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
  exposedHeaders: ['X-Total-Count', 'X-Page-Count'],
  maxAge: 86400  // 24 hours
};

app.use(cors(corsOptions));
```

## Sensitive Data Handling

### PII Protection
Protect Personally Identifiable Information.

**Why:** PII protection is legally required by regulations like GDPR and CCPA. Breaches can result in massive fines, lawsuits, and permanent reputation damage.

```typescript
// PII detection and masking
const PII_PATTERNS = {
  ssn: /\b\d{3}-\d{2}-\d{4}\b/,
  creditCard: /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/,
  email: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/,
  phone: /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/,
  ipAddress: /\b(?:\d{1,3}\.){3}\d{1,3}\b/
};

function maskPII(text: string): string {
  let masked = text;
  
  // Mask SSN
  masked = masked.replace(PII_PATTERNS.ssn, 'XXX-XX-XXXX');
  
  // Mask credit cards
  masked = masked.replace(PII_PATTERNS.creditCard, (match) => {
    return match.slice(0, 4) + ' XXXX XXXX ' + match.slice(-4);
  });
  
  // Mask email
  masked = masked.replace(PII_PATTERNS.email, (match) => {
    const [local, domain] = match.split('@');
    return local[0] + '***@' + domain;
  });
  
  return masked;
}

// Audit logging with PII protection
async function auditLog(action: string, data: any): Promise<void> {
  // Sanitize data before logging
  const sanitized = JSON.parse(JSON.stringify(data, (key, value) => {
    // Never log these fields
    const blacklist = ['password', 'token', 'secret', 'apiKey', 'creditCard', 'ssn'];
    if (blacklist.includes(key)) {
      return '[REDACTED]';
    }
    
    // Mask PII in string values
    if (typeof value === 'string') {
      return maskPII(value);
    }
    
    return value;
  }));
  
  await db.query(
    `INSERT INTO audit_logs 
     (action, data, user_id, ip_address, user_agent, timestamp)
     VALUES ($1, $2, $3, $4, $5, $6)`,
    [action, sanitized, req.user?.id, req.ip, req.get('user-agent'), new Date()]
  );
}

// Data retention and purging
CREATE TABLE data_retention_policies (
    table_name VARCHAR(100) PRIMARY KEY,
    retention_days INTEGER NOT NULL,
    purge_enabled BOOLEAN DEFAULT true,
    last_purge TIMESTAMPTZ
);

-- Automated PII purge
CREATE OR REPLACE FUNCTION purge_old_pii()
RETURNS void AS $$
DECLARE
    policy RECORD;
BEGIN
    FOR policy IN SELECT * FROM data_retention_policies WHERE purge_enabled
    LOOP
        EXECUTE format(
            'DELETE FROM %I WHERE created_at < NOW() - INTERVAL ''%s days''',
            policy.table_name,
            policy.retention_days
        );
        
        UPDATE data_retention_policies 
        SET last_purge = NOW() 
        WHERE table_name = policy.table_name;
    END LOOP;
END;
$$ LANGUAGE plpgsql;

// Right to be forgotten (GDPR)
async function deleteUserData(userId: string): Promise<void> {
  const transaction = await db.beginTransaction();
  
  try {
    // Anonymize rather than delete where possible
    await transaction.query(
      `UPDATE posts 
       SET user_id = '00000000-0000-0000-0000-000000000000',
           content = '[DELETED]'
       WHERE user_id = $1`,
      [userId]
    );
    
    // Delete PII
    await transaction.query('DELETE FROM user_profiles WHERE user_id = $1', [userId]);
    await transaction.query('DELETE FROM user_addresses WHERE user_id = $1', [userId]);
    
    // Delete user record
    await transaction.query('DELETE FROM users WHERE id = $1', [userId]);
    
    await transaction.commit();
    
    // Log the deletion
    await auditLog('user_data_deleted', { userId, reason: 'GDPR request' });
    
  } catch (error) {
    await transaction.rollback();
    throw error;
  }
}
```

## Security Monitoring

### Intrusion Detection
Monitor for suspicious activities.

**Why:** Early detection of attacks allows rapid response before damage occurs. Monitoring provides visibility into attack patterns and helps identify vulnerabilities before they're exploited.

```typescript
// Suspicious activity detection
class SecurityMonitor {
  private thresholds = {
    failedLogins: 5,
    passwordResets: 3,
    apiErrors: 100,
    unusualHours: [0, 6],  // 12 AM - 6 AM
    geoVelocity: 500  // km/hour max travel speed
  };
  
  async detectSuspiciousLogin(userId: string, ip: string, location: GeoLocation): Promise<Alert[]> {
    const alerts: Alert[] = [];
    
    // Check failed login attempts
    const failedAttempts = await redis.get(`failed_login:${userId}`);
    if (failedAttempts > this.thresholds.failedLogins) {
      alerts.push({
        type: 'EXCESSIVE_FAILED_LOGINS',
        severity: 'HIGH',
        userId,
        details: { attempts: failedAttempts }
      });
    }
    
    // Check unusual login time
    const hour = new Date().getHours();
    if (hour >= this.thresholds.unusualHours[0] && hour <= this.thresholds.unusualHours[1]) {
      const userPattern = await this.getUserLoginPattern(userId);
      if (!userPattern.includes(hour)) {
        alerts.push({
          type: 'UNUSUAL_LOGIN_TIME',
          severity: 'MEDIUM',
          userId,
          details: { hour }
        });
      }
    }
    
    // Check impossible travel
    const lastLogin = await this.getLastLogin(userId);
    if (lastLogin) {
      const distance = calculateDistance(lastLogin.location, location);
      const timeDiff = (Date.now() - lastLogin.timestamp) / 3600000;  // hours
      const velocity = distance / timeDiff;
      
      if (velocity > this.thresholds.geoVelocity) {
        alerts.push({
          type: 'IMPOSSIBLE_TRAVEL',
          severity: 'CRITICAL',
          userId,
          details: { 
            distance,
            timeDiff,
            velocity,
            lastLocation: lastLogin.location,
            currentLocation: location
          }
        });
      }
    }
    
    // Check for credential stuffing patterns
    const recentIpLogins = await redis.smembers(`ip_users:${ip}`);
    if (recentIpLogins.length > 10) {
      alerts.push({
        type: 'CREDENTIAL_STUFFING',
        severity: 'CRITICAL',
        details: {
          ip,
          attemptedUsers: recentIpLogins.length
        }
      });
    }
    
    return alerts;
  }
  
  async monitorApiUsage(apiKey: string, endpoint: string): Promise<void> {
    // Track unusual API patterns
    const pattern = await redis.hgetall(`api_pattern:${apiKey}`);
    
    // Detect scanning behavior
    if (this.isScanning(pattern)) {
      await this.alertSecurity({
        type: 'API_SCANNING',
        severity: 'HIGH',
        apiKey,
        pattern
      });
    }
    
    // Detect data exfiltration
    const dataVolume = await redis.get(`api_volume:${apiKey}`);
    if (dataVolume > this.thresholds.dataExfiltration) {
      await this.alertSecurity({
        type: 'POTENTIAL_DATA_EXFILTRATION',
        severity: 'CRITICAL',
        apiKey,
        volume: dataVolume
      });
    }
  }
}

// Real-time security event stream
class SecurityEventStream {
  async logSecurityEvent(event: SecurityEvent): Promise<void> {
    // Store in time-series database
    await influxDB.writePoints([{
      measurement: 'security_events',
      tags: {
        type: event.type,
        severity: event.severity,
        userId: event.userId
      },
      fields: {
        details: JSON.stringify(event.details)
      },
      timestamp: new Date()
    }]);
    
    // Send to SIEM
    await siem.send(event);
    
    // Critical alerts
    if (event.severity === 'CRITICAL') {
      await this.sendAlert(event);
    }
  }
}
```

## Incident Response

### Security Incident Procedures
Prepare for security incidents.

**Why:** Having a clear incident response plan minimizes damage during breaches, ensures legal compliance, maintains customer trust, and reduces recovery time.

```typescript
// Incident response automation
class IncidentResponse {
  async handleSecurityIncident(incident: SecurityIncident): Promise<void> {
    const response = {
      incidentId: generateIncidentId(),
      startTime: new Date(),
      type: incident.type,
      severity: incident.severity,
      actions: []
    };
    
    try {
      // Step 1: Contain
      if (incident.severity === 'CRITICAL') {
        await this.containThreat(incident);
        response.actions.push('Threat contained');
      }
      
      // Step 2: Assess
      const assessment = await this.assessDamage(incident);
      response.actions.push('Damage assessed');
      
      // Step 3: Notify
      if (this.requiresNotification(incident)) {
        await this.notifyStakeholders(incident, assessment);
        response.actions.push('Stakeholders notified');
      }
      
      // Step 4: Remediate
      await this.remediate(incident);
      response.actions.push('Remediation completed');
      
      // Step 5: Document
      await this.documentIncident(response);
      
    } catch (error) {
      await this.escalate(incident, error);
    }
  }
  
  async containThreat(incident: SecurityIncident): Promise<void> {
    switch (incident.type) {
      case 'ACCOUNT_COMPROMISE':
        // Lock affected accounts
        await this.lockUserAccount(incident.userId);
        await this.invalidateUserSessions(incident.userId);
        break;
        
      case 'API_KEY_LEAK':
        // Revoke compromised keys
        await this.revokeApiKey(incident.apiKeyId);
        break;
        
      case 'DATA_BREACH':
        // Isolate affected systems
        await this.enableReadOnlyMode();
        await this.blockSuspiciousIps(incident.ips);
        break;
    }
  }
}

// Breach notification requirements
const BREACH_NOTIFICATION_REQUIREMENTS = {
  GDPR: {
    timeframe: 72,  // hours
    authority: 'Data Protection Authority',
    userNotification: true,
    threshold: 'high risk to rights and freedoms'
  },
  CCPA: {
    timeframe: 'without unreasonable delay',
    authority: 'California Attorney General',
    userNotification: true,
    threshold: 'unencrypted PII'
  }
};
```

