In short

Do not use ECB. It encrypts each 16-byte block on its own, so equal plaintext blocks give equal ciphertext blocks, and it detects no tampering. Name an authenticated mode explicitly, such as AES-256-GCM with a fresh 96-bit nonce or ChaCha20-Poly1305, through a maintained library. Watch for defaults: in Java, Cipher.getInstance("AES") means AES/ECB/PKCS5Padding.

  • Under one key, equal 16-byte plaintext blocks always produce equal ciphertext blocks.
  • A longer key does not help: the leak comes from the mode, not the key size.
  • ECB has no integrity: blocks can be swapped, deleted or replayed without any error.
  • ECB usually enters code through defaults, such as Java’s Cipher.getInstance("AES"), or explicit MODE_ECB options.
  • Replace it with an AEAD and a versioned format, then re-encrypt stored ECB data.

Verify it

  1. Search the code and its dependencies for the ECB patterns listed above.
  2. Encrypt a plaintext made of two identical 16-byte blocks and confirm the two ciphertext blocks differ.
  3. Encrypt the same record twice and confirm the outputs differ.
  4. Flip one ciphertext byte and confirm decryption fails without returning plaintext.
Applies to

Historical ECB leakage and modern migration decisions. The toy block exercise is not an encryption recipe.

Part of
Learning path: Cryptography without shortcuts (step 9 of 12). Workflow: Fix a finding

Fix it in your stack

Tested fixes: the vulnerable code, the fix, a plausible fix that still fails, and the regression tests that tell them apart.

Learning goals and prerequisitesAbout 18 minutes: 5 to read and 13 for the exercise

What you will learn

  • Derive equality leakage from ECB’s per-block equation.
  • Distinguish pattern evidence from guessed plaintext.
  • Specify a migration and negative tests for an authenticated format.

Before you start

Developers who call encryption APIs directly, and reviewers who find ECB in code or data formats.

  • Understand equal-size plaintext blocks and deterministic encryption; no cipher implementation is required.
Go to the guided practice

A permutation applied independently to every block

AES transforms one 16-byte block under a key. ECB extends that primitive to a message by encrypting each block independently: Cᵢ = Eₖ(Pᵢ). Because encryption under one key is a permutation, two equal plaintext blocks produce equal ciphertext blocks, and two equal ciphertext blocks imply equal plaintext blocks under that key. An observer can therefore recover an equality map without recovering the key or the plaintext.

Consider a fixed-width payroll export whose department field always starts on a 16-byte boundary. Repeated department blocks produce repeated ciphertext blocks. Once an observer learns that one block means SALES, every equal block acquires that meaning. An image is a memorable demonstration, but database rows, repeated headers, and padded short identifiers are equally useful examples. The leakage depends on alignment and repetition, not on whether the file is visually recognizable.

Plain text
Plaintext blocks:   A | B | A | C | B
ECB ciphertext:     x | y | x | z | y
Known mapping: B = "department=SALES"
Deduction: blocks 2 and 5 carry the same known department.

What the observer must be able to compare

The equality claim is within the same key and block boundaries. Seeing equal ciphertext under two different keys does not establish equal plaintext. A change of serialization can move a field across a block boundary and hide this particular pattern without making the construction secure. Likewise, compression may remove visible repeated blocks while introducing other information leakage; it is not a security repair.

If the attacker can request encryptions of chosen candidate values under the target key, deterministic encryption becomes a dictionary oracle. For a field with only four possible values, encrypting those four padded values identifies every corresponding ciphertext. More entropy in the AES key does nothing to enlarge that four-value plaintext dictionary. With no chosen-plaintext access, frequency and context may still reveal information, but an equality map alone does not prove a particular value.

Choose the security property the record needs

For ordinary application records, use a supported authenticated-encryption construction with correct nonce handling; the AEAD record guide at /domains/identity-crypto/encryption/authenticated-record-design/ covers nonces and associated data. Distinct nonces make repeated plaintexts encrypt differently; the tag also detects tampering. ECB has no authenticity: replacing, deleting, or reordering complete encrypted blocks need not cause any cryptographic failure. A parser rejecting malformed records can catch some modifications but cannot authenticate the remaining valid ones.

Do not add an IV field to an ECB record and assume that fixes the mode: ECB does not consume an IV. Do not invent a random prefix scheme, because blocks after the prefix still have independent deterministic behavior. Searchable encryption and deterministic tokenization are separate designs with deliberate equality leakage; choosing one requires a documented use case and threat model rather than rebranding ECB.

Migration needs a versioned format. New writes use the new format; old reads decrypt only explicitly marked legacy records and then rewrite them. Do not let an authentication failure trigger a silent fallback to ECB. Retain old decryption capability only for the migration window and backups that still require it.

