DEV Community

Cover image for AES-CBC vs AES-GCM: The Crypto Choice I’d Make Today
Mark Yu
Mark Yu

Posted on • Edited on

AES-CBC vs AES-GCM: The Crypto Choice I’d Make Today

Most crypto bugs I have seen were not caused by someone inventing a new cipher.

They were caused by using a good cipher in the wrong mode, reusing an IV, skipping authentication, or copying a Stack Overflow snippet without understanding the contract.

If you are building a normal application in 2026, my default advice is simple:

Prefer authenticated encryption like AES-GCM. Avoid raw AES-CBC unless you know exactly how you will authenticate the ciphertext.

The Quick Map

Mode What it gives you Main risk
AES-ECB encryption only leaks patterns, avoid it
AES-CBC encryption only needs padding and MAC
AES-CTR stream-like encryption nonce reuse is dangerous
AES-GCM encryption + authentication nonce must be unique

The important word is authentication.

Encryption hides content. Authentication tells you whether the ciphertext was modified.

Never Use ECB

ECB encrypts equal plaintext blocks into equal ciphertext blocks.

That means patterns can leak.

same plaintext block  -> same ciphertext block
same plaintext block  -> same ciphertext block
Enter fullscreen mode Exit fullscreen mode

This is why ECB is the classic "do not use this" example.

CBC Needs More Than Encryption

CBC uses an initialization vector:

plaintext + key + IV -> ciphertext
Enter fullscreen mode Exit fullscreen mode

The IV must be unpredictable and unique for the message.

The bigger issue: CBC does not authenticate data by itself.

That means this is incomplete:

ciphertext = AES-CBC-Encrypt(key, iv, plaintext)
Enter fullscreen mode Exit fullscreen mode

You also need a MAC, usually encrypt-then-MAC:

ciphertext = AES-CBC-Encrypt(encKey, iv, plaintext)
tag = HMAC(macKey, iv + ciphertext)
Enter fullscreen mode Exit fullscreen mode

If you skip the MAC, attackers may be able to tamper with ciphertext and learn things from error behavior.

Why I Prefer GCM

AES-GCM gives encryption and authentication together:

plaintext + key + nonce + aad -> ciphertext + tag
Enter fullscreen mode Exit fullscreen mode

You still need to manage nonces correctly, but the API nudges you toward the right model.

Node.js example:

import crypto from "node:crypto";

const key = crypto.randomBytes(32); // AES-256
const iv = crypto.randomBytes(12);  // recommended GCM nonce size

const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
const ciphertext = Buffer.concat([
  cipher.update("secret message", "utf8"),
  cipher.final(),
]);
const tag = cipher.getAuthTag();

console.log({ iv, ciphertext, tag });
Enter fullscreen mode Exit fullscreen mode

Decrypt:

const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
decipher.setAuthTag(tag);

const plaintext = Buffer.concat([
  decipher.update(ciphertext),
  decipher.final(),
]);

console.log(plaintext.toString("utf8"));
Enter fullscreen mode Exit fullscreen mode

If the ciphertext or tag is modified, decryption fails.

That is exactly what you want.

Practical Rules

  • Do not use ECB.
  • Do not hardcode keys.
  • Do not reuse GCM nonces with the same key.
  • Do not store encryption keys beside encrypted data.
  • Do not invent your own padding or MAC scheme.
  • Use high-level libraries when possible.

Where Block Ciphers Still Matter

You may hit this when building:

  • encrypted backups
  • secure tokens
  • internal secrets tooling
  • file encryption
  • compliance-heavy backend systems

For normal password storage, do not use AES. Use a password hashing algorithm such as Argon2, bcrypt, or scrypt.

That distinction is important:

password storage -> hash
data secrecy     -> encryption
Enter fullscreen mode Exit fullscreen mode

Final Thought

Crypto code should be boring. If your encryption design feels clever, slow down.

For most application work, AES-GCM or a trusted high-level library is the safer path than hand-rolling AES-CBC with padding and MAC logic.

Have you ever had to review encryption code in an application backend?

Top comments (0)