Backend Security Fundamentals - 2/3

From Input Protection to Cryptographic Defense

You’ve mastered the OWASP Top 10, implemented bulletproof input validation, and built XSS and CSRF protection that stops attackers at your application’s boundaries. Your endpoints reject malicious requests before they can reach your core business logic. But here’s the cryptographic reality that separates secure applications from security theater: even with perfect input validation, your application is only as secure as how it handles the most sensitive data—passwords, personal information, and communications between client and server.

The cryptographic weakness that destroys trust:

// Your perfectly validated, SQL injection-proof login system
const registerUser = async (req, res) => {
  const validatedData = validateUserRegistration(req.body); // Perfect validation

  try {
    // ❌ The security disaster hiding in plain sight
    const newUser = await db.users.create({
      email: validatedData.email,
      username: validatedData.username,
      password: validatedData.password, // Stored in plaintext
      createdAt: new Date(),
    });

    res.status(201).json({
      success: true,
      message: "Account created successfully",
    });

    // When your database gets breached:
    // - Every user password is immediately exposed
    // - Users who reuse passwords lose accounts across multiple services
    // - Your company faces massive lawsuits and regulatory fines
    // - Brand reputation destroyed permanently
  } catch (error) {
    res.status(500).json({ error: "Registration failed" });
  }
};

The uncomfortable cryptographic truth: Attackers don’t need to bypass your input validation if they can compromise your database and find passwords stored in plaintext, or if they can intercept unencrypted communications between your users and servers. Modern applications require cryptographic security that protects data even when other security layers fail.

Real-world password breach consequences:

// What happens when password security fails:
const passwordBreachImpact = {
  databaseCompromise: {
    plaintextPasswords: "23 million users exposed instantly",
    passwordReuse: "Users lose accounts on 15+ other services",
    accountTakeovers: "Attackers access banking, email, social media",
    legalConsequences: "Class action lawsuits, GDPR fines up to 4% revenue",
  },

  inadequateHashing: {
    md5Cracked: "Rainbow tables break hashes in seconds",
    sha1Cracked: "GPU farms crack billions of hashes per second",
    noSaltHashing: "Common passwords exposed across all users",
    fastHashing: "Brute force attacks succeed in hours",
  },

  // The most secure application logic means nothing
  // when cryptographic foundations are weak
};

Cryptographic security mastery requires understanding:

  • Password hashing algorithms (bcrypt, scrypt, Argon2) that resist brute force attacks
  • Encryption vs. hashing fundamentals and when to use each approach
  • SSL/TLS implementation that secures all client-server communications
  • Certificate management and public key infrastructure (PKI)
  • Cryptographic best practices that remain secure as computing power increases

This article elevates your security knowledge from input protection to cryptographic defense. You’ll implement password security that survives database breaches, encryption that protects sensitive data at rest, and SSL/TLS configurations that secure communications against sophisticated attacks.


Password Security: The Cryptographic Foundation

Understanding Password Hashing vs Encryption

The fundamental difference that determines security:

// ❌ Common cryptographic mistakes that seem secure
const crypto = require("crypto");

class InsecurePasswordManager {
  // MISTAKE 1: Using encryption instead of hashing
  encryptPassword(password) {
    const algorithm = "aes-256-cbc";
    const key = process.env.ENCRYPTION_KEY;
    const iv = crypto.randomBytes(16);

    const cipher = crypto.createCipher(algorithm, key);
    let encrypted = cipher.update(password, "utf8", "hex");
    encrypted += cipher.final("hex");

    return encrypted;

    // Problem: Encryption is reversible
    // If encryption key is compromised, all passwords are exposed
    // Encryption is for data you need to decrypt later
  }

  // MISTAKE 2: Using fast hashing algorithms
  hashPasswordMD5(password) {
    return crypto.createHash("md5").update(password).digest("hex");

    // Problem: MD5 is cryptographically broken
    // Modern GPUs can compute billions of MD5 hashes per second
    // Rainbow tables make common passwords instantly crackable
  }

  hashPasswordSHA256(password) {
    return crypto.createHash("sha256").update(password).digest("hex");

    // Problem: SHA-256 is too fast
    // Designed for data integrity, not password security
    // No built-in salt means identical passwords have identical hashes
  }

