Algorithm selection β the short answer
Use case Recommended Avoid ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Symmetric encryption AES-256-GCM DES, 3DES, RC4, AES-ECB Asymmetric encryption RSA-2048+ / ECC P-256 RSA-1024, DSA-1024 Key exchange ECDHE, X25519 Static RSA key exchange Digital signatures Ed25519, ECDSA P-256 RSA-PKCS1v1.5 Password hashing Argon2id, bcrypt MD5, SHA1, SHA256 (raw) General hashing SHA-256, SHA-3 MD5, SHA1 TLS TLS 1.3 / TLS 1.2+ TLS 1.0, TLS 1.1, SSLv3 MAC / AEAD HMAC-SHA256, AES-GCM HMAC-MD5, unauthenticated modes
Rule #1: Do not invent your own cryptography. Do not implement algorithms from scratch. Use well-audited, actively maintained libraries. The list of teams that have successfully invented new cryptographic primitives is very short and does not include your engineering team.
Symmetric encryption β AES
AES (Advanced Encryption Standard) is the industry standard for symmetric encryption. It is fast, hardware-accelerated on modern CPUs, and cryptographically sound. The choice of mode and key length determines actual security.
Key length
- AES-256: The standard for sensitive data. The 256-bit key provides a 128-bit security level against quantum attacks (Grover's algorithm halves the effective key length).
- AES-128: Acceptable for non-quantum-sensitive data today. 128-bit key provides 64-bit security against quantum adversaries β adequate for most current use cases but not future-proof.
Mode of operation
- AES-GCM (Galois/Counter Mode): The recommended mode. Provides both encryption and authentication (AEAD β Authenticated Encryption with Associated Data). If ciphertext is tampered with, decryption fails and the tampering is detected.
- AES-CBC: Acceptable if combined with a separate HMAC for authentication (Encrypt-then-MAC pattern). Never use CBC without authentication β vulnerable to padding oracle attacks.
- AES-ECB: Never use. ECB mode encrypts identical plaintext blocks to identical ciphertext blocks, leaking data structure (the "ECB penguin" problem).
from cryptography.hazmat.primitives.ciphers.aead import AESGCM import os # Generate a random 256-bit key key = os.urandom(32) # 32 bytes = 256 bits # Generate a random 96-bit nonce (12 bytes β standard for GCM) nonce = os.urandom(12) aesgcm = AESGCM(key) # Encrypt with optional associated data (authenticated but not encrypted) plaintext = b"sensitive data" associated_data = b"header metadata" ciphertext = aesgcm.encrypt(nonce, plaintext, associated_data) # Decrypt β raises InvalidTag if tampered plaintext = aesgcm.decrypt(nonce, ciphertext, associated_data)
Never reuse a nonce with the same key in GCM. Nonce reuse in GCM completely compromises the encryption β both the key and all messages encrypted with that key can be recovered. Generate a new random nonce for every encryption operation.
Asymmetric encryption β RSA and ECC
Asymmetric cryptography uses a key pair: a public key for encryption/verification and a private key for decryption/signing. It is slower than symmetric encryption and unsuitable for encrypting large data volumes directly β it is typically used for key exchange or signatures.
RSA
- Minimum key length: 2048 bits. RSA-1024 is broken β do not use it.
- Recommended: RSA-4096 for long-lived keys protecting high-value data. RSA-2048 remains acceptable for most use cases in 2026.
- Use OAEP padding for encryption. RSA-PKCS1v1.5 encryption padding is vulnerable to Bleichenbacher attacks. Use RSAES-OAEP.
- Use PSS padding for signatures. RSA-PKCS1v1.5 signatures are generally safe but PSS (Probabilistic Signature Scheme) is preferred.
Elliptic Curve Cryptography (ECC)
ECC provides equivalent security to RSA at much smaller key sizes, making it faster and preferred for new systems.
- P-256 (NIST curve): Widely supported, equivalent to RSA-3072 security. Standard choice for TLS and JWT (ES256).
- Ed25519 (Edwards curve): Preferred for digital signatures. Faster than ECDSA, immune to weak random number generation affecting ECDSA signing, and simpler to implement correctly.
- X25519: Preferred for key exchange (Diffie-Hellman). Used in modern TLS 1.3.
Password hashing β not just any hash
General-purpose hash functions (SHA-256, SHA-512) are designed to be fast β which makes them terrible for password storage. An attacker with GPU hardware can compute billions of SHA-256 hashes per second. Password hashing algorithms are specifically designed to be slow and memory-intensive.
- Argon2id: The current recommendation. Winner of the Password Hashing Competition (2015). Resistant to both GPU and side-channel attacks. Configure: minimum 64 MB memory, 3 iterations, 4 parallelism for interactive logins.
- bcrypt: The widely supported fallback. Use a work factor of 12 or higher (2026). Limit input to 72 bytes β longer passwords are silently truncated by most bcrypt implementations.
- scrypt: Memory-hard algorithm. Good choice if Argon2 is unavailable. N=32768, r=8, p=1 as minimum parameters.
- MD5, SHA1, SHA256 for passwords: Never. These are wrong for this use case regardless of salting.
from argon2 import PasswordHasher ph = PasswordHasher( time_cost=3, # iterations memory_cost=65536, # 64 MB parallelism=4, ) # Hash a password hash = ph.hash("user_password") # Verify β raises exceptions on wrong password or outdated params try: ph.verify(hash, "user_password") if ph.check_needs_rehash(hash): hash = ph.hash("user_password") # rehash with updated params except Exception: # Verification failed pass
TLS configuration
- Minimum: TLS 1.2. Preferred: TLS 1.3. TLS 1.0 and 1.1 are deprecated by all major browser vendors and IETF (RFC 8996). SSLv3 and earlier are critically broken.
- Enable forward secrecy. Use ECDHE or DHE cipher suites so that a future compromise of the server's private key does not decrypt past sessions.
- TLS 1.3 cipher suites are fixed and strong. TLS 1.3 removed the cipher negotiation complexity β there are only five cipher suites, all AEAD and all providing forward secrecy.
- Disable weak cipher suites. RC4, DES, 3DES, EXPORT, NULL, and anonymous cipher suites must be explicitly disabled.
- Set HSTS with a long max-age. HTTP Strict Transport Security prevents protocol downgrade attacks. Minimum max-age: 31536000 (one year). Include
includeSubDomainsandpreloadwhen ready.
Encryption key management
- Generate keys with a cryptographically secure random number generator. Never derive keys from predictable values (timestamps, process IDs, sequential numbers).
- Store encryption keys separately from encrypted data. A key stored alongside the data it protects provides no security.
- Use a KMS for key storage. AWS KMS, GCP Cloud KMS, Azure Key Vault, and HashiCorp Vault are designed for secure key storage with audit logging and access control.
- Rotate keys on a schedule. Use envelope encryption: encrypt data with a data encryption key (DEK), encrypt the DEK with a key encryption key (KEK). Rotating the KEK re-wraps all DEKs without re-encrypting all data.
- Never log or expose keys. Treat encryption keys like passwords β they should never appear in logs, error messages, or API responses.
Common cryptographic mistakes
- Using ECB mode: Leaks data structure. Always use GCM or CBC+HMAC.
- Hardcoding encryption keys: A key in source code is not a key β it is a constant. Anyone with repo access has the "key".
- Reusing IVs/nonces: Catastrophic for GCM. Use a random nonce for every encryption operation.
- Rolling your own crypto: Implementing AES from scratch, designing custom protocols, or modifying standard algorithms. Use established libraries.
- Encrypting without authenticating: Unauthenticated ciphertext can be modified without detection. Use AEAD modes or add a MAC.
- Using MD5 or SHA1 for anything security-relevant: MD5 is broken for collision resistance. SHA1 is deprecated. Use SHA-256 minimum.
- Comparing secrets with
==: Timing attacks allow an attacker to determine how many bytes matched. Use constant-time comparison for any secret comparison.
Cryptographic failures are OWASP A02:2021. They moved up from third to second in the 2021 OWASP Top 10. The most common failure mode is not weak encryption β it is skipping encryption entirely, or using the right algorithm incorrectly.
Recommended cryptographic libraries
Language Library Notes ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ Python cryptography (pyca) Full-featured, actively audited Python argon2-cffi Argon2 password hashing Node.js built-in crypto module For standard ops; libsodium for modern crypto Node.js libsodium-wrappers Recommended for new projects Go stdlib crypto/* Well-audited, use golang.org/x/crypto for extras Java Bouncy Castle / JCA Prefer JCA + provider; avoid raw Bouncy Castle Rust RustCrypto / ring ring preferred for audited primitives .NET System.Security.Cryptography Built-in, well-maintained
The libsodium rule: If you are starting a new project and do not have specific interoperability requirements, libsodium (or its language bindings) makes strong defaults the only option. It picks the algorithms for you β all of which are modern and secure.
Detect weak cryptography in your codebase
AquilaX SAST identifies use of MD5, SHA1, DES, ECB mode, hardcoded keys, and other cryptographic weaknesses β across every file in your repository.
Scan for cryptographic flaws β