Skip to main content

Use of Broken or Weak Cryptographic Algorithms

Insecure cryptography occurs when applications use outdated, broken, or improperly configured cryptographic algorithms, making encrypted data vulnerable to decryption, tampering, or forgery.

Medium CWE-327 A02:2021 Cryptographic Failures
Affects: C#JavaJavaScriptPHPPythonRubyGo

What is Insecure Cryptography?

Insecure Cryptography (CWE-327) refers to the use of cryptographic algorithms, modes, or configurations that do not provide adequate security. Cryptography is used to protect data confidentiality (encryption), integrity (hashing/MACs), and authenticity (signatures). When cryptographic implementations are flawed, attackers can:

  • Decrypt sensitive data — Break weak encryption to access plaintext passwords, financial data, personal information, and communications
  • Forge signatures — Create valid signatures or MACs using broken algorithms, bypassing integrity checks
  • Crack password hashes — Reverse weak or unsalted hashes to recover passwords at scale
  • Perform downgrade attacks — Force systems to use weaker algorithms when stronger ones are available
  • Exploit predictable randomness — Use weak random number generators to predict tokens, keys, or IVs

Cryptographic failures are ranked A02:2021 in the OWASP Top 10, reflecting their prevalence and impact on data protection.

Why it matters

Cryptographic failures have far-reaching consequences because encryption is the last line of defense for data protection:

  1. Regulatory compliance — PCI-DSS, HIPAA, GDPR, and other regulations mandate specific cryptographic standards. Using deprecated algorithms (MD5, SHA-1, DES, RC4) can result in compliance failures and fines.
  2. Data breach amplification — If a database is breached, properly encrypted data remains protected. Weakly encrypted data becomes immediately accessible to attackers.
  3. Password cracking at scale — MD5 and SHA-1 hashes can be reversed at billions of hashes per second using commodity GPUs. Unsalted hashes are trivially crackable via rainbow tables.
  4. TLS/SSL configuration — Weak cipher suites and protocol versions (SSLv3, TLS 1.0/1.1) expose traffic to interception attacks like POODLE and BEAST.
  5. Long-term data exposure — Data encrypted with weak algorithms today can be stored and decrypted later as computing power increases (“harvest now, decrypt later”).

How exploitation works

MD5/SHA-1 collision attacks

MD5 and SHA-1 are cryptographically broken — practical collision attacks exist:

# MD5 collision: two different files with the same MD5 hash
md5sum file1.pdf d131dd02c5e6eec4
md5sum file2.pdf d131dd02c5e6eec4  # Same hash, different content!

# SHA-1 collision: demonstrated by Google's SHAttered attack in 2017
# Cost: ~$110,000 in cloud compute

Password hash cracking

# Hashcat cracking MD5 passwords
# Speed: ~50 billion MD5 hashes/second on modern GPU
hashcat -m 0 -a 3 hashes.txt ?a?a?a?a?a?a?a?a

# Unsalted SHA-256: ~10 billion hashes/second
hashcat -m 1400 hashes.txt rockyou.txt

# Compare with bcrypt (work factor 12): ~25,000 hashes/second
# That's a 400,000x slowdown — making brute force infeasible

ECB mode pattern leakage

ECB (Electronic Codebook) mode encrypts each block independently, creating patterns in the ciphertext that reveal structure in the plaintext:

Plaintext blocks:   [AAAA] [BBBB] [AAAA] [CCCC]
ECB ciphertext:     [xxxx] [yyyy] [xxxx] [zzzz]  ← identical blocks visible!
CBC ciphertext:     [xxxx] [yyyy] [zzzz] [wwww]  ← no pattern

The famous “ECB penguin” demonstrates this — encrypting a bitmap image with ECB mode preserves the outline of the original image.

Weak random number generation

# VULNERABLE: Python's random module uses Mersenne Twister (not cryptographically secure)
import random

def generate_reset_token():
    return str(random.randint(100000, 999999))
    # Mersenne Twister state can be recovered from 624 outputs,
    # allowing prediction of all future and past tokens

Vulnerable code examples

C# — Weak algorithms

// VULNERABLE: MD5 for password hashing
public string HashPassword(string password)
{
    using var md5 = MD5.Create();
    byte[] hash = md5.ComputeHash(Encoding.UTF8.GetBytes(password));
    return Convert.ToHexString(hash);
}

// VULNERABLE: DES encryption (56-bit key, brute-forceable)
public byte[] Encrypt(byte[] data, byte[] key)
{
    using var des = DES.Create();
    des.Key = key;
    des.Mode = CipherMode.ECB;  // ECB mode leaks patterns
    using var encryptor = des.CreateEncryptor();
    return encryptor.TransformFinalBlock(data, 0, data.Length);
}

// VULNERABLE: SHA-1 for data integrity
public string ComputeSignature(string data, string secret)
{
    using var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(secret));
    byte[] hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
    return Convert.ToBase64String(hash);
}

Java — Deprecated crypto