  // MISTAKE 3: Using weak salts or no salts
  hashPasswordWithWeakSalt(password) {
    const salt = "12345"; // Static salt
    return crypto
      .createHash("sha256")
      .update(password + salt)
      .digest("hex");

    // Problem: Same salt for all passwords
    // Attackers can pre-compute rainbow tables for your salt
    // Dictionary attacks still work against common passwords
  }
}

Professional password hashing with bcrypt:

// ✅ Cryptographically secure password management
const bcrypt = require("bcrypt");
const crypto = require("crypto");
const zxcvbn = require("zxcvbn"); // Password strength estimation

class SecurePasswordManager {
  constructor() {
    // Cost factor for bcrypt (2^12 = 4096 iterations)
    // Increase this as hardware becomes faster
    this.saltRounds = 12;

    // For highly sensitive applications, consider higher values
    // this.saltRounds = 15; // Takes ~3 seconds on modern hardware
  }

  async hashPassword(password) {
    try {
      // Validate password strength first
      const strength = this.validatePasswordStrength(password);
      if (!strength.isStrong) {
        throw new PasswordError(
          "Password does not meet security requirements",
          {
            suggestions: strength.suggestions,
            score: strength.score,
          }
        );
      }

      // Generate salt and hash password
      // bcrypt automatically generates a unique salt for each password
      const saltedHash = await bcrypt.hash(password, this.saltRounds);

      return {
        hash: saltedHash,
        algorithm: "bcrypt",
        saltRounds: this.saltRounds,
        hashedAt: new Date(),
      };
    } catch (error) {
      console.error("Password hashing failed:", error);
      throw error;
    }
  }

  async verifyPassword(plainPassword, hashedPassword) {
    try {
      // bcrypt automatically extracts the salt from the hash
      const isValid = await bcrypt.compare(plainPassword, hashedPassword);
      return isValid;
    } catch (error) {
      console.error("Password verification failed:", error);
      return false;
    }
  }

  validatePasswordStrength(password) {
    // Use zxcvbn for sophisticated password strength analysis
    const result = zxcvbn(password);

    const requirements = {
      minLength: password.length >= 12,
      hasUppercase: /[A-Z]/.test(password),
      hasLowercase: /[a-z]/.test(password),
      hasNumbers: /\d/.test(password),
      hasSpecialChars: /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password),
      notCommonPassword: result.score >= 3, // zxcvbn score 0-4
      noSequentialChars: !/(.)\1\1/.test(password), // No 3+ repeated characters
      noKeyboardPatterns: !/(qwerty|asdf|123456|password)/i.test(password),
    };

    const failedRequirements = Object.entries(requirements)
      .filter(([key, passed]) => !passed)
      .map(([key]) => key);

    const suggestions = this.generatePasswordSuggestions(
      failedRequirements,
      result
    );

