$ cat owasp/a04.md
A04:2025
Cryptographic Failures
Sensitive data is stored or sent without proper protection - or "protected" with something guessable.
## 1 · why it happens
Most crypto bugs are not broken algorithms. They are developers using the wrong tool: Math.random() for reset tokens, MD5 for passwords, a JWT secret like "secret", or keys committed next to the code they protect.
Once an attacker can predict a token or crack a hash offline, every other control in the app is bypassed.
# JavaScript
const resetToken = Math.random().toString(36).slice(2); // predictable
const hash = crypto.createHash('md5').update(password).digest('hex');
jwt.sign(payload, 'secret');const resetToken = crypto.randomBytes(32).toString('hex');
const hash = await bcrypt.hash(password, 12); // or argon2
jwt.sign(payload, process.env.JWT_SECRET, { algorithm: 'HS256', expiresIn: '15m' });
// JWT_SECRET: 32+ random bytes, from a secret manager# C#
var token = new Random().Next().ToString(); // predictable
using var md5 = MD5.Create();
var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(password));var token = Convert.ToHexString(RandomNumberGenerator.GetBytes(32));
var hasher = new PasswordHasher<User>(); // PBKDF2 with salt
var hash = hasher.HashPassword(user, password);## 2 · how attackers find it from outside
| public source | what it tells them |
|---|---|
| Public GitHub repos & history | Hard-coded JWT secrets, API keys and private keys - even if deleted in a later commit. |
| JavaScript bundles | Client-side "encryption" keys and API keys shipped to every browser. |
| Certificate & TLS checks (crt.name / SSL Labs) | Old protocols and certificates on forgotten subdomains. |
→ all OSINT sources · run them on yourself
## 3 · how it's abused
⚠ Recon your own assets, or targets you have written permission to test. Passive OSINT reads public data, but using it to target someone else's systems can still be illegal or against their terms. Examples on this site use example.com placeholders.
hashcat -a 0 -m 16500 jwt.txt wordlist.txtCracks the secret of a captured JWT offline. A weak secret means the attacker can sign their own tokens.
{"alg":"none"}Some libraries accepted unsigned tokens when the header said "none" (CVE-2015-9235).
Payload examples adapted from PayloadsAllTheThings (MIT, © Swissky). Full lists:Insecure Randomness · JSON Web Token · API Key Leaks
## 4 · fix
- Use a CSPRNG for every token: crypto.randomBytes / RandomNumberGenerator.
- Hash passwords with bcrypt, scrypt, Argon2 or PBKDF2 - never MD5/SHA1.
- Long random secrets from a secret manager, never in the repo.
- Pin the expected JWT algorithm when verifying.
- TLS everywhere, HSTS on, no sensitive data in URLs.
## 5 · detect
Gitleaks
Find secrets in your git history before someone else does.
fix at sourceTruffleHog
Find secrets and check whether they still work.
fix at sourceBearer
Static analysis that follows sensitive data through your code.
Official description: OWASP A04:2025 Cryptographic Failures