CVE-2026-47842
This Vulnerability has been fixed in the Never-Ending Support (NES) version offered by HeroDevs.
Overview
Spring Security is the authentication and access-control framework for Spring-based applications. Alongside authentication, authorization and protection against common attacks, it ships a standalone cryptography module, spring-security-crypto, whose encryptors, password encoders and key generators are widely used by applications to protect data at rest.
An Information Exposure vulnerability (CVE-2026-47842) has been identified in AesBytesEncryptor, which allows attackers with read access to an encrypted data store to determine when two records hold the same plaintext, correlate encrypted values across rows and across users, and recover protected values by pre-encrypting candidate plaintexts and comparing the resulting ciphertext.
Per OWASP: Cryptographic failures are failures related to cryptography, or its absence, which often lead to the exposure of sensitive data. Initialization vectors that are ignored or reused, or an insecure mode of operation, make ciphertext deterministic and let stored values be correlated or recovered.
This issue affects the spring-security-crypto cryptography module of Spring Security.
Details
Module Info
- Product: Spring Security
- Affected packages:
org.springframework.security:spring-security-crypto - Affected versions: 7.1.0, >=7.0.0 <=7.0.6, >=6.5.0 <=6.5.11, >=6.4.0 <=6.4.18, >=6.3.0 <=6.3.10, >=6.2.0 <=6.2.8, >=5.8.0 <=5.8.27, >=5.7.0 <=5.7.25, 5.5.8, 4.2.20.RELEASE
- GitHub repository: https://github.com/spring-projects/spring-security
- Published packages: https://central.sonatype.com/artifact/org.springframework.security/spring-security-crypto
- Package manager: Maven
- Fixed in:
- NES for Spring Security: 4.2.32, 5.5.13, 5.7.26, 5.8.29, 6.2.17, 6.3.15, 6.4.18 and 6.5.13
- OSS Spring Security 7.0.7 and 7.1.1
Vulnerability Info
This Medium-severity vulnerability is found in the org.springframework.security:spring-security-crypto package in the cryptography module of Spring Security.
AesBytesEncryptor selects its initialization vector generator from the CipherAlgorithm enum when the caller does not supply one. The CBC constant is declared with a shared NULL_IV_GENERATOR, a BytesKeyGenerator whose generateKey() always returns the same 16 zero bytes:
private static final String AES_CBC_ALGORITHM = "AES/CBC/PKCS5Padding";
private static final BytesKeyGenerator NULL_IV_GENERATOR = new BytesKeyGenerator() {
private final byte[] VALUE = new byte[16];
@Override
public int getKeyLength() {
return this.VALUE.length;
}
@Override
public byte[] generateKey() {
return this.VALUE;
}
};
public enum CipherAlgorithm {
CBC(AES_CBC_ALGORITHM, NULL_IV_GENERATOR),
GCM(AES_GCM_ALGORITHM, KeyGenerators.secureRandom(16));
public BytesKeyGenerator defaultIvGenerator() {
return this.ivGenerator;
}
}
Every constructor that does not receive an explicit generator falls back to that constant. The two-argument password constructor passes a null generator all the way down, and the secret-key constructor substitutes alg.defaultIvGenerator(), which for CBC is the all-zero generator:
public AesBytesEncryptor(String password, CharSequence salt) {
this(password, salt, null);
}
public AesBytesEncryptor(String password, CharSequence salt, @Nullable BytesKeyGenerator ivGenerator) {
this(password, salt, ivGenerator, CipherAlgorithm.CBC);
}
public AesBytesEncryptor(SecretKey secretKey, @Nullable BytesKeyGenerator ivGenerator, CipherAlgorithm alg) {
this.secretKey = new SecretKeySpec(secretKey.getEncoded(), "AES");
this.alg = alg;
this.encryptor = alg.createCipher();
this.decryptor = alg.createCipher();
this.ivGenerator = (ivGenerator != null) ? ivGenerator : alg.defaultIvGenerator();
}
@Override
public byte[] encrypt(byte[] bytes) {
synchronized (this.encryptor) {
byte[] iv = this.ivGenerator.generateKey();
CipherUtils.initCipher(this.encryptor, Cipher.ENCRYPT_MODE, this.secretKey, this.alg.getParameterSpec(iv));
byte[] encrypted = CipherUtils.doFinal(this.encryptor, bytes);
return (this.ivGenerator != NULL_IV_GENERATOR) ? EncodingUtils.concatenate(iv, encrypted) : encrypted;
}
}
Because the same zero IV is reused for every operation, AES in CBC mode becomes deterministic: for a given password and salt, or a given secret key, the same plaintext always produces byte-for-byte identical ciphertext, and the encrypted output carries no per-record randomness at all. The input that reaches the flawed path is any application data handed to encrypt(byte[]) by an encryptor built through the two-argument constructor, through a CBC construction with a null ivGenerator, or through the Encryptors factory methods that wrap them. An attacker who can read the stored ciphertext, for example through a database dump, a backup, a log, or a read-only reporting account, learns which records share a value without ever recovering the key. Where the plaintext space is small or guessable, such as national identifiers, status flags, email addresses, or short tokens, the attacker can encrypt each candidate with the same password and salt and match the output directly, turning the store into a lookup table. The salt is a per-application constant rather than a per-record value, so it does not break this correlation.
Mitigation
Only recent versions of Spring Security receive community support. Older lines are End-of-Life and will not receive public updates to address this issue.
Users of the affected components should apply one of the following mitigations:
- Upgrade to a currently supported version of Spring Security.
- Stop using AesBytesEncryptor and the Encryptors factory methods, move to AesCbcBytesEncryptor or AesGcmBytesEncryptor, or supply a non-null ivGenerator when constructing a CBC encryptor, and re-encrypt any data that was written with the all-zero initialization vector.
- Leverage a commercial support partner like HeroDevs for post-EOL security support.