Common Cryptography Mistakes
Using cryptography does not guarantee security. Incorrect implementations create a false sense of protection while leaving data exposed. These are the most common mistakes.
Mistake 1: ECB Mode (Electronic Codebook)
In ECB mode, each data block is encrypted independently using the same key. Identical blocks produce identical ciphertext blocks.
Data: [BLOCK_A][BLOCK_A][BLOCK_B]
ECB: [CIPHER_A][CIPHER_A][CIPHER_B]
^--- visible pattern ---^
This leaks the structure of the original data. The classic example: encrypting a bitmap image with AES-ECB — the silhouette remains visible.
Fix: use CBC with a random IV, or better yet, AES-GCM.
from Crypto.Cipher import AES
# Wrong
cipher = AES.new(key, AES.MODE_ECB)
# Correct
import os
iv = os.urandom(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
Mistake 2: Reused IV
CBC and CTR require a unique IV (initialization vector) per message. Reusing the same IV with the same key breaks confidentiality.
In CTR mode, two ciphertexts with the same IV and key allow XOR between them, revealing data.
C1 = M1 ⊕ keystream
C2 = M2 ⊕ keystream (same IV → same keystream)
C1 ⊕ C2 = M1 ⊕ M2 ← message structure is exposed
Fix: generate a random IV with os.urandom(16) for each encryption. Store the IV alongside the ciphertext (it does not need to be secret).
Mistake 3: Hardcoded Key
# Never do this
KEY = b"my_fixed_key_1234567890abcdef00"
def encrypt(data):
cipher = AES.new(KEY, AES.MODE_GCM)
return cipher.encrypt(data)
A key in source code leaks through git repositories, logs, and decompiled binaries. A compromised key compromises all historical data.
Fix: load the key from an environment variable or a secrets service.
import os
KEY = os.environ["APP_ENCRYPTION_KEY"].encode()
# Or use AWS Secrets Manager, HashiCorp Vault, etc.
Mistake 4: Encrypting Without Authenticating
AES-CBC encrypts but does not guarantee integrity. An attacker can alter the ciphertext and decryption will produce garbage or, worse, a controlled value (bit-flipping attack).
Original ciphertext: [...][TARGET_BLOCK][...]
Attacker flips a bit in the previous block → alters TARGET_BLOCK after decryption
Fix: use authenticated modes — AES-GCM or AES-CCM. They combine encryption and authentication in one operation.
from Crypto.Cipher import AES
import os
key = os.urandom(32)
nonce = os.urandom(16)
cipher = AES.new(key, AES.MODE_GCM, nonce=nonce)
ciphertext, tag = cipher.encrypt_and_digest(b"sensitive data")
# tag verifies integrity during decryption
Mistake 5: Weak Random Number Generator
Do not use Python’s random or C’s rand() to generate keys or IVs. These generators are pseudorandom and not cryptographically secure.
# Wrong
import random
key = random.randbytes(32)
# Correct
import os
key = os.urandom(32)
Summary
| Mistake | Consequence | Fix |
|---|---|---|
| ECB mode | Leaks patterns | Use GCM or CBC with IV |
| Reused IV | Breaks confidentiality | Random IV per message |
| Hardcoded key | Exposure via source code | Env variable / vault |
| No authentication | Bit-flipping attack | Use AES-GCM |
| Weak PRNG | Predictable key | Use os.urandom() |