    return {
      isStrong: failedRequirements.length === 0 && result.score >= 3,
      score: result.score,
      requirements,
      failedRequirements,
      suggestions,
      crackTime: result.crack_times_display.offline_slow_hashing_1e4_per_second,
    };
  }

  generatePasswordSuggestions(failedRequirements, zxcvbnResult) {
    const suggestions = [];

    if (failedRequirements.includes("minLength")) {
      suggestions.push("Use at least 12 characters");
    }

    if (failedRequirements.includes("hasUppercase")) {
      suggestions.push("Include uppercase letters");
    }

    if (failedRequirements.includes("hasLowercase")) {
      suggestions.push("Include lowercase letters");
    }

    if (failedRequirements.includes("hasNumbers")) {
      suggestions.push("Include numbers");
    }

    if (failedRequirements.includes("hasSpecialChars")) {
      suggestions.push("Include special characters (!@#$%^&*)");
    }

    if (failedRequirements.includes("notCommonPassword")) {
      suggestions.push("Avoid common passwords and dictionary words");
    }

    if (failedRequirements.includes("noSequentialChars")) {
      suggestions.push("Avoid repeating the same character multiple times");
    }

    // Add zxcvbn-specific suggestions
    suggestions.push(...zxcvbnResult.feedback.suggestions);

    return suggestions;
  }

  // Secure random password generation
  generateSecurePassword(length = 16) {
    const uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    const lowercase = "abcdefghijklmnopqrstuvwxyz";
    const numbers = "0123456789";
    const symbols = "!@#$%^&*()_+-=[]{}|;:,.<>?";

    const allChars = uppercase + lowercase + numbers + symbols;
    let password = "";

    // Ensure at least one character from each category
    password += uppercase[crypto.randomInt(0, uppercase.length)];
    password += lowercase[crypto.randomInt(0, lowercase.length)];
    password += numbers[crypto.randomInt(0, numbers.length)];
    password += symbols[crypto.randomInt(0, symbols.length)];

    // Fill remaining length with random characters
    for (let i = 4; i < length; i++) {
      password += allChars[crypto.randomInt(0, allChars.length)];
    }

    // Shuffle the password to avoid predictable patterns
    return this.shuffleString(password);
  }

  shuffleString(str) {
    const arr = str.split("");
    for (let i = arr.length - 1; i > 0; i--) {
      const j = crypto.randomInt(0, i + 1);
      [arr[i], arr[j]] = [arr[j], arr[i]];
    }
    return arr.join("");
  }

  // Password rotation and security policies
  async checkPasswordAge(userPasswordData) {
    const now = new Date();
    const passwordAge = now - new Date(userPasswordData.hashedAt);
    const maxAge = 90 * 24 * 60 * 60 * 1000; // 90 days

    return {
      needsRotation: passwordAge > maxAge,
      ageInDays: Math.floor(passwordAge / (24 * 60 * 60 * 1000)),
      daysUntilExpiry: Math.floor(
        (maxAge - passwordAge) / (24 * 60 * 60 * 1000)
      ),
    };
  }
}

// Integration with user registration
class SecureUserService {
  constructor() {
    this.passwordManager = new SecurePasswordManager();
  }

  async registerUser(userData) {
    try {
      // Validate all input data
      const validatedData = this.validateRegistrationData(userData);

      // Hash password securely
      const passwordData = await this.passwordManager.hashPassword(
        validatedData.password
      );

      // Create user with hashed password
      const newUser = await db.users.create({
        email: validatedData.email,
        username: validatedData.username,
        passwordHash: passwordData.hash,
        passwordAlgorithm: passwordData.algorithm,
        passwordSaltRounds: passwordData.saltRounds,
        passwordCreatedAt: passwordData.hashedAt,
        emailVerified: false,
        isActive: true,
        createdAt: new Date(),
        updatedAt: new Date(),
        loginAttempts: 0,
        lockoutUntil: null,
      });

      // Generate email verification token
      const verificationToken = await this.generateVerificationToken(
        newUser.id
      );

      // Send verification email (implementation depends on your email service)
      await this.sendVerificationEmail(newUser.email, verificationToken);

      // Log security event
      this.logSecurityEvent({
        type: "user_registered",
        userId: newUser.id,
        email: newUser.email,
        timestamp: new Date(),
        ipAddress: userData.ipAddress,
        userAgent: userData.userAgent,
      });

      return {
        id: newUser.id,
        email: newUser.email,
        username: newUser.username,
        emailVerified: false,
        message:
          "Account created successfully. Please check your email for verification.",
      };
    } catch (error) {
      if (error instanceof PasswordError) {
        throw new ValidationError("Password requirements not met", {
          suggestions: error.details.suggestions,
          score: error.details.score,
        });
      }

      console.error("User registration failed:", error);
      throw new Error("Registration failed");
    }
  }

