Deterministic AES/CBC Encryption in Spring Security AesBytesEncryptor Allows Ciphertext Correlation

MEDIUM | AUGUST 20, 2026 | CVE-2026-47842

Description

This is a continuation of CVE-2020-5408.

Applications using AesBytesEncryptor with the two-argument constructor or when passing a null IV generator and CBC as the encryption mode encrypt data with AES/CBC using a null (all-zero) initialization vector. Because the IV is fixed, identical plaintexts always produce identical ciphertext for a given password and salt, regardless of when or how many times the value was encrypted.

An attacker with read access to the encrypted data store can exploit this property to determine when two records share the same plaintext value, correlate encrypted values across rows or users, and mount dictionary attacks by pre-encrypting candidate values and comparing the results.

Affected Spring Products and Versions

Spring Security:

  • 7.1.0 - 7.1.0
  • 7.0.0 - 7.0.6
  • 6.5.0 - 6.5.11
  • 6.4.0 - 6.4.18
  • 5.8.0 - 5.8.27
  • 5.7.0 - 5.7.25

Mitigation

Users of affected versions should upgrade to the corresponding fixed version.
Fix versionAvailability
7.1.1OSS
7.1.0.1Enterprise Support Only
7.0.7OSS
7.0.6.1Enterprise Support Only
6.5.12Enterprise Support Only
6.4.19Enterprise Support Only
5.8.28Enterprise Support Only
5.7.26Enterprise Support Only

Users should review the migration guide to determine whether data re-encryption is also required.

Migration

This release introduces AesCbcBytesEncryptor and AesGcmBytesEncryptor as secure replacements for AesBytesEncryptor, which is now deprecated. Encryptors.standard(), Encryptors.stronger(), Encryptors.text(), and Encryptors.delux() are deprecated for the same reason.

Scenarios That Require No DB-Level Migration

If you supplied a SecretKey--either with CipherAlgorithm.CBC and a non-null ivGenerator or with CipherAlgorithm.GCM--the new encryptors produce byte-for-byte equivalent ciphertext.

In those cases, the code can be updated in the following way with no further steps:

Deprecated Replace With
AesBytesEncryptor(secretKey, null, CipherAlgorithm.GCM) AesGcmBytesEncryptor.withSecretKey(SecretKey).build()
AesBytesEncryptor(secretKey, ivGenerator, CipherAlgorithm.GCM) AesGcmBytesEncryptor.withSecretKey(SecretKey).ivGenerator(ivGenerator).build()
AesBytesEncryptor(secretKey, ivGenerator, CipherAlgorithm.CBC) AesCbcBytesEncryptor.withSecretKey(SecretKey).ivGenerator(ivGenerator).build()

Scenarios That Require DB-Level Migration

If you are using the Encryptors API, are using AesBytesEncryptor with a password, or are using CipherAlgorithm.CBC with a null ivGenerator, the old and new encryptors are not compatible for one or more of the following reasons:

  1. CBC no longer uses a null IV by default, and
  2. The old password-based constructors derive keys using PBKDF2-HMAC-SHA1 with 1,024 iterations, while the new encryptors use PBKDF2-HMAC-SHA256 with 600,000 iterations.

In these cases, ciphertext written by an old encryptor can only be decrypted by an old encryptor instance, so a direct swap is not possible without a migration step.

The replacement APIs in these cases are as follows:

Deprecated Replaced By
Encryptors.standard() AesCbcBytesEncryptor.withPassword
Encryptors.stronger() AesGcmBytesEncryptor.withPassword
Encryptors.text() AesCbcBytesEncryptor.withPassword, then hex-encode/decode the bytes yourself (e.g. with org.springframework.security.crypto.codec.Hex)
Encryptors.delux() AesGcmBytesEncryptor.withPassword, then hex-encode/decode the bytes yourself (e.g. with org.springframework.security.crypto.codec.Hex)
AesBytesEncryptor(password, salt) AesCbcBytesEncryptor.withPassword(password, salt)
AesBytesEncryptor(..., CipherAlgorithm.CBC) AesCbcBytesEncryptor.withPassword
AesBytesEncryptor(..., CipherAlgorithm.GCM) AesGcmBytesEncryptor.withPassword

Encryptors.text() and Encryptors.delux() return a TextEncryptor that hex-encodes its output, whereas AesCbcBytesEncryptor/AesGcmBytesEncryptor return a BytesEncryptor operating on raw bytes, so callers that need string-in/string-out semantics are responsible for the encoding step themselves.

Spring Security offers no migration API for encrypted material, but the effort should be quite similar to when your organization rotates the encryption password or secret key as a normal business operation. While you should perform your own analysis to decide how to migrate your data, the following pseudocode illustrates what this could look like:

BytesEncryptor v1 = Encryptors.stronger(oldPassword, salt);
BytesEncryptor v2 = AesGcmBytesEncryptor.withPassword(newPassword, salt);
for (Entity e : entities) {
    byte[] b = e.getEncryptedData();
    e.setEncryptedData(v2.encrypt(v1.decrypt(b)));
    e.save();
}

Note that since this migration upgrades the key derivation algorithm, it is a good opportunity to rotate the password, as this snippet additionally illustrates.

History

  • 2026-08-20: Initial vulnerability report published.

Get ahead

VMware offers training and certification to turbo-charge your progress.

Learn more

Get support

Tanzu Spring offers support and binaries for OpenJDK™, Spring, and Apache Tomcat® in one simple subscription.

Learn more

Upcoming events

Check out all the upcoming events in the Spring community.

View all