// VULNERABLE: MD5 password hashing
public String hashPassword(String password) throws NoSuchAlgorithmException {
    MessageDigest md = MessageDigest.getInstance("MD5");
    byte[] digest = md.digest(password.getBytes(StandardCharsets.UTF_8));
    return DatatypeConverter.printHexBinary(digest);
}

// VULNERABLE: AES with ECB mode
public byte[] encrypt(byte[] data, SecretKey key) throws Exception {
    Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // ECB mode
    cipher.init(Cipher.ENCRYPT_MODE, key);
    return cipher.doFinal(data);
}

// VULNERABLE: Insecure random for security tokens
public String generateToken() {
    Random random = new Random(); // Not cryptographically secure
    return String.valueOf(random.nextLong());
}

Python — Weak crypto

# VULNERABLE: MD5 for password hashing
import hashlib

def hash_password(password):
    return hashlib.md5(password.encode()).hexdigest()

# VULNERABLE: DES encryption
from Crypto.Cipher import DES
cipher = DES.new(key, DES.MODE_ECB)  # DES + ECB
encrypted = cipher.encrypt(pad(data, 8))

# VULNERABLE: random module for security-sensitive operations
import random
session_id = ''.join(random.choices('abcdefghijklmnop', k=32))

Node.js — Deprecated crypto

// VULNERABLE: MD5 hashing
const crypto = require('crypto');

function hashPassword(password) {
    return crypto.createHash('md5').update(password).digest('hex');
}

// VULNERABLE: createCipher (uses key derivation without salt)
function encrypt(text, password) {
    const cipher = crypto.createCipher('des', password); // Deprecated API + DES
    let encrypted = cipher.update(text, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    return encrypted;
}

// VULNERABLE: Weak random
function generateResetCode() {
    return Math.floor(Math.random() * 1000000).toString().padStart(6, '0');
}

PHP — Insecure functions

// VULNERABLE: md5 for password hashing
$hash = md5($password);
$hash = sha1($password);  // Also weak for passwords

// VULNERABLE: mcrypt (deprecated, removed in PHP 7.2)
$encrypted = mcrypt_encrypt(MCRYPT_DES, $key, $data, MCRYPT_MODE_ECB);

// VULNERABLE: rand() for security-sensitive values
$token = rand(100000, 999999);

Secure code examples

C# — Strong cryptography

// SECURE: BCrypt for password hashing
public string HashPassword(string password)
{
    return BCrypt.Net.BCrypt.HashPassword(password, workFactor: 12);
}

public bool VerifyPassword(string password, string hash)
{
    return BCrypt.Net.BCrypt.Verify(password, hash);
}

// SECURE: AES-256-GCM for authenticated encryption
public (byte[] ciphertext, byte[] nonce, byte[] tag) Encrypt(byte[] data, byte[] key)
{
    byte[] nonce = new byte[12]; // 96-bit nonce for GCM
    RandomNumberGenerator.Fill(nonce);
    byte[] tag = new byte[16];
    byte[] ciphertext = new byte[data.Length];

    using var aes = new AesGcm(key, tagSizeInBytes: 16);
    aes.Encrypt(nonce, data, ciphertext, tag);
    return (ciphertext, nonce, tag);
}

// SECURE: Cryptographic random
public string GenerateToken()
{
    byte[] tokenBytes = RandomNumberGenerator.GetBytes(32);
    return Convert.ToBase64String(tokenBytes);
}

Java — Modern cryptography

// SECURE: BCrypt for password hashing
public String hashPassword(String password) {
    return BCrypt.hashpw(password, BCrypt.gensalt(12));
}

// SECURE: AES-GCM authenticated encryption
public byte[] encrypt(byte[] data, SecretKey key) throws Exception {
    byte[] iv = new byte[12]; // 96-bit IV for GCM
    SecureRandom.getInstanceStrong().nextBytes(iv);

    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    GCMParameterSpec spec = new GCMParameterSpec(128, iv); // 128-bit auth tag
    cipher.init(Cipher.ENCRYPT_MODE, key, spec);
    byte[] ciphertext = cipher.doFinal(data);

    // Prepend IV to ciphertext
    byte[] result = new byte[iv.length + ciphertext.length];
    System.arraycopy(iv, 0, result, 0, iv.length);
    System.arraycopy(ciphertext, 0, result, iv.length, ciphertext.length);
    return result;
}

// SECURE: Cryptographic random
public String generateToken() {
    byte[] bytes = new byte[32];
    SecureRandom.getInstanceStrong().nextBytes(bytes);
    return Base64.getUrlEncoder().encodeToString(bytes);
}

Python — Secure crypto

# SECURE: bcrypt for password hashing
import bcrypt

def hash_password(password: str) -> str:
    return bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12)).decode()

def verify_password(password: str, hashed: str) -> bool:
    return bcrypt.checkpw(password.encode(), hashed.encode())

# SECURE: AES-GCM authenticated encryption
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import os

def encrypt(data: bytes, key: bytes) -> bytes:
    nonce = os.urandom(12)  # 96-bit nonce
    aesgcm = AESGCM(key)    # 256-bit key
    ciphertext = aesgcm.encrypt(nonce, data, None)
    return nonce + ciphertext  # Prepend nonce