  async authenticateUser(credentials) {
    try {
      const { email, password, ipAddress, userAgent } = credentials;

      // Find user by email
      const user = await db.users.findOne({
        email: email.toLowerCase(),
        isActive: true,
      });

      if (!user) {
        // Prevent username enumeration by timing attack
        await this.passwordManager.hashPassword("dummy_password");
        throw new AuthenticationError("Invalid credentials");
      }

      // Check for account lockout
      if (user.lockoutUntil && user.lockoutUntil > new Date()) {
        throw new AuthenticationError(
          "Account temporarily locked due to too many failed attempts"
        );
      }

      // Verify password
      const isValidPassword = await this.passwordManager.verifyPassword(
        password,
        user.passwordHash
      );

      if (!isValidPassword) {
        // Increment failed login attempts
        await this.handleFailedLogin(user.id, ipAddress);
        throw new AuthenticationError("Invalid credentials");
      }

      // Check if password needs rotation
      const passwordAge = await this.passwordManager.checkPasswordAge({
        hashedAt: user.passwordCreatedAt,
      });

      // Reset login attempts on successful login
      await db.users.updateOne(
        { id: user.id },
        {
          $unset: { lockoutUntil: 1 },
          $set: {
            loginAttempts: 0,
            lastLoginAt: new Date(),
            lastLoginIp: ipAddress,
          },
        }
      );

      // Log successful authentication
      this.logSecurityEvent({
        type: "authentication_success",
        userId: user.id,
        email: user.email,
        timestamp: new Date(),
        ipAddress,
        userAgent,
        passwordNeedsRotation: passwordAge.needsRotation,
      });

      return {
        user: {
          id: user.id,
          email: user.email,
          username: user.username,
          emailVerified: user.emailVerified,
        },
        passwordNeedsRotation: passwordAge.needsRotation,
        daysUntilPasswordExpiry: passwordAge.daysUntilExpiry,
      };
    } catch (error) {
      console.error("Authentication failed:", error);
      throw error;
    }
  }

  async handleFailedLogin(userId, ipAddress) {
    const maxAttempts = 5;
    const lockoutDuration = 30 * 60 * 1000; // 30 minutes

    const user = await db.users.findOne({ id: userId });
    const loginAttempts = (user.loginAttempts || 0) + 1;

    const updateData = { loginAttempts };

    if (loginAttempts >= maxAttempts) {
      updateData.lockoutUntil = new Date(Date.now() + lockoutDuration);

      // Log security event for potential brute force attack
      this.logSecurityEvent({
        type: "account_locked",
        userId,
        loginAttempts,
        timestamp: new Date(),
        ipAddress,
        severity: "high",
      });
    }

    await db.users.updateOne({ id: userId }, { $set: updateData });
  }
}

Advanced Password Hashing with Argon2

Next-generation password hashing:

// ✅ Argon2 - Winner of the Password Hashing Competition
const argon2 = require("argon2");

class Argon2PasswordManager extends SecurePasswordManager {
  constructor() {
    super();

    // Argon2 configuration - tuned for security vs performance
    this.argon2Config = {
      type: argon2.argon2id, // Most secure variant
      memoryCost: 2 ** 16, // 64 MB memory usage
      timeCost: 3, // 3 iterations
      parallelism: 1, // Single-threaded (increase for multi-core servers)
      hashLength: 32, // 32-byte hash output
      saltLength: 16, // 16-byte salt
    };
  }

  async hashPassword(password) {
    try {
      // Validate password strength
      const strength = this.validatePasswordStrength(password);
      if (!strength.isStrong) {
        throw new PasswordError(
          "Password does not meet security requirements",
          {
            suggestions: strength.suggestions,
            score: strength.score,
          }
        );
      }

      // Hash with Argon2
      const hash = await argon2.hash(password, this.argon2Config);

      return {
        hash,
        algorithm: "argon2id",
        config: this.argon2Config,
        hashedAt: new Date(),
      };
    } catch (error) {
      console.error("Argon2 password hashing failed:", error);
      throw error;
    }
  }

  async verifyPassword(plainPassword, hashedPassword) {
    try {
      return await argon2.verify(hashedPassword, plainPassword);
    } catch (error) {
      console.error("Argon2 password verification failed:", error);
      return false;
    }
  }

  // Performance tuning based on server capabilities
  async benchmarkArgon2Performance() {
    const testPassword = "test_password_for_benchmarking";
    const configs = [
      { memoryCost: 2 ** 14, timeCost: 2, parallelism: 1 }, // Light
      { memoryCost: 2 ** 16, timeCost: 3, parallelism: 1 }, // Medium (recommended)
      { memoryCost: 2 ** 18, timeCost: 4, parallelism: 2 }, // Heavy
    ];

    console.log("Benchmarking Argon2 configurations...");

    for (const config of configs) {
      const startTime = Date.now();
      await argon2.hash(testPassword, { ...config, type: argon2.argon2id });
      const duration = Date.now() - startTime;

      console.log(
        `Config: memory=${config.memoryCost / 1024}KB, time=${
          config.timeCost
        }, parallel=${config.parallelism} - Duration: ${duration}ms`
      );
    }

    // Aim for 200-500ms on your production hardware
    // Increase parameters if hashing is too fast
    // Decrease if it's too slow for user experience
  }
}

