← Back to all articles

AES in Practice: Modes, IVs and Authenticated Encryption

EncryptionPitfalls

AES is only a block cipher

AES transforms 16 bytes at a time. Encrypting arbitrary data requires a mode of operation (GCM, CBC…) that chains blocks together. Saying "I use AES" is therefore incomplete — the mode and parameters matter just as much.

Mode comparison

ModeAuthenticatedIV requirementRecommendation
GCMYes (AEAD)12 random bytes, never reuseFirst choice: secrecy plus tamper detection
CBCNo16 random bytesRequires a separate HMAC
ECBNoNoneAvoid: identical plaintext blocks leak patterns

Why the IV must never repeat

GCM derives its keystream from the IV and a counter. Reusing an IV under the same key overlaps two keystreams — an attacker XORs the ciphertexts to recover plaintext — and it also breaks the authentication tag. In practice, generate a fresh random IV per encryption and store it next to the ciphertext (the IV does not need to be secret).

A correct encryption flow

  1. Generate 12 random bytes as the IV;
  2. Encrypt with AES-GCM, producing ciphertext and an authentication tag;
  3. Store or transmit IV + ciphertext + tag together (do not encrypt the IV);
  4. On decryption, always verify the tag and fail hard — never use partial plaintext.

Where does the key come from

A user password is not a key — its entropy is far below 128 bits. Derive a key with a KDF (PBKDF2, scrypt, Argon2) using a strong work factor, or generate a random key with a CSPRNG and solve key storage separately.

Common questions

Is an IV the same as a salt? No: the IV randomises the encryption so the same plaintext encrypts differently; the salt randomises key derivation so the same password yields a different key. Do I still need to hash after encrypting? AEAD modes already provide integrity; only CBC needs an additional MAC.

A storage format that ages well

// Keep the IV and tag next to the ciphertext, self-describing
{
  "v": 1,              // version, so you can migrate later
  "alg": "AES-GCM",
  "iv": "base64...",   // 12 random bytes
  "ct": "base64...",   // ciphertext
  "tag": "base64..."   // auth tag (some libraries append it to ct)
}

Keeping the key safe

  1. Server keys belong in a KMS or environment variables, never in source control;
  2. For long-lived data, use envelope encryption: a master key in a KMS wraps per-record data keys;
  3. On the client, store keys in a password manager or the OS keychain — not localStorage;
  4. Plan rotation: an algorithm upgrade or a suspected leak must migrate smoothly — which is why the version field exists.

Real-world cases: three easy mistakes

  1. "The ciphertext differs every time — is it broken?" No. CBC/GCM pick a fresh random IV each time, so ciphertext naturally differs; keep the IV to decrypt.
  2. "I flipped one byte and decryption errored out": that is authenticated encryption (GCM) working as intended — it detects tampering and refuses, safer than CBC's garbage output.
  3. "Security review rejected ECB in production": ECB uses no IV, so identical plaintext blocks yield identical ciphertext, leaking image and data structure; avoid it for sensitive data.

FAQ

How do I share the key safely? Send the key and ciphertext separately over different channels (e.g. a secrets platform or offline) — never in the same message. Must the IV be stored? Yes; it is not secret but decryption needs it, so store it alongside the ciphertext. What is the GCM tag? It is the integrity check that verifies the ciphertext was not altered; without it integrity cannot be validated. What if the key leaks? Rotate the key and re-encrypt immediately — there is no "change password" for symmetric encryption.

Try them: symmetric encryption, hashing

The consequences of nonce reuse

  • Catastrophic in GCM: reusing a nonce under one key lets an attacker recover the authentication key and forge messages — worse than plaintext leakage;
  • Inferable plaintext in CBC: the same IV and a shared plaintext prefix yield identical ciphertext blocks, revealing structure (ECB's famous penguin is the extreme case);
  • How to generate them: prefer a random 96-bit nonce with a cap on encryptions per key, or a counter you are certain cannot wrap;
  • Rotate keys: when approaching the limit, change the key rather than pushing on.