首页 网络安全 正文

jasypt-spring-boot 可预测盐值漏洞CVE-2026-9370

摘要

栋科技漏洞库关注到 jasypt-spring-boot 受影响版本中存在的可预测盐值漏洞,该漏洞现已追踪为CVE-2026-9370,CVSS 3.X评分3.7。

jasypt-spring-boot 是基于 Jasypt(Java Simplified Encryption)开发的 Spring Boot 集成组件,能够无缝整合 Spring Boot 配置体系。

一、基本情况

jasypt-spring-boot 是 Spring Boot 配置加密的主流解决方案,核心价值是低成本、无侵入地保护敏感配置,可以广泛应用于生产环境。

jasypt-spring-boot 可预测盐值漏洞CVE-2026-9370

jasypt-spring-boot 支持主流加密算法,可以对 Spring Boot 配置文件中的敏感信息(数据库密码、API 密钥、令牌等)进行加密存储。

栋科技漏洞库关注到 jasypt-spring-boot 受影响版本中存在的可预测盐值漏洞,该漏洞现已追踪为CVE-2026-9370,CVSS 3.X评分3.7。

二、漏洞分析

CVE-2026-9370漏洞可导致所有使用该组件、按照官方密码加密方案配置的用户,在全版本中都使用了密码学层面不安全的默认配置。

受影响版本SimpleGCMConfig.getSecretKeySaltGenerator() 方法在未配置盐值时返回 ZeroSaltGenerator,生成固定的 16 字节 0x00。

导致使用相同主密码的所有部署生成相同的 AES-256 密钥,攻击者可在跨部署场景中利用此问题。

com.ulisesbocchio.jasyptspringboot.encryptor.SimpleGCMConfig#getSecretKeySaltGenerator() 方法在用户未显式配置 

jasypt.encryptor.gcm-secret-key-salt 参数时,会默认返回 org.jasypt.salt.ZeroSaltGenerator(一个固定的 16 字节全零盐值)。

项目说明文档将该参数标注为可选,默认值为 0,无盐值。

因此,按照官方文档配置AES/GCM 主密码加密的用户,会使用固定不变的密钥派生方式,且部署间无唯一标识。

该组件中PBKDF2默认迭代次数仅1000次(远低于OWASP 2023推荐的60万次),该配置同时存在于GCM主密码模式和 PBE 模式中。

所有使用该组件、按照官方密码加密方案配置的用户,在全版本中都使用了密码学层面不安全的默认配置。

1、以上两个问题共同导致:

跨部署环境的 AES-256 密钥复用:使用相同主密码的不同部署环境,会生成完全一致的密钥;任意环境的密文可在其他环境直接解密。

针对弱主密码的离线暴力破解攻击成本大幅降低:一套预计算的彩虹表可用于全球所有部署环境。

2、受影响组件

jasypt-spring-boot 中包含 SimpleGCMConfig 类的所有发行版本

3、存在漏洞的代码路径:

(1)GCM 主密码模式

文件路径:jasypt-spring-boot/src/main/java/com/ulisesbocchio/jasyptspringboot/encryptor/SimpleGCMConfig.java

第 75–81 行:getSecretKeySaltGenerator() 方法在未配置盐值时,直接返回全零盐生成器(ZeroSaltGenerator)

第 35 行:secretKeyIterations 密钥迭代次数默认值为 1000

(2)PBE 模式

文件路径:jasypt-spring-boot/src/main/java/com/ulisesbocchio/jasyptspringboot/configuration/StringEncryptorBuilder.java

第 97 行:config.setKeyObtentionIterations("1000") 密钥获取迭代次数默认值为 1000

问题 1:GCM 主密码密钥推导过程中使用硬编码全零盐值

// SimpleGCMConfig.java
public SaltGenerator getSecretKeySaltGenerator() {
    return saltGenerator != null ?
            saltGenerator :
            (secretKeySalt == null ?
                    new ZeroSaltGenerator() :          // <-- vulnerability
                    new FixedBase64ByteArraySaltGenerator(secretKeySalt));
}

当未配置jasypt.encryptor.gcm-secret-key-salt(默认状态)时,盐值生成器会回退使用org.jasypt.salt.ZeroSaltGenerator,

该生成器会产生 16 字节的 0x00 数据。此盐值会被传入 PBKDF2-HMAC-SHA256 算法以派生 AES-256 密钥。

由于使用固定盐值的 PBKDF2 算法具有确定性,所有使用相同主密码的部署环境都会生成完全一致的 AES-256 密钥。

项目 README 文件将该盐值属性标注为 “可选,默认值为 0,无盐值”,因此按照文档说明进行配置的用户会使用存在漏洞的代码逻辑。

程序启动时不会输出任何警告信息。

4、调用链(默认配置的用户路径):

StringEncryptorBuilder.createGCMDefault()
  └─ SimpleGCMConfig                             (user sets only secretKeyPassword)
      └─ SimpleGCMByteEncryptor.loadSecretKey(config)
          └─ getAESKeyFromPassword(password,
                                   ZeroSaltGenerator,      // deterministic 16×0x00
                                   1000,
                                   "PBKDF2WithHmacSHA256")

问题2:两种基于密码的模式均使用不安全的默认迭代次数(1000)

// SimpleGCMConfig.java (GCM mode)
private int secretKeyIterations = 1000;

// StringEncryptorBuilder.java line 97 (PBE mode)
config.setKeyObtentionIterations(
    get(configProps::getKeyObtentionIterations,
        propertyPrefix + ".key-obtention-iterations", "1000"));

默认的 1000 次迭代同时适用于 GCM 主密码模式和 PBE 模式(PBEWITHHMACSHA512ANDAES_256)。

OWASP 2023 密码学指南建议 PBKDF2-HMAC-SHA256 使用 600000 次迭代。

在 1000 次迭代情况下,普通 GPU 每秒可针对截获的密文计算约 1 亿个PBKDF2 候选值 —— 足以对弱/中等强度主密码实施字典攻击。

关于 README 中 “可选” 的描述

对这份报告最常见的第一反应是:

“README 将 gcm-secret-key-salt 标注为可选,未设置该参数的用户接受了默认值。这属于配置问题,而非漏洞。” 

本报告直接回应该异议。

README 的描述 ——“可选,默认值为 0,无盐值。

若提供,请以 ba64 格式指定盐值字符串”—— 并不会让该问题不再成为漏洞。

这是典型的 CWE-1188(使用不安全默认值初始化资源)。

MITRE CWE 分类专门添加 CWE-1188,用于覆盖库提供不安全默认值并将其标注为 “可选” 而非 “必填” 的情况。

5、文档描述无法免除该库责任的三个原因:

(1)README 的描述存在实质性误导。

“默认值为 0,无盐值” 暗示盐值步骤被跳过。

实际行为是 PBKDF2 使用 16 字节全零盐值运行 —— 一个在全球所有该库部署中共享的硬编码常量。

阅读 README 的用户无法从描述中判断密码学后果(确定性密钥派生;跨部署密钥复用)。

他们会合理认为 “盐值只是一项额外加固选项”,而非 “不设置该参数,我的 AES 密钥将与所有使用相同密码的其他部署完全一致”。

(2)未记录启动警告。

按照文档说明配置的用户不会收到任何运行时提示,告知其正使用存在漏洞的代码路径。

许多同类库会输出类似 “警告:

未配置盐值;派生密钥具有确定性,所有使用该密码的安装实例将共享相同密钥” 的警告;该库未实现此功能。

(3)实例级盐值是安全 PBKDF2 的必填参数,而非风格偏好。

NIST SP 800-132 和 OWASP 2023 均将唯一随机盐值指定为 PBKDF2 保障安全性的前提条件。

在面向用户的文档中将盐值标注为 “可选”,错误表述了密码学标准。

CWE-1188 已成为许多库相关 CVE 编号的依据,这些库均有意提供被文档标注为 “可选” 或 “可配置” 的不安全默认值。

维护者知晓该选项的事实,是 CWE-1188 问题认定的前提,而非抗辩理由。

三、POC概念验证

附件为可运行的 JUnit 5 概念验证代码:ZeroSaltKeyReusePoCTest.java。

1、三项测试验证:

defaultSaltGeneratorIsZero_whenNoSaltConfigured—— 确认未配置盐值时,默认盐值生成器为全零盐值生成器。

pbkdf2WithZeroSaltIsDeterministic—— 使用相同密码的两次独立 PBKDF2 派生操作生成相同的 AES 密钥。

ciphertextFromDeploymentA_decryptsOnDeploymentB—— 由一个 SimpleGCMStringEncryptor 实例生成的密文,可在仅共享主密码、独立构建的第二个实例中正常解密。

观测输出(JDK 21,jasypt 1.9.3,jasypt-spring-boot 4.0.5-SNAPSHOT)

Derived AES key (deployment A): f8dd1a881bb76f3df8d0482c6815aeb91aa75a2a2c84cba585aecee1103a93bf
Derived AES key (deployment B): f8dd1a881bb76f3df8d0482c6815aeb91aa75a2a2c84cba585aecee1103a93bf
Plaintext                       : db-admin-password=hunter2
Ciphertext produced by A        : pBhukUDHUfMf4IRlMnWkxk9FVLunH9EAm6nhCKtRpmj+p07nvR2xVNVVVHz1u3IcxPfwiqA=
Ciphertext produced by B        : ZpJ2gjghaqF+6FlNQDxjtkTzCw73dk2MFeY3/1O9q/b2RzIL7hPu5CH4S7v7T+Im3/UXKDo=
A-ciphertext decrypted on B as  : db-admin-password=hunter2
B-ciphertext decrypted on A as  : db-admin-password=hunter2

Tests run: 3, Successful: 3, Failed: 0

两个独立加密器实例生成了完全相同的派生密钥(f8dd1a88…3a93bf),且成功实现跨实例解密,具体证实了跨部署密钥复用的影响。

2、复现步骤

# 1. Place the attached PoC at:
#    jasypt-spring-boot/src/test/java/com/ulisesbocchio/jasyptspringboot/security/ZeroSaltKeyReusePoCTest.java

# 2. From the inner module directory:
cd jasypt-spring-boot
mvn -pl . -Dtest=ZeroSaltKeyReusePoCTest test

可在任意 JDK 17 及以上版本中复现,

使用 Maven 坐标:com.github.ulisesbocchio:jasypt-spring-boot:4.0.5-SNAPSHOT(或包含 SimpleGCMConfig 的任意早期版本)。

概念验证代码仅使用模拟值(密码 “correct horse battery staple”,明文 “db-admin-password=hunter2”),不含真实密钥。

修复版本中通过移除 ZeroSaltGenerator 回退机制解决问题,但截至报告生成时官方尚未发布修复版本。

四、影响范围

jasypt-spring-boot 3.x:≤ 3.0.5

jasypt-spring-boot4.x:≤ 4.0.4

五、修复建议

暂无

六、参考链接

管理员已设置登录后刷新可查看



扫描二维码,在手机上阅读
评论
更换验证码
友情链接