Data Encryption: Protecting Information at Rest

Encryption vs Hashing: When to Use Each

Understanding the cryptographic toolkit:

// ✅ Comprehensive encryption and hashing service
const crypto = require("crypto");

class CryptographicService {
  constructor() {
    // Encryption keys (store securely in environment variables)
    this.encryptionKey = process.env.ENCRYPTION_KEY; // 32-byte key for AES-256
    this.signingKey = process.env.SIGNING_KEY; // For HMAC signatures

    if (!this.encryptionKey || !this.signingKey) {
      throw new Error("Encryption keys not configured");
    }

    // Encryption algorithm
    this.algorithm = "aes-256-gcm"; // Authenticated encryption
  }

  // Use encryption for data you need to decrypt later
  encryptSensitiveData(plaintext) {
    try {
      const iv = crypto.randomBytes(16); // Random initialization vector
      const cipher = crypto.createCipher(this.algorithm, this.encryptionKey);
      cipher.setIV(iv);

      let encrypted = cipher.update(plaintext, "utf8", "hex");
      encrypted += cipher.final("hex");

      // Get authentication tag for integrity verification
      const authTag = cipher.getAuthTag();

      // Combine IV, auth tag, and encrypted data
      const result = {
        iv: iv.toString("hex"),
        authTag: authTag.toString("hex"),
        encrypted: encrypted,
        algorithm: this.algorithm,
      };

      return Buffer.from(JSON.stringify(result)).toString("base64");
    } catch (error) {
      console.error("Encryption failed:", error);
      throw new Error("Failed to encrypt data");
    }
  }

  decryptSensitiveData(encryptedData) {
    try {
      // Parse encrypted data structure
      const data = JSON.parse(
        Buffer.from(encryptedData, "base64").toString("utf8")
      );

      const iv = Buffer.from(data.iv, "hex");
      const authTag = Buffer.from(data.authTag, "hex");
      const encrypted = data.encrypted;

      // Create decipher
      const decipher = crypto.createDecipher(
        this.algorithm,
        this.encryptionKey
      );
      decipher.setIV(iv);
      decipher.setAuthTag(authTag);

      // Decrypt
      let decrypted = decipher.update(encrypted, "hex", "utf8");
      decrypted += decipher.final("utf8");

      return decrypted;
    } catch (error) {
      console.error("Decryption failed:", error);
      throw new Error("Failed to decrypt data");
    }
  }

  // Use hashing for data integrity verification (non-reversible)
  createDataHash(data) {
    return crypto.createHash("sha256").update(data).digest("hex");
  }

  // Use HMAC for authenticated hashing (prevents tampering)
  createAuthenticatedHash(data) {
    return crypto
      .createHmac("sha256", this.signingKey)
      .update(data)
      .digest("hex");
  }

  verifyAuthenticatedHash(data, expectedHash) {
    const actualHash = this.createAuthenticatedHash(data);
    return crypto.timingSafeEqual(
      Buffer.from(actualHash, "hex"),
      Buffer.from(expectedHash, "hex")
    );
  }

  // Secure random token generation
  generateSecureToken(length = 32) {
    return crypto.randomBytes(length).toString("hex");
  }

  generateApiKey() {
    const prefix = "sk_"; // Identifiable prefix
    const randomPart = crypto.randomBytes(32).toString("hex");
    return prefix + randomPart;
  }
}

// Real-world usage examples
class SecureDataService {
  constructor() {
    this.crypto = new CryptographicService();
  }

