资讯中心

别再用String.equals()比较密码!jBCrypt的checkpw如何用常量时间比较抵御时序攻击

📅 2026/8/22 14:44:32
别再用String.equals()比较密码!jBCrypt的checkpw如何用常量时间比较抵御时序攻击
别再用String.equals()比较密码jBCrypt的checkpw如何用常量时间比较抵御时序攻击【免费下载链接】jBCryptA Java implementation of OpenBSDs Blowfish password hashing code项目地址: https://gitcode.com/gh_mirrors/jb/jBCrypt存密码这件事很多 Java 开发者都会踩同一个坑拿String.equals()去比较用户输入的密码和数据库里的密文。这个看似人畜无害的习惯其实会通过耗时的细微差异悄悄泄露密码信息。而 jBCrypt 的checkpw方法用了一个巧妙的常量时间比较技巧从根上堵住了这条漏洞。本文带你搞懂时序攻击的原理并手把手教你用 jBCrypt 安全地校验密码。为什么用 String.equals() 比较密码很危险先弄清一个概念时序攻击Timing Attack。String.equals()的底层实现是短路比较——从第一个字符开始逐位比对一旦发现不同立即返回 false。这意味着输入z开头的密码和存着pass...的密文比较第 1 个字节就失败速度最快输入pa开头第 3 个字节才失败稍微慢一点攻击者反复尝试、统计响应耗时就能像玩猜词游戏一样逐字节逼近正确密码比较方式行为耗时特征风险String.equals()逐位比较首个不同处即停止前缀越像耗时越长可被时序侧信道利用jBCryptcheckpw全程遍历XOR 累积比较无论对不对耗时恒定抵御时序攻击对于普通场景这点微秒级差异可能被噪声掩盖但在密码校验这种被攻击者无限次调用的场景下统计几百上千次请求后信号就足以浮出水面。jBCrypt checkpw 常量时间比较源码解析jBCrypt 是 OpenBSD Blowfish 密码哈希算法源自 Niels Provos 与 David Mazieres 的《A Future-Adaptable Password Scheme》的 Java 实现核心代码只有一个文件src/main/java/org/mindrot/BCrypt.java。看checkpw方法约第 761 行的关键逻辑String try_pw hashpw(plaintext, hashed); // 用库中密码 已有盐重新哈希 // ... byte ret 0; for (int i 0; i try_bytes.length; i) ret | hashed_bytes[i] ^ try_bytes[i]; // XOR 累积不提前退出 return ret 0;两个精妙之处重哈希而不是比明文hashpw(plaintext, hashed)会从已存储的哈希串里自动提取盐和成本因子对候选密码重新跑一遍完整哈希得到一个新的完整哈希串再比对。整个过程的重头戏是crypt_raw约第 610 行中 2^cost 轮的 Blowfish 密钥扩展计算开销极大且恒定。XOR 累积比较循环把每一位的异或结果累积进ret即使第一个字节就不匹配循环也会老老实实走完全部字节。耗时只取决于字符串长度与有多少前缀相同无关——攻击者拿不到任何逐字节的反馈 ️‍♂️顺带一提gensalt()约第 714 行用SecureRandom生成 16 字节随机盐保证同一个密码每次哈希结果都不同防止彩虹表攻击。快速上手3步用 jBCrypt 安全存储与校验密码第1步引入依赖在pom.xml中加入 Maven 依赖坐标见 README.md 与pom.xmldependency groupIdorg.mindrot/groupId artifactIdjbcrypt/artifactId version0.3/version /dependency也可以克隆源码本地构建git clone https://gitcode.com/gh_mirrors/jb/jBCrypt第2步注册时哈希存储String hashed BCrypt.hashpw(password, BCrypt.gensalt()); // 或指定成本因子越大越慢、越抗暴力破解默认 10 String hashed BCrypt.hashpw(password, BCrypt.gensalt(12));成本因子按 2 的指数增长每 1 耗时翻倍。新项目建议 10~12登录接口响应时间在 200ms 左右比较合适。第3步登录时校验if (BCrypt.checkpw(candidate, storedHash)) { // 密码正确 }三行代码零样板。单元测试参考src/test/java/org/mindrot/TestBCrypt.java。常见疑问速答为什么每次登录都要跑一遍完整哈希这正是 bcrypt 的设计目的让试错本身就很贵默认成本 10 约需百毫秒级暴力破解成本因此抬高数个量级。成本因子以后能升级吗可以。检测到旧哈希的成本因子低于当前标准时校验通过后用新参数重新哈希并更新存储即可。jBCrypt 能兼容其他系统的 bcrypt 吗可以。它实现的是标准$2a$格式hashpw会校验盐的版本前缀与 OpenBSD、PHP、.NET 等生态互通。许可协议ISC/BSD 宽松协议见LICENSE可放心商用。总结密码校验安全清单❌ 永远不要用String.equals()/比较密码或任何机密✅ 使用 jBCryptcheckpw这类恒定耗时比较XOR 累积、不短路✅ 用SecureRandom盐 可调成本因子的自适应哈希bcrypt/scrypt/argon2 同族✅ 数据库只存哈希串绝不存明文哈希串本身可放心外泄✅ 登录接口叠加速率限制与锁定策略进一步抬高在线攻击成本把比较这个看似无害的小步骤做对你的密码体系才算真正闭环了。【免费下载链接】jBCryptA Java implementation of OpenBSDs Blowfish password hashing code项目地址: https://gitcode.com/gh_mirrors/jb/jBCrypt创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考