In 2023, a major cloud provider leaked sensitive data due to improper key storage. The impact? Millions of accounts compromised.
Cryptographic mistakes are expensive — not only financially, but also as irreparable damage to your brand's trust and reputation. One hardcoded key or reused nonce can lead to data breaches, lawsuits, fines, and a lifetime of being featured in “what not to do” security talks.
But hey, you’re here, so let’s make sure you’re doing everything right. Dive in to learn about the common cryptographic anti-patterns and how to avoid them.
Hardcoded credentials in the codebase are much more common than you think. A forgotten comment here, a testing variable there (sometimes intentional) can quickly become a nightmare if found by threat actors and can be abused to easily waltz right into your system.
This is really common in mobile applications and web applications where developers just minify/obfuscate the client-side code and think they’re good to go. Spoiler alert: obfuscation is security by obscurity, and therefore, not security!
So, what's a better approach?
Randomness is the foundational primitive for many cryptographic operations, from generating encryption keys, creating secure tokens to uniquely identifying users. Developers often fall into the trap of thinking they can create randomness from predictable inputs.
Knowing the stakes, it’s clear that failure in generating secure randomness jeopardizes the safety of your entire system. Take the following code snippet as an example:
At first glance, it might seem fine. You’re using the current time and a username to generate a password reset key. But the details matter:
Besides insecure design like the example above, developers often unknowingly use insecure defaults in programming languages such as Math.random() which are known to not be cryptographically secure.
Well, it depends. SecureRandom is a cryptographically secure randomness generator but, if it’s not configured properly, it will not be able to provide secure randomness.
A common anti-pattern when using SecureRandom is manually setting the seed with setSeed(...). Sure, the documentation says you can seed it, but that doesn’t mean you should.
Here’s why:
Let’s talk about cryptographic algorithms that belong in a museum, not your codebase. MD5, SHA-1, and DES are the dinosaurs of cryptography, once mighty, now extinct (or at least, they should be).
Yet, like a bad sequel, they keep showing up in legacy systems, third-party libraries, and even new projects where developers copy-paste code from StackOverflow circa 2012.
Why it’s a problem:
How to fix it:
Using AES? Great! But if you’re using it in ECB mode, you might as well paint your plaintext on a billboard. AES has several modes of operation. In ECB (Electronic Codebook) mode, it encrypts identical plaintext blocks into identical ciphertext blocks, leaking patterns like a sieve.
The picture you see above is more than just varying portraits of a Tux penguin. This is what you get when you encrypt the original image with AES ECB. Yeah, not the best “encryption”, is it?
Why it’s a problem:
How to fix it:
Nonce stands for “number used once.” Emphasis on once. Nonces are critical in algorithms like AES-GCM, ChaCha20-Poly1305, and ECDSA. Reusing them catastrophically undermines security.
What could go wrong:
How to fix it:
Your encryption is only as strong as your keys. However, mismanaging cryptographic keys such as storing them in plaintext configuration files, sharing them over insecure channels, or hardcoding them in Docker images renders even the most robust algorithms useless.
Why it’s a problem:
The 2021 Codecov Breach
Attackers extracted AWS credentials from a Codecov script, allowing them to access encrypted customer data. Proper key isolation and short-lived credentials could have mitigated this.
How to fix it:
Encrypting data in transit is table stakes. But unencrypted data at rest? That’s a threat actor’s treasure chest.
You need to make sure that your data at rest is both logically encrypted, for example, the keystores are encrypted as well as the hardware is encrypted with the use of HSM.
Why it’s a problem:
How to fix it:
SSL/TLS is the bedrock of secure internet communication, but misconfigurations can render it ineffective. Modern threats like protocol downgrade attacks, weak cipher suites, and certificate mismanagement expose systems to eavesdropping, data tampering, and impersonation.
What could go wrong:
How to fix it:
Unless you’re a cryptographer with a PhD and a penchant for pain, don’t roll your own cryptography. Just don’t.
Why it’s bad:
How to fix it:
Well yes, but there’s more. Just adding a good cryptography library to the project dependencies isn’t where it ends. You have to invoke the right functions from the library in the right mode at the right places. It’s not too rare that the right modules and right algorithms have been used but in the wrong configuration, say, a NULL nonce here, an insecure size there. That stuff happens all the time.
Cryptographic keys serve distinct purposes. Conflating them introduces systemic risks. When you reuse a key for encryption that you use for authorization you explicitly introduce a scenario where an attacker with access to authorization key will also be able to freely decrypt and tamper the encrypted data.
How to fix it:
Cryptographic vulnerabilities are often discovered too late: after a breach, during a pentest, or worse, in the hands of an attacker.
The key to avoiding these pitfalls lies in shifting security left, embedding robust practices into every stage of the Software Development Lifecycle. By catching and mitigating cryptography issues early, you can save time, money, and your reputation.
For teams looking to master secure development practices, AppSecEngineer offers enterprise training that keeps teams ahead of emerging threats and equips them with the skills to orchestrate a secure SDLC.
Some of the most frequent cryptographic errors include: • Hardcoding secrets in source code. • Using weak or outdated algorithms like MD5, SHA-1, or DES. • Reusing nonces in AES-GCM or ECDSA, which can expose encryption keys. • Using predictable random number generation (e.g., Math.random() instead of SecureRandom). • Misconfiguring SSL/TLS, leading to weak encryption or protocol downgrade attacks.
Hardcoded keys can be easily extracted from code repositories, mobile apps, or reverse-engineered binaries. If an attacker gains access, they can decrypt sensitive data or impersonate legitimate users. Fix: Use environment variables or secrets management tools like AWS Secrets Manager, HashiCorp Vault, or Azure Key Vault.
A nonce (number used once) should never be reused because: • In AES-GCM, nonce reuse leaks authentication keys, allowing message forgery. • In ECDSA, reusing a nonce can expose private signing keys (e.g., PlayStation 3 hack). • Fix: Always generate nonces using a cryptographically secure random number generator (CSPRNG) or use counter-based nonces with safeguards.
MD5 and SHA-1 are vulnerable to collision attacks, where different inputs can produce the same hash. This allows attackers to forge data integrity checks. Fix: Use SHA-256, SHA-3, or Argon2/PBKDF2 for password hashing.
AES-ECB (Electronic Codebook) mode encrypts identical plaintext blocks into identical ciphertext blocks, leaking patterns in encrypted data. Fix: Use AES-GCM (preferred) or AES-CBC with HMAC for authenticated encryption.
Weak randomness can make keys, session tokens, and cryptographic nonces predictable. For example: • Math.random() in JavaScript is not cryptographically secure. • Using predictable seeds (System.currentTimeMillis()) in SecureRandom reduces entropy and makes keys guessable. Fix: Use SecureRandom (Java), crypto.getRandomValues() (JavaScript), or /dev/urandom (Linux). Never manually seed CSPRNGs.
Using the same key for encryption and signing introduces systemic vulnerabilities. If an attacker gains access to one, they can decrypt or forge messages. Fix: Use separate encryption keys (AES-GCM, XChaCha20) and signing keys (Ed25519, HMAC-SHA-256).
Avoid storing keys in plaintext configuration files, source code, or logs. Best practices: • Use cloud KMS (AWS KMS, Google Cloud KMS, HashiCorp Vault) for secure storage. • Implement key rotation every 90 days. • Apply least privilege access (IAM roles, ACLs) to limit key exposure.
Common SSL/TLS mistakes include: • Allowing weak protocols (SSL 2.0, SSL 3.0, TLS 1.0/1.1). • Using weak ciphers (RC4, 3DES, NULL ciphers). • Disabling certificate validation, making systems vulnerable to man-in-the-middle attacks. Fix: • Enforce TLS 1.2 or 1.3. • Use ECDHE for key exchange and AES-GCM for encryption. • Verify SSL/TLS certificates properly and enable OCSP stapling.
Cryptography is highly complex, and even minor mistakes can introduce severe vulnerabilities. Custom algorithms or protocols lack peer review and cryptanalysis, making them unsafe. Fix: Always use well-vetted cryptographic libraries like OpenSSL, libsodium, or Bouncy Castle instead of writing your own encryption functions.