  // Encrypt personal information (PII)
  async storeUserProfile(userId, profileData) {
    try {
      // Separate sensitive from non-sensitive data
      const { ssn, creditCardNumber, medicalInfo, ...publicData } = profileData;

      // Encrypt sensitive data
      const encryptedData = {};
      if (ssn) {
        encryptedData.ssn = this.crypto.encryptSensitiveData(ssn);
      }
      if (creditCardNumber) {
        encryptedData.creditCardNumber =
          this.crypto.encryptSensitiveData(creditCardNumber);
      }
      if (medicalInfo) {
        encryptedData.medicalInfo = this.crypto.encryptSensitiveData(
          JSON.stringify(medicalInfo)
        );
      }

      // Create integrity hash for public data
      const publicDataHash = this.crypto.createAuthenticatedHash(
        JSON.stringify(publicData)
      );

      await db.userProfiles.updateOne(
        { userId },
        {
          $set: {
            ...publicData,
            encryptedData,
            dataIntegrityHash: publicDataHash,
            updatedAt: new Date(),
          },
        },
        { upsert: true }
      );

      return { success: true };
    } catch (error) {
      console.error("Profile storage failed:", error);
      throw new Error("Failed to store profile");
    }
  }

  async getUserProfile(userId) {
    try {
      const profile = await db.userProfiles.findOne({ userId });
      if (!profile) return null;

      // Verify data integrity
      const { encryptedData, dataIntegrityHash, ...publicData } = profile;
      const expectedHash = this.crypto.createAuthenticatedHash(
        JSON.stringify(publicData)
      );

      if (
        !crypto.timingSafeEqual(
          Buffer.from(dataIntegrityHash, "hex"),
          Buffer.from(expectedHash, "hex")
        )
      ) {
        console.error("Data integrity verification failed for user:", userId);
        throw new Error("Data integrity compromised");
      }

      // Decrypt sensitive data
      const decryptedData = {};
      for (const [key, encryptedValue] of Object.entries(encryptedData)) {
        try {
          if (key === "medicalInfo") {
            decryptedData[key] = JSON.parse(
              this.crypto.decryptSensitiveData(encryptedValue)
            );
          } else {
            decryptedData[key] =
              this.crypto.decryptSensitiveData(encryptedValue);
          }
        } catch (decryptError) {
          console.error(
            `Failed to decrypt ${key} for user ${userId}:`,
            decryptError
          );
          decryptedData[key] = null; // Handle gracefully
        }
      }

      return {
        ...publicData,
        ...decryptedData,
      };
    } catch (error) {
      console.error("Profile retrieval failed:", error);
      throw new Error("Failed to retrieve profile");
    }
  }

  // Token-based authentication with encrypted session data
  async createSecureSession(userId, sessionData) {
    try {
      const sessionId = this.crypto.generateSecureToken();
      const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24 hours

      // Encrypt session data
      const encryptedSessionData = this.crypto.encryptSensitiveData(
        JSON.stringify(sessionData)
      );

      await db.sessions.create({
        sessionId,
        userId,
        encryptedData: encryptedSessionData,
        createdAt: new Date(),
        expiresAt,
        isActive: true,
      });

      return sessionId;
    } catch (error) {
      console.error("Session creation failed:", error);
      throw new Error("Failed to create session");
    }
  }

  async validateSession(sessionId) {
    try {
      const session = await db.sessions.findOne({
        sessionId,
        isActive: true,
        expiresAt: { $gt: new Date() },
      });

      if (!session) return null;

      // Decrypt session data
      const sessionData = JSON.parse(
        this.crypto.decryptSensitiveData(session.encryptedData)
      );

      return {
        userId: session.userId,
        data: sessionData,
        expiresAt: session.expiresAt,
      };
    } catch (error) {
      console.error("Session validation failed:", error);
      return null;
    }
  }
}

SSL/TLS: Securing Communications

Professional SSL/TLS Configuration

Bulletproof HTTPS implementation:

// ✅ Production-grade HTTPS server setup
const https = require("https");
const http = require("http");
const fs = require("fs");
const path = require("path");
const express = require("express");

class SecureServerManager {
  constructor() {
    this.app = express();
    this.httpServer = null;
    this.httpsServer = null;

    this.setupSecurityMiddleware();
    this.configureSSL();
  }

