ASVS 5.0 V11.4.4 (Level 2) requires that when a secret key is derived from a password, the application uses an approved key derivation function with key stretching parameters tuned so brute force against the resulting key stays impractical.
Why it matters
A user's passphrase is low entropy. If you take SHA-256 of it and use that as an AES key, an attacker with the ciphertext can test billions of guesses per second. A slow KDF raises the cost of each guess.
This is different from password storage (V11.4.2): here the output is an encryption key, not a stored verifier.
How to implement it
Use Argon2id, scrypt or PBKDF2 with a random salt per derivation, and store the salt and parameters with the ciphertext so they can be raised later.
import os
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
salt = os.urandom(16)
kdf = Scrypt(salt=salt, length=32, n=2**15, r=8, p=1)
key = kdf.derive(passphrase.encode())
If you use PBKDF2, use HMAC SHA-256 and a high iteration count; check current OWASP and NIST guidance, since numbers rise over time.
How to test it
Find every place a key comes from a passphrase. Confirm a KDF call exists, the salt is random and at least 16 bytes, and the parameters are stored with the data. Time the derivation: it should take noticeable time, on the order of a tenth of a second or more on your hardware.
Common mistakes
- A single hash round as a KDF
- A fixed or empty salt
- Hard coding parameters so you cannot upgrade them
Quick check
Where should salt and parameters be stored?
With the encrypted data, so the key can be re derived and parameters raised later.
Sources
- OWASP ASVS
- OWASP Password Storage Cheat Sheet