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" });
}
});