  setupSecurityMiddleware() {
    // Trust proxy for accurate client IP detection
    this.app.set("trust proxy", 1);

    // Force HTTPS in production
    if (process.env.NODE_ENV === "production") {
      this.app.use((req, res, next) => {
        if (!req.secure && req.get("x-forwarded-proto") !== "https") {
          return res.redirect(
            301,
            `https://${req.get("host")}${req.originalUrl}`
          );
        }
        next();
      });
    }

    // Security headers
    this.app.use((req, res, next) => {
      // HTTP Strict Transport Security
      res.set(
        "Strict-Transport-Security",
        "max-age=63072000; includeSubDomains; preload"
      );

      // Content Security Policy
      res.set(
        "Content-Security-Policy",
        "default-src 'self'; " +
          "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; " +
          "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; " +
          "font-src 'self' https://fonts.gstatic.com; " +
          "img-src 'self' data: https:; " +
          "connect-src 'self'; " +
          "frame-ancestors 'none'; " +
          "base-uri 'self'; " +
          "form-action 'self'"
      );

      // Additional security headers
      res.set("X-Content-Type-Options", "nosniff");
      res.set("X-Frame-Options", "DENY");
      res.set("X-XSS-Protection", "1; mode=block");
      res.set("Referrer-Policy", "strict-origin-when-cross-origin");
      res.set("Permissions-Policy", "camera=(), microphone=(), geolocation=()");

      next();
    });
  }

  configureSSL() {
    if (process.env.NODE_ENV === "production") {
      try {
        // Production SSL configuration
        this.sslOptions = {
          // Certificate files (use Let's Encrypt or commercial certificates)
          key: fs.readFileSync("/etc/ssl/private/server.key"),
          cert: fs.readFileSync("/etc/ssl/certs/server.crt"),
          ca: fs.readFileSync("/etc/ssl/certs/ca-bundle.crt"), // Certificate chain

          // SSL/TLS security settings
          secureProtocol: "TLSv1_2_method", // Minimum TLS 1.2
          ciphers: this.getSecureCipherSuite(),
          honorCipherOrder: true,

          // Perfect Forward Secrecy
          dhparam: fs.readFileSync("/etc/ssl/private/dhparam.pem"),

          // Client certificate verification (if needed)
          requestCert: false,
          rejectUnauthorized: false,
        };
      } catch (error) {
        console.error("SSL configuration failed:", error);
        process.exit(1);
      }
    } else {
      // Development self-signed certificate
      this.sslOptions = this.generateSelfSignedCertificate();
    }
  }

  getSecureCipherSuite() {
    // Modern, secure cipher suite (as of 2024)
    return [
      "ECDHE-RSA-AES256-GCM-SHA384",
      "ECDHE-RSA-AES128-GCM-SHA256",
      "ECDHE-RSA-AES256-SHA384",
      "ECDHE-RSA-AES128-SHA256",
      "ECDHE-RSA-AES256-SHA",
      "ECDHE-RSA-AES128-SHA",
      "AES256-GCM-SHA384",
      "AES128-GCM-SHA256",
      "AES256-SHA256",
      "AES128-SHA256",
      "AES256-SHA",
      "AES128-SHA",
    ].join(":");
  }

  generateSelfSignedCertificate() {
    // For development only - never use in production
    const selfsigned = require("selfsigned");
    const attrs = [
      { name: "commonName", value: "localhost" },
      { name: "organizationName", value: "Development" },
      { name: "organizationalUnitName", value: "IT Department" },
    ];

    const pems = selfsigned.generate(attrs, {
      keySize: 2048,
      days: 365,
      algorithm: "sha256",
    });

    return {
      key: pems.private,
      cert: pems.cert,
    };
  }

  startServers() {
    const httpPort = process.env.HTTP_PORT || 80;
    const httpsPort = process.env.HTTPS_PORT || 443;

    // HTTP server (redirects to HTTPS in production)
    this.httpServer = http.createServer(this.app);
    this.httpServer.listen(httpPort, () => {
      console.log(`HTTP server listening on port ${httpPort}`);
    });

    // HTTPS server
    this.httpsServer = https.createServer(this.sslOptions, this.app);
    this.httpsServer.listen(httpsPort, () => {
      console.log(`HTTPS server listening on port ${httpsPort}`);
    });

    // Graceful shutdown
    this.setupGracefulShutdown();
  }

