If you are encrypting data with AES, you have to choose a mode of operation. The two modes you will encounter most often are CBC and GCM. Both encrypt your data. Both use the same underlying AES cipher. But they are not equally safe to use.
The short version: GCM is the modern choice. CBC is the legacy choice. Using CBC correctly requires extra steps that are easy to get wrong, and the consequences of getting them wrong are severe. Let's look at why.
How CBC Works
CBC stands for Cipher Block Chaining. It was one of the earliest modes of operation for block ciphers and has been in use since the 1970s.
Here is how it works. AES encrypts data in 16-byte blocks. In CBC mode, each block of plaintext is XORed with the previous block's ciphertext before being encrypted. The first block is XORed with an initialization vector (IV) since there is no previous ciphertext. This chaining is what makes CBC different from the simpler ECB mode, where each block is encrypted independently and identical plaintext blocks produce identical ciphertext blocks.
The chaining means that even if two blocks of plaintext are identical, their ciphertext will be different (as long as the IV is unique). This hides patterns in the data, which is the minimum requirement for a secure mode of operation.
To decrypt, the process is reversed. Each block of ciphertext is decrypted, then XORed with the previous ciphertext block to recover the plaintext. The IV is used for the first block.
CBC also requires padding because AES blocks are exactly 16 bytes. If your plaintext is not a multiple of 16 bytes, the last block needs to be padded. The most common padding scheme is PKCS#7, which fills the remaining bytes with the number of padding bytes added. If the plaintext is already a multiple of 16, a full block of padding is added.
The Padding Oracle Problem
Here is where CBC gets dangerous. The padding scheme creates a vulnerability that is one of the most exploited in the history of cryptography.
When CBC decrypts the last block, it checks whether the padding is valid. If the padding is invalid, the application typically returns an error. If the padding is valid, it proceeds. This error (or lack of error) is a padding oracle: it tells the attacker whether the padding for a given ciphertext is valid.
With this oracle, an attacker can decrypt the entire ciphertext byte by byte without knowing the key. They modify the ciphertext, submit it for decryption, and observe whether the padding is valid. Each valid response reveals one byte of plaintext. It is slow but it works, and it has broken real systems.
The famous POODLE attack exploited this against SSL 3.0. The Lucky 13 attack exploited it against TLS 1.0 and 1.1. Countless web applications have been vulnerable because they used CBC and leaked padding errors through different response codes or timing differences.
The fundamental problem is that CBC combines encryption with a padding check that leaks information. There is no way to fix this within CBC itself. The only solution is to add a separate authentication mechanism.
The Missing Authentication
This is the bigger issue with CBC: it provides encryption but no authentication. An attacker who intercepts a CBC-encrypted message cannot read it, but they can modify it. They can flip bits in the IV to flip corresponding bits in the first block of plaintext. They can truncate the ciphertext. They can reorder blocks.
Without authentication, the recipient has no way to detect these modifications. They will happily decrypt the tampered ciphertext and hand the result to the application, which might interpret the garbage in dangerous ways.
The solution is to combine CBC with a message authentication code (MAC). The standard approach is Encrypt-then-MAC: encrypt the plaintext with CBC, then compute a MAC (like HMAC-SHA-256) over the IV and ciphertext. The recipient verifies the MAC before attempting decryption. If the MAC does not match, the message is rejected.
This works. It is secure when done correctly. But it is also easy to get wrong. You need to use separate keys for encryption and MAC. You need to compute the MAC over the right data (the IV and the ciphertext, not the plaintext). You need to verify the MAC with a constant-time comparison to avoid timing attacks. You need to verify before decrypting. Get any of these wrong and you may have a vulnerability.
How GCM Solves All of This
GCM takes a completely different approach. Instead of chaining blocks, it uses a counter. It encrypts a counter value to produce a keystream, then XORs the plaintext with the keystream. No padding is needed because XOR works on any length. No padding check means no padding oracle.
More importantly, GCM computes an authentication tag as part of the encryption process. The tag is derived from the ciphertext and a key. When you decrypt, GCM verifies the tag first. If it does not match, decryption fails. The caller never receives tampered data.
This means GCM gives you authenticated encryption in a single operation. No separate MAC. No separate key management. No risk of computing the MAC over the wrong data. No risk of verifying it with a timing-unsafe comparison. The API is simple: encrypt returns ciphertext plus tag, decrypt verifies tag and returns plaintext or fails.
This simplicity is not just about convenience. It is about security. The history of cryptography is full of vulnerabilities caused by developers misimplementing Encrypt-then-MAC. GCM eliminates that entire class of errors by making authentication a built-in part of the encryption operation.
When You Might Still See CBC
CBC is not gone. You will encounter it in legacy systems, in older TLS configurations, and in some file encryption tools. If you are maintaining a system that uses CBC, you do not necessarily need to rewrite everything overnight, but you should:
1. Ensure a separate MAC is being computed and verified before decryption. 2. Ensure the MAC uses a different key from the encryption. 3. Ensure the MAC is computed over the IV and ciphertext (Encrypt-then-MAC, not MAC-then-Encrypt). 4. Ensure MAC verification is constant-time. 5. Ensure padding errors do not leak through different responses or timing.
If any of these are not true, you have a vulnerability. And honestly, if you are building something new, just use GCM. It is supported everywhere, it is fast, and it handles all of this for you.
The Bottom Line
CBC and GCM both encrypt data with AES. The difference is that GCM also authenticates the data, and CBC does not. Using CBC without a MAC is a vulnerability. Using CBC with a MAC is secure but error-prone. Using GCM is secure and simple.
For new code, there is no reason to choose CBC over GCM. GCM is faster (especially with hardware acceleration), simpler to use correctly, and provides authenticated encryption by default. You can try it yourself with our AES text encryptor.
For a deeper look at how GCM works, read our AES-256-GCM explained post.