AES-ECB
The ECB Penguin
Same block in, same block out — so the picture survives the encryption.
Use AES-GCM
- AES-GCM, an authenticated mode, with a unique nonce for every message under a key.
- No ECB for data encryption. Web Crypto doesn’t even offer it.
- Keep Web Crypto’s default 128-bit GCM tag — NIST plans to drop tags under 96 bits.
- Standard
- NIST SP 800-38D (GCM); SP 800-38A (modes); RFC 5116 (AEAD)
- Deadline
- No dated sunset. NIST said in 2023 it will limit ECB’s approval; the SP 800-131A Rev. 3 draft proposes disallowing ECB for data encryption.
What happens
We draw a paper penguin in this tab and encrypt its pixels with AES-128 in ECB mode — a deliberately broken, quarantined implementation — one 16-byte block at a time, all under the same key. Identical plaintext blocks become identical ciphertext blocks, so flat color stays flat and the outline walks straight through the “encryption”. Beside it, the same pixels go through AES-128-GCM in your browser’s Web Crypto and come out as noise: same cipher, same key size, different mode. All of it is live.
Real-world
In 2020, Citizen Lab found every Zoom meeting’s audio and video encrypted with one shared AES-128 key in ECB mode; Zoom 5.0 moved to AES-256-GCM. Citizen Lab’s report
Adobe’s 2013 breach showed the same fingerprint: its leaked passwords were Triple DES, and the repeats in the dump showed ECB, so identical passwords had identical ciphertext. The deeper mistake was encrypting them at all. Passwords should be stored with a slow, salted password hash such as Argon2id, never encrypted. Sophos analysis (archived)
In your code
ECB rarely ships on purpose. Often it ships as a default: with Java’s standard provider (SunJCE), asking for plain "AES" gets you ECB.
// Java (SunJCE): plain "AES" means AES/ECB/PKCS5Padding
Cipher.getInstance("AES");
# Python, PyCryptodome
AES.new(key, AES.MODE_ECB)
// Node
createCipheriv('aes-128-ecb', key, null)
// the fix: an AEAD, with a fresh 96-bit nonce for every message
Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
c.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, nonce));
GCM has its own sharp edge: never reuse a nonce under one key. A repeat leaks the XOR of the two messages and will likely let an attacker recover the authentication key and forge messages. A 2016 scan found 184 HTTPS servers doing it. With random nonces, NIST caps a key at 232 messages.
How to find it
- Search code and config for
MODE_ECB,modes.ECB,/ECB/,-ecb,CipherMode.ECBand a bareCipher.getInstance("AES"). - Static analysis helps, unevenly: Semgrep flags ECB and a bare
"AES"in Java, and CodeQL’sweak-cryptographic-algorithmqueries catch ECB in Java and Python. Neither catches every Python or Node call, so search as well. - Look at stored ciphertext: identical 16-byte blocks in records that should differ are ECB’s fingerprint.