  setupGracefulShutdown() {
    const shutdown = () => {
      console.log("Received shutdown signal, closing servers...");

      this.httpServer.close(() => {
        console.log("HTTP server closed");

        this.httpsServer.close(() => {
          console.log("HTTPS server closed");
          process.exit(0);
        });
      });

      // Force shutdown after 30 seconds
      setTimeout(() => {
        console.error("Forcing shutdown after timeout");
        process.exit(1);
      }, 30000);
    };

    process.on("SIGTERM", shutdown);
    process.on("SIGINT", shutdown);
  }
}

// SSL/TLS certificate management
class CertificateManager {
  constructor() {
    this.certPath = "/etc/ssl/certs/";
    this.keyPath = "/etc/ssl/private/";
  }

  async checkCertificateExpiry() {
    try {
      const cert = fs.readFileSync(path.join(this.certPath, "server.crt"));
      const certInfo = this.parseCertificate(cert);

      const now = new Date();
      const expiryDate = new Date(certInfo.validTo);
      const daysUntilExpiry = Math.floor(
        (expiryDate - now) / (1000 * 60 * 60 * 24)
      );

      if (daysUntilExpiry <= 30) {
        console.warn(`SSL certificate expires in ${daysUntilExpiry} days!`);
        await this.sendCertificateExpiryAlert(daysUntilExpiry);
      }

      return {
        expiryDate,
        daysUntilExpiry,
        needsRenewal: daysUntilExpiry <= 30,
      };
    } catch (error) {
      console.error("Certificate expiry check failed:", error);
      return null;
    }
  }

  parseCertificate(certBuffer) {
    // Parse X.509 certificate (simplified)
    // In production, use a proper X.509 parsing library
    const certString = certBuffer.toString();

    // Extract validity dates (this is a simplified example)
    const validFromMatch = certString.match(/Not Before: (.+)/);
    const validToMatch = certString.match(/Not After : (.+)/);

    return {
      validFrom: validFromMatch ? validFromMatch[1] : null,
      validTo: validToMatch ? validToMatch[1] : null,
    };
  }

  async sendCertificateExpiryAlert(daysUntilExpiry) {
    // Send alert to operations team
    const alert = {
      type: "certificate_expiry_warning",
      message: `SSL certificate expires in ${daysUntilExpiry} days`,
      urgency: daysUntilExpiry <= 7 ? "high" : "medium",
      timestamp: new Date(),
    };

    // Implementation depends on your alerting system
    console.error("CERTIFICATE ALERT:", alert);
  }
}

// Usage
const secureServer = new SecureServerManager();
const certManager = new CertificateManager();

// Check certificate expiry daily
setInterval(() => {
  certManager.checkCertificateExpiry();
}, 24 * 60 * 60 * 1000);

secureServer.startServers();

Key Takeaways

Cryptographic security transforms your application from vulnerable to bulletproof. Password hashing with bcrypt or Argon2 protects user credentials even when databases are compromised. Proper encryption safeguards sensitive data at rest. SSL/TLS secures all communications between clients and servers.

The cryptographic security mindset:

  • Hash passwords, encrypt data: Passwords should never be reversible; sensitive data should be encrypted when you need to decrypt it later
  • Use proven algorithms: bcrypt, Argon2, AES-256-GCM are battle-tested; avoid creating custom cryptography
  • Forward secrecy matters: SSL/TLS configurations should protect past communications even if keys are compromised
  • Key management is critical: Encryption is only as secure as how you protect the keys

What distinguishes cryptographically secure applications:

  • Password hashing that survives rainbow table attacks and GPU-accelerated brute forcing
  • Encryption implementations that protect data integrity with authenticated encryption modes
  • SSL/TLS configurations that enforce modern security standards and cipher suites
  • Certificate management that prevents service interruptions due to expired certificates

What’s Next

With cryptographic foundations secured, the final piece of backend security covers rate limiting, API security, secrets management, and compliance requirements. You’ll learn to defend against DDoS attacks, implement proper secrets management, and ensure your applications meet regulatory security standards.

Cryptography protects your data. The final security layer protects your entire application infrastructure from operational attacks and regulatory violations.