What should you do today?

  • Search code for Cipher.getInstance("AES"), AES/ECB, MODE_ECB, modes.ECB and -ecb cipher names, including in dependencies.
  • Replace each with an AEAD, such as AES-256-GCM or ChaCha20-Poly1305, through a library that chooses the nonce for you where possible; the nonce lab at /labs/nonces/ shows what goes wrong otherwise.
  • Add a versioned record format, decrypt legacy ECB records only on an explicit legacy path, and rewrite them.
  • Add a test that encrypts two equal blocks and fails if the ciphertext blocks match.

Replace the Java default with AES-GCM

In Java, a transformation without a mode falls back to the provider’s default, and the JDK’s default provider uses ECB. This class shows the fix: name AES/GCM/NoPadding, generate a fresh 96-bit nonce for every record, keep the 128-bit tag, and authenticate the record’s context. It was tested on JDK 21, which confirmed that Cipher.getInstance("AES") produces repeated ciphertext blocks identical to AES/ECB/PKCS5Padding, while the GCM version produces different output for the same input and rejects a changed byte or a wrong context.

FixedRecordCipher.javaJava
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;

public final class RecordCipher {
  private static final SecureRandom RANDOM = new SecureRandom();

  // Vulnerable: Cipher.getInstance("AES") names no mode, and the JDK's default provider
  // fills in AES/ECB/PKCS5Padding, so equal 16-byte blocks give equal ciphertext blocks.
  // Fixed: name AES-GCM, use a fresh 96-bit nonce and authenticate the record's context.
  public static byte[] encrypt(SecretKey key, byte[] plaintext, byte[] context) throws Exception {
    byte[] nonce = new byte[12];
    RANDOM.nextBytes(nonce);
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, nonce));
    cipher.updateAAD(context);
    return ByteBuffer.allocate(12 + plaintext.length + 16).put(nonce).put(cipher.doFinal(plaintext)).array();
  }

  public static byte[] decrypt(SecretKey key, byte[] record, byte[] context) throws Exception {
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
    cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(128, record, 0, 12));
    cipher.updateAAD(context);
    return cipher.doFinal(record, 12, record.length - 12); // AEADBadTagException on any change
  }
}

Verify equality leakage and the replacement boundary

The exercise uses an explicitly supplied substitution table, not AES. It isolates the mode behavior so the conclusion can be checked without a library. It proves how independently deterministic blocks leak patterns; it does not measure the security of a real cipher.

For a real replacement, confirm that repeated encryption of identical plaintext under one key yields distinct nonces and ciphertexts, that decryption restores the original bytes, and that changing a ciphertext byte or record context returns failure without plaintext. A single successful round trip only proves that the two functions agree. Record the algorithm, nonce allocation, authenticated context, and legacy-read cutoff as separate review decisions.

Guided practice

An analyst receives a toy block-encrypted export and one known field. Recover only what the evidence permits.

Supplied scenario material
Plain text
Toy permutation under key K: A→7, B→2, C→9, D→4.
Record 1 plaintext: A B A C
Record 2 ciphertext: 7 4 7 2
Known fact: B means SALES. A, C, D remain unknown.
  1. 1. Encrypt and compare

    Write Record 1 ciphertext and mark equal positions across both records.

    Expected result and reasoning

    Record 1 is 7 2 7 9. Positions 1 and 3 repeat within each record and across records. Their underlying value is A, but its business meaning is not supplied.

  2. 2. Separate knowledge from guessing

    Identify every SALES block; then decide whether ciphertext 4 means ENGINEERING.

    Expected result and reasoning

    SALES appears at Record 1 position 2 and Record 2 position 4. No supplied fact identifies D or ciphertext 4 as ENGINEERING.

  3. 3. Test a partial repair

    Add an ignored IV field to both records, then propose an authenticated replacement acceptance test.

    Expected result and reasoning

    An ignored IV changes no block and fixes nothing. A replacement must round-trip, use distinct nonces, and reject a changed tag or wrong record context before releasing bytes.

Your deliverable: An annotated equality map and three concrete replacement acceptance cases.

The small permutation is a teaching model. No real encryption is executed and no real plaintext is recovered.

Save a draft on this device to resume later. Automatic saving begins after you choose Save. Drafts are never sent by this application; use synthetic data.

No draft saved on this device yet.

Sources & further reading

The standards, specifications and documentation behind the guidance above. Each is the evidence for a claim on this page; consult it for detail beyond the defaults and versions listed here.

  1. NIST SP 800-38A: Block Cipher Modes of Operation. December 2001.
  2. RFC 5116: An Interface and Algorithms for Authenticated Encryption. January 2008.
  3. OWASP Cryptographic Storage Cheat Sheet
  4. Java SE 21: javax.crypto.Cipher (provider default mode and padding)

Reference information

ASVS 5.0.0 requirements
v5.0.0-11.3.1 · v5.0.0-11.3.2 · v5.0.0-11.3.3

Download this guide as Markdown