# SECURE: Cryptographic random
import secrets

def generate_token() -> str:
    return secrets.token_urlsafe(32)  # 256-bit random token

def generate_reset_code() -> str:
    return f"{secrets.randbelow(1000000):06d}"

Node.js — Modern crypto

// SECURE: bcrypt for password hashing
const bcrypt = require('bcrypt');

async function hashPassword(password) {
    return await bcrypt.hash(password, 12);
}

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

// SECURE: AES-256-GCM
const crypto = require('crypto');

function encrypt(data, key) {
    const iv = crypto.randomBytes(12); // 96-bit IV for GCM
    const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
    const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
    const tag = cipher.getAuthTag();
    return Buffer.concat([iv, tag, encrypted]); // IV + tag + ciphertext
}

// SECURE: Cryptographic random
function generateToken() {
    return crypto.randomBytes(32).toString('base64url');
}

PHP — Secure functions

// SECURE: password_hash with bcrypt (default) or Argon2
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
// Or with Argon2 (PHP 7.2+)
$hash = password_hash($password, PASSWORD_ARGON2ID);

// Verification
if (password_verify($password, $hash)) { /* authenticated */ }

// SECURE: AES-256-GCM
$key = random_bytes(32); // 256-bit key
$nonce = random_bytes(12);
$ciphertext = openssl_encrypt($data, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $nonce, $tag);

// SECURE: Cryptographic random
$token = bin2hex(random_bytes(32));

What Offensive360 detects

Our SAST engine identifies insecure cryptographic usage through pattern analysis and configuration inspection. We detect:

  • Deprecated hash algorithms — MD5, SHA-1, and other broken hashes used for security-sensitive operations (password hashing, data integrity, signatures)
  • Weak encryption algorithms — DES, 3DES, RC4, Blowfish, and other algorithms with known weaknesses or insufficient key sizes
  • Insecure cipher modes — ECB mode, CBC without authenticated encryption (padding oracle risk), and other weak modes
  • Hardcoded IVs/nonces — Initialization vectors or nonces that are static, zero-filled, or derived from non-random sources
  • Weak key sizes — RSA keys under 2048 bits, AES keys under 128 bits, and other insufficient key lengths
  • Insecure random number generatorsMath.random(), random.Random(), rand(), Random() used for security-sensitive purposes (tokens, keys, IVs)
  • Deprecated APIscreateCipher() in Node.js (uses weak key derivation), mcrypt_* in PHP, and other deprecated cryptographic functions
  • Missing authentication — Encryption without authentication (AES-CBC without HMAC), which is vulnerable to padding oracle attacks

Each finding identifies the specific algorithm or configuration weakness, the risk, and the recommended secure alternative.

Remediation guidance

  1. Use AES-256-GCM for encryption — AES-GCM provides both confidentiality and authenticity in a single operation. Use 256-bit keys and 96-bit random nonces. Never reuse a nonce with the same key.

  2. Use bcrypt, scrypt, or Argon2id for password hashing — These algorithms are designed to be slow (preventing brute force) and include automatic salting. Use a work factor of at least 12 for bcrypt.

  3. Use SHA-256 or SHA-3 for integrity — When you need a hash for data integrity (not passwords), use SHA-256, SHA-384, SHA-512, or SHA-3 family algorithms.

  4. Use HMAC-SHA256 for message authentication — When you need to verify both integrity and authenticity, use HMAC with SHA-256 or stronger.

  5. Use cryptographically secure random generators — Always use SecureRandom (Java), RandomNumberGenerator (C#), secrets (Python), crypto.randomBytes (Node.js), or random_bytes (PHP) for security-sensitive random values.

  6. Use RSA-2048+ or Ed25519 for asymmetric operations — RSA keys must be at least 2048 bits (4096 recommended). For new systems, prefer Ed25519 or ECDSA P-256.

  7. Keep cryptographic libraries updated — Use maintained libraries like libsodium, cryptography (Python), or platform-native crypto APIs. Avoid implementing cryptographic algorithms yourself.

False-positive considerations

Offensive360 may flag cryptographic usage that is acceptable in certain contexts:

  • MD5/SHA-1 for non-security purposes — Using MD5 for file checksums (not security), cache keys, or content deduplication where collision resistance is not a security requirement
  • Weak algorithms in legacy protocol compliance — Some protocols (e.g., certain LDAP configurations, legacy APIs) require specific algorithms for compatibility
  • Test data — Cryptographic operations in test code using intentionally weak parameters for speed
  • Hashing for display/logging — Using MD5 to generate shortened identifiers for display purposes

Our engine considers the context (variable names, function purpose, data sensitivity) to reduce false positives, but some cases may require manual review and suppression.

References

Author: Offensive360 Security Research
Last reviewed: March 1, 2026

Detect Use of Broken or Weak Cryptographic Algorithms in your code

Run Offensive360 SAST against your codebase to find this and hundreds of other vulnerabilities.