Weak Cryptography
detect, understand, remediate
Weak cryptography covers any application-layer cryptographic choice that fails to deliver its security guarantee: MD5 or SHA1 used for password hashing or signatures, AES in ECB mode, predictable initialisation vectors, hardcoded keys, weak key lengths, JWTs accepting the none algorithm, and Math.random used as a security primitive. The data may be encrypted on paper, but the protection is illusory once an attacker recognises the weakness.
No credit card required. Free plan available forever.
What is weak cryptography?
Weak cryptography (CWE-327, CWE-326, CWE-330) is the use of a broken algorithm, a deprecated parameter choice, or an insecure construction in code that is meant to provide confidentiality, integrity, or authentication. The application encrypts, signs, or hashes something, so on paper the protection is in place, but the choice of primitive, mode, key length, or randomness is too weak to deliver the guarantee the system was supposed to offer. The encryption is real; the security is not.
OWASP renamed this category from Sensitive Data Exposure to A02:2021 Cryptographic Failures precisely to focus on the root cause rather than the downstream leak. The leaked data is the symptom. The MD5 password hash, the AES-ECB ciphertext, the predictable IV, the JWT verifier that accepts the none algorithm, the seed taken from Math.random: those are the bugs.
Weak cryptography sits next to but is distinct from TLS/SSL misconfiguration, which covers the transport layer, and hardcoded secrets, which covers key handling rather than primitive choice. This page focuses on the application-layer choices: which algorithm, which mode, which parameters, and which source of randomness the code actually uses when it calls the crypto API.
How weak cryptography fails in practice
Identify the primitive
The pentester reviews source, traffic, and stored artefacts to spot which algorithm is used: MD5, SHA1, DES, RC4, AES-ECB, RSA-1024, SHA1WithRSA, the JWT alg field, or a custom roll-your-own scheme.
Confirm the weakness applies here
Not every old primitive is automatically exploitable. SHA1 inside HMAC differs from SHA1 as a password hash. The tester confirms the context: signing, hashing, encryption, KDF, or session token, and matches that to the known break.
Build the attack from the break
For MD5 password hashes the attack is offline brute force or rainbow tables. For ECB the attack is block-pattern recovery. For predictable IVs the attack is chosen-plaintext or replay. For JWT alg=none the attack is forged tokens with no key.
Demonstrate impact
The proof of concept is recovered plaintext, a forged signature, an authenticated session as a different user, or duplicate ciphertext blocks that prove the structure of the underlying data.
The most common weak-crypto findings on a pentest
The patterns below cover the majority of cryptographic failures that show up in web, mobile, and API engagements. Each sits at the intersection of a familiar API call and a primitive choice that has been understood as inadequate for years.
MD5 or SHA1 used for password hashing
Fast hashes were built for integrity checks, not for password storage. Modern attackers brute force MD5 at billions of guesses per second on commodity hardware. Replace with bcrypt, scrypt, or Argon2id with a tuned cost factor.
AES in ECB mode
ECB encrypts identical plaintext blocks to identical ciphertext blocks, so structure leaks even when the key never does. The classic signal is two encrypted images that visibly preserve the original silhouette. Use AES-GCM or AES-CBC with a random IV per message.
Predictable or reused initialisation vectors
CBC and CTR mode lose their guarantees if the IV is constant, derived from the plaintext, or generated from a non-cryptographic random source. Reused nonces break GCM in particular and let an attacker recover the authentication key.
Math.random and equivalents used for security tokens
Math.random in JavaScript, java.util.Random in Java, rand() in C, and Random in .NET are not cryptographically secure. Tokens, password reset codes, IVs, salts, and session identifiers must come from crypto.getRandomValues, SecureRandom, /dev/urandom, or RandomNumberGenerator.Create.
JWT accepting the none algorithm or weak HS256 secrets
Verifiers that honour the alg field from the token (including alg=none) can be tricked into accepting unsigned tokens. HS256 with a short or guessable secret falls to offline brute force in seconds. See the full pattern on the JWT vulnerabilities page.
Short key lengths
RSA below 2048 bits, ECC below 224 bits, AES-128 in long-lived data-at-rest stores, and DES or 3DES anywhere are all flagged on a modern pentest. Industry guidance has been clear on these thresholds for over a decade.
Self-rolled cryptographic protocols
A custom encryption scheme, a hand-built MAC, or a session token that mixes timestamp, user id, and a hash without authenticated encryption rarely holds up to review. The fix is almost always to swap the construction for libsodium, Tink, or a vetted equivalent.
Missing authentication on encrypted data
Encryption without a MAC (CBC mode without HMAC, AES-CTR without an integrity check) leaves ciphertext malleable. Padding oracles, bit-flipping attacks, and chosen-ciphertext attacks all start here. Use AES-GCM, XChaCha20-Poly1305, or AES-CBC plus HMAC-SHA256 with separate keys.
A worked example: ECB mode in a session cookie
A common shape on engagements is a session cookie that encrypts a JSON blob containing the user id, the role, and the expiry. The server uses AES-128, but in ECB mode and without a MAC.
// Vulnerable: AES-ECB, no authentication, predictable structure
const cipher = createCipheriv('aes-128-ecb', key, null);
const session = JSON.stringify({ uid: user.id, role: user.role, exp });
const cookie = Buffer.concat([cipher.update(session), cipher.final()]);
res.cookie('session', cookie.toString('base64'));The pentester logs in twice, once as a low-privilege user and once as an admin, and observes that the byte ranges encoding the role field render as identical 16-byte ciphertext blocks across users with the same role. They cut the admin block out of the admin cookie, paste it into the low-privilege cookie at the right offset, and replay the request. The server decrypts a structurally valid session JSON and treats the request as the admin user, because nothing checked the integrity of the ciphertext. The fix is AES-GCM with a per-message nonce and an associated-data binding, plus migrating to a signed token format for anything the client should not be able to forge.
On the SecPortal authenticated scanner, this kind of cookie shape shows up first as a header anomaly (no Set-Cookie HttpOnly, no integrity envelope, predictable length) and is then promoted to a manual finding once the tester confirms the block-cut works. Most ECB findings start with a static analysis hit; the cookie is the proof.
The same missing-authentication shape powers a wider attack class. CBC without a MAC, paired with a server that distinguishes a padding error from any other error, becomes a padding oracle that decrypts plaintext byte by byte and forges chosen ciphertext, all without recovering the key. The remediation overlap is the same: switch to authenticated encryption, or pair an unauthenticated mode with a separate HMAC verified before the inner decryption runs.
How to detect weak cryptography
Automated detection
- SecPortal's code scanning runs Semgrep with rulesets covering OWASP Top 10 and security-audit checks. The rules flag MD5 and SHA1 in security-sensitive contexts, AES-ECB usage, fixed and zero IVs, Math.random and java.util.Random for tokens, and short RSA key generation.
- The external scanner inspects the TLS handshake and certificate chain. Weak signature algorithms (SHA1WithRSA), short RSA keys, deprecated protocols, and weak cipher suites are reported with severity and remediation guidance.
- The authenticated scanner inspects JWT structure, flags tokens that accept alg=none, weak HS256 secrets, and key confusion patterns where a public key is used as a symmetric secret.
- SCA dependency scanning identifies vulnerable cryptographic libraries (older versions of bouncy castle, jose, jsonwebtoken) where known weaknesses have been published as CVEs.
Manual testing
- Inventory every place the application encrypts, signs, hashes, or generates a token, then check the algorithm, the mode, the IV strategy, the key length, and the source of randomness against current guidance (NIST SP 800-131A, OWASP ASVS V6, BSI TR-02102).
- Look for ciphertexts that contain identical 16-byte blocks across different sessions or users. That is the signature of ECB mode and is visible without breaking any keys.
- Sample 100 password reset tokens and run them through dieharder, ent, or a NIST randomness suite. A non-random distribution is strong evidence that the token comes from a non-cryptographic RNG.
- For JWTs, change the alg header to none, drop the signature, and replay the request. Try the public-to-symmetric key confusion (RS256 token resigned as HS256 with the public key as the secret).
- For password storage, request a database dump in scope, hash a known plaintext with each candidate algorithm (MD5, SHA1, bcrypt, scrypt, argon2), and match the format to confirm what the application is doing.
How to fix weak cryptography
Use a vetted library, not a roll-your-own scheme
libsodium (NaCl), Google Tink, AWS Encryption SDK, or the platform crypto module with explicit modes and parameters cover almost every common need. Hand-built primitives, custom MACs, and custom KDFs are flagged regardless of how they look on paper.
Pick algorithms that match the threat model
For password hashing use Argon2id (preferred), scrypt, or bcrypt with a tuned cost factor. For symmetric encryption use AES-GCM or XChaCha20-Poly1305 with a per-message nonce. For asymmetric use RSA-2048 or higher, or Ed25519/X25519. For random tokens use the platform CSPRNG.
Always pair encryption with authentication
Encryption alone protects confidentiality but not integrity. Use authenticated encryption (AES-GCM, ChaCha20-Poly1305) or pair an unauthenticated mode with a separate HMAC under a separate key. Bind associated data such as user id and expiry into the AAD so a ciphertext cannot be replayed against another user.
Generate cryptographic material from a CSPRNG
Keys, IVs, nonces, salts, password reset tokens, and session identifiers must come from crypto.getRandomValues (browser/Node), java.security.SecureRandom (JVM), secrets.token_bytes (Python), or System.Security.Cryptography.RandomNumberGenerator (.NET). Math.random and language-default RNGs are flagged as findings.
Reject the JWT none algorithm and pin the verifier
Configure the JWT library to accept only the algorithm the application issued (do not let the token tell the verifier what algorithm to use). Ensure the verifier uses a public key for asymmetric algorithms and a strong shared secret for symmetric ones. Cross-reference the full set of patterns on the JWT vulnerabilities page.
Plan for crypto-agility
Algorithms get deprecated. Keep the algorithm and the key identifier alongside the ciphertext or hash so the system can rotate without dropping data. A version prefix on a hash or a kid header on a token costs almost nothing and saves a re-platforming exercise when the next break lands.
Migrate sensitive data after the fix
Switching the algorithm in code does not retroactively rehash old passwords or re-encrypt old records. Rehash on next login, schedule a background re-encryption job for stored data, and rotate keys after the migration. The fix is incomplete until the legacy ciphertexts and hashes are gone.
Reporting a weak-cryptography finding
Weak-crypto findings are easy to dispute when the report stops at "application uses MD5" without showing the impact. Engineering pushes back that MD5 is "just for cache keys" or "a legacy field that nobody reads", and the finding stalls. The strongest reports name the exact location (file, line, function), the primitive in use, the operation it supports (password hashing, session signing, token generation, encryption at rest), and the realistic attack model that follows from that combination.
On a SecPortal engagement, the finding sits on the engagement record with the affected component, the CVSS 3.1 vector calibrated to the actual data the primitive protects, the CWE-327 (or CWE-326, CWE-330) mapping, the source-code or proof-of-concept evidence, and the remediation guidance from this page. Pentest engagement records keep the finding linked to the original commit and the retest verification, so the close-out conversation references the actual algorithm change rather than a vague "crypto fixed" ticket. The finding triage workflow covers how to separate scanner-derived flags (an MD5 import in source) from manually validated findings (an MD5 password hash in the live database) so the report differentiates rule hits from confirmed exploits.
Compliance impact
OWASP Top 10
A02:2021 Cryptographic Failures
OWASP ASVS
V6 Stored Cryptography
PCI DSS
Req. 3 and 4 Strong Cryptography
ISO 27001
A.8.24 Use of Cryptography
HIPAA
Security Rule Encryption Standard
GDPR
Article 32 Pseudonymisation and Encryption
SOC 2
CC6.1 Logical Access and Encryption
NIST 800-53
SC-13 Cryptographic Protection
A pentester checklist for weak cryptography
The list below is the minimum coverage a tester should walk before declaring a target's cryptographic surface acceptable. Each item maps to a specific finding shape, with a CVSS profile that reflects the data the primitive protects.
- Inventory every cryptographic call site in source: hash functions, symmetric and asymmetric encryption, signing, KDFs, random number generation, and JWT issuance and verification.
- Confirm password storage uses Argon2id, scrypt, or bcrypt with a tuned cost. Hashes that look like 32 hex characters or 40 hex characters are MD5 and SHA1; that is a finding.
- Inspect every block-cipher invocation for the mode and IV. ECB anywhere is a finding. CBC or CTR with a fixed or reused IV is a finding. GCM with a nonce reuse is a finding.
- Test JWT verifiers for alg=none acceptance, key confusion (RS256 to HS256), and weak HS256 secrets via offline brute force on intercepted tokens.
- Sample security-sensitive tokens (password resets, session ids, CSRF tokens) and verify they come from a CSPRNG. Predictable distributions, sequential ids, and low entropy are findings.
- Audit the TLS configuration for weak signature algorithms (SHA1WithRSA), short keys (RSA-1024), and deprecated protocols (TLS 1.0, TLS 1.1) using the external scanner or a tool like sslyze.
- Record the CVSS vector calibrated to the data being protected, the CWE mapping (CWE-327 for broken algorithm, CWE-326 for inadequate strength, CWE-330 for use of insufficiently random values), the source-code or proof-of-concept evidence, and the migration guidance so the finding is reproducible at retest.
Catch weak cryptography before it ships
SecPortal SAST flags MD5, SHA1, ECB mode, and insecure RNG patterns in source, the authenticated scanner inspects JWT signing algorithms, and the external scanner audits TLS configuration. Connect a repo and start scanning for free.
No credit card required. Free plan available forever.