Cryptographic Failures

Top 10 OWASP


What?

Cryptographic Failures refers to weaknesses in cryptography, or the complete lack of encryption when it's needed. This vulnerability occurs when sensitive data is not properly protected, making it vulnerable to exposure. Common issues include using weak encryption algorithms, improper key management, or transmitting sensitive data without encryption.

How to Prevent

  • Classify data processed, stored, or transmitted by an application. Identify which data is sensitive according to privacy laws, regulatory requirements, or business needs.
  • Don't store sensitive data unnecessarily. Discard it as soon as possible or use PCI DSS compliant tokenization.
  • Make sure to encrypt all sensitive data at rest.
  • Ensure up-to-date and strong standard algorithms, protocols, and keys are in place; use proper key management.
  • Encrypt all data in transit with secure protocols such as TLS with forward secrecy (FS) ciphers, cipher prioritization by the server, and secure parameters.
  • Disable caching for responses that contain sensitive data.
  • Store passwords using strong adaptive and salted hashing functions with a work factor (delay factor), such as Argon2, scrypt, bcrypt, or PBKDF2.
  • Always use authenticated encryption instead of just encryption.
  • Verify independently the effectiveness of configuration and settings.
// Example of proper password hashing using bcrypt
const bcrypt = require("bcrypt");

class PasswordService {
  static async hashPassword(plainPassword) {
    // Generate a salt with appropriate cost factor
    const saltRounds = 12;
    const salt = await bcrypt.genSalt(saltRounds);

    // Hash the password with the generated salt
    const hashedPassword = await bcrypt.hash(plainPassword, salt);
    return hashedPassword;
  }

  static async verifyPassword(plainPassword, hashedPassword) {
    // Compare the provided password with stored hash
    return await bcrypt.compare(plainPassword, hashedPassword);
  }
}

// Example of data encryption using Node.js crypto
const crypto = require("crypto");

class EncryptionService {
  // Use environment variables for key management
  static ENCRYPTION_KEY = process.env.ENCRYPTION_KEY;
  static ALGORITHM = "aes-256-gcm";

  static encrypt(text) {
    // Generate a random initialization vector
    const iv = crypto.randomBytes(12);

    // Create cipher with key, iv, and algorithm
    const cipher = crypto.createCipheriv(
      this.ALGORITHM,
      Buffer.from(this.ENCRYPTION_KEY, "hex"),
      iv
    );

    // Encrypt the data
    let encrypted = cipher.update(text, "utf8", "hex");
    encrypted += cipher.final("hex");

    // Get the authentication tag
    const authTag = cipher.getAuthTag();

    // Return IV, encrypted data, and auth tag
    return {
      iv: iv.toString("hex"),
      encryptedData: encrypted,
      authTag: authTag.toString("hex"),
    };
  }

  static decrypt(encrypted) {
    // Create decipher
    const decipher = crypto.createDecipheriv(
      this.ALGORITHM,
      Buffer.from(this.ENCRYPTION_KEY, "hex"),
      Buffer.from(encrypted.iv, "hex")
    );

    // Set auth tag
    decipher.setAuthTag(Buffer.from(encrypted.authTag, "hex"));

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

    return decrypted;
  }
}

// Example usage in an API endpoint
app.post("/api/users", async (req, res) => {
  try {
    // Hash password before storing
    const hashedPassword = await PasswordService.hashPassword(
      req.body.password
    );

    // Encrypt sensitive data
    const encryptedSSN = EncryptionService.encrypt(req.body.ssn);

    // Store user with hashed password and encrypted data
    const user = await User.create({
      email: req.body.email,
      password: hashedPassword,
      ssn: encryptedSSN,
    });

    res.status(201).json({ message: "User created successfully" });
  } catch (error) {
    res.status(500).json({ error: "Internal server error" });
  }
});