问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
CVE-2025-22228:Spring Security BCryptPasswordEncoder 不强制最大密码长度
漏洞分析
Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。它是保护基于 Spring 的应用程序的事实标准。 `BCryptPasswordEncoder.matches(CharSequence,String)``true`只要前 72 个字符相同,就会错误地返回大于 72 个字符的密码。
漏洞描述 Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。它是保护基于 Spring 的应用程序的事实标准。 `BCryptPasswordEncoder.matches(CharSequence,String)``true`只要前 72 个字符相同,就会错误地返回大于 72 个字符的密码。 影响版本: - Spring Security: - 5.7.0 - 5.7.15 - 5.8.0 - 5.8.17 - 6.0.0 - 6.0.15 - 6.1.0 - 6.1.13 - 6.2.0 - 6.2.9 - 6.3.0 - 6.3.7 - 6.4.0 - 6.4.3 - 较旧的、不受支持的版本也受到影响 环境搭建 ---- 这个类方法主要用来加密和检查密码。就不单独编写页面了,直接在后台实现具体逻辑验证漏洞即可。编写`demo`如下: ```php // 原始密码(前72字符相同,总长度超过72) String password1 \= "a".repeat(72); String password2 \= password1 + "attack\_suffix"; // 总长度 72+14=86 System.out.println("password1: " + password1 + ", password1长度: " + password1.length()); System.out.println("password2: " + password2 + ", password2长度: " + password2.length()); // 生成盐值 String salt \= BCrypt.gensalt(); // 哈希处理 BCryptPasswordEncoder encoder \= new BCryptPasswordEncoder(); String hash1 \= encoder.encode(password1); // 验证密码是否匹配 boolean isMatch2 \= encoder.matches(password2, hash1); // 使用 password2 的值与 password1 的哈希值对比 System.out.println("Password2 匹配结果: " + isMatch2); // 漏洞存在时返回 true(前72字符相同) ``` 漏洞分析 ---- 漏洞出在`BCryptPasswordEncoder`这个加密器上,看一下这个加密器的检查逻辑。 ### 密码加密过程 先生成随机数盐 prefix : 算法版本,$2A表示使用的 BCrypt 算法版本。 log\_rounds : 控制哈希计算的迭代次数,这里为10次,计算公式为 `2^cost` 最后使用base64加密随机数拼接到后边  最终如下:  接着用生成的盐hash化处理密码。提取盐中的迭代次数、随机数,  接下来进入hash过程,初始化一个数字数组,18个元素,一个元素占用32位。  接着,用密码和随机数对这个数组进行处理,这里可以看到是遍历18次。  接着进入`streamtowords`方法,每次处理密码的`4`位。  `this.P`算是根据原密码和随机数生成的新的`KEY`。这个`this.P`有效位数也就是`18 * 4 = 72`,至于密码多余的位数,循环结束也就不处理了,等于直接无视了。 接着往下,加密`cdata`(框架自定义的密文数据),并根据`cdata`生成最终的`HASH`  用来加密`cdata`的`KEY`正是新生成的  整理一下这个加密过程: 若密码超过72字节,超出部分会被 **直接截断**,仅保留前72字节。 BCrypt 在哈希计算时,会将密码和盐值拼接后进行多次 Blowfish 加密轮次。其核心步骤如下: 1. **初始化状态数组(State Array)**: 使用盐值和密码初始化一个 **18 x 32 位(共 576 位,即 72 字节)** 的状态数组。 2. **密钥扩展(Key Expansion)**: 密码和盐值被填充或截断至 **72 字节**,用于生成子密钥。 3. **加密轮次(Encryption Rounds)**: 通过多次迭代(次数由成本因子决定)生成最终哈希值。 漏洞复现 ----  漏洞修复 ---- ### 方法一 升级新版本 | 受影响的版本 | 修复版本 | 可用性 | |---|---|---| | 5.7.x | 5.7.16 | [仅限企业支持](https://spring.vmware.com/) | | 5.8.x | 5.8.18 | [仅限企业支持](https://spring.vmware.com/) | | 6.0.x | 6.0.16 | [仅限企业支持](https://spring.vmware.com/) | | 6.1.x | 6.1.14 | [仅限企业支持](https://spring.vmware.com/) | | 6.2.x | 6.2.10 | [仅限企业支持](https://spring.vmware.com/) | | 6.3.x | 6.3.8 | 开源软件 | | 6.4.x | 6.4.4 | 开源软件 | #### 方法二 不方便升级新版本的,可以自行限制密码长度,参考代码: `public class CustomPasswordEncoder extends BCryptPasswordEncoder { @Override public boolean matches(CharSequence rawPassword, String encodedPassword) { if (rawPassword.length() > 72) { throw new IllegalArgumentException("密码长度超过72字符"); } return super.matches(rawPassword, encodedPassword); } }`
发表于 2025-04-27 15:05:00
阅读 ( 741 )
分类:
Web应用
0 推荐
收藏
0 条评论
请先
登录
后评论
la0gke
9 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!