BCrypt 的 cost 设多少合适?{bcrypt} 这个前缀是干什么的?旧库里没有前缀的哈希怎么办?72 字节限制会造成什么?这篇在 Spring Security 7 上测 BCrypt 不同 strength 的耗时,再逐个验证 DelegatingPasswordEncoder 的行为。
默认 strength = 10;同一个密码编码两次:$2a$10$2zXk4kuCDTlq85SuLXd4d.AII1ikOPyLjsIkfX0nDf0IuEY4ciY/2 与 $2a$10$j7Mp7N.JUp2qRIhx5hCxReZSwwdCaFkiskvRNO/WrfYVTVCfMJKOu;长度 60;两个值相同:false;都能 matches:true
strength= 4:encode 中位 1.2 ms,matches 中位 1.2 ms,哈希前缀 $2a$04$
strength= 6:encode 中位 4.8 ms,matches 中位 4.7 ms,哈希前缀 $2a$06$
strength= 8:encode 中位 17.7 ms,matches 中位 17.8 ms,哈希前缀 $2a$08$
strength=10:encode 中位 70.1 ms,matches 中位 69.8 ms,哈希前缀 $2a$10$
strength=12:encode 中位 274.6 ms,matches 中位 282.3 ms,哈希前缀 $2a$12$
strength=14:encode 中位 1105.2 ms,matches 中位 1082.7 ms,哈希前缀 $2a$14$
strength 取值范围:
strength=3:IllegalArgumentException:Bad strength
strength=32:IllegalArgumentException:Bad strength每加 2 耗时约 ×4;strength 12 每次校验约 275 ms CPU。
encode("secret") = {bcrypt}$2a$10$ABga77euJ266Yktz8GEDau5UGkfb7I/1i9lzBHsM3x0KUKNnWUKaW
matches 带前缀的各种哈希(同一个明文 secret):
{noop} → matches=true,哈希长度 12
{bcrypt} → matches=true,哈希长度 68
{pbkdf2@SpringSecurity_v5_8} → matches=true,哈希长度 124
{scrypt@SpringSecurity_v5_8} → matches=true,哈希长度 105
{argon2@SpringSecurity_v5_8} → matches=true,哈希长度 125
没有前缀的哈希(例如直接从旧库里读出来的 BCrypt):
抛 IllegalArgumentException:Given that there is no default password encoder configured, each password must have a password encoding prefix. Please either prefix this password with '{noop}' or set a default password encoder in `DelegatingPasswordEncoder`.
setDefaultPasswordEncoderForMatches(BCrypt) 之后 matches → true
upgradeEncoding(是否需要用新参数重新编码):
{bcrypt} strength 4 → true;{bcrypt} strength 10(默认)→ false;{noop} → true;{bcrypt} strength 12 → false前缀选算法;无前缀要设默认编码器;upgradeEncoding 用来升级旧哈希。
只在第 72 个字节之后不同的两个密码:matches(72 字节原文, 哈希)=true,matches(86 字节、前 72 字节相同, 同一个哈希)=true
encode(73 字节的密码):抛 IllegalArgumentException:password cannot be more than 72 bytes
matches(null, 哈希) 成功
matches("", 空密码的哈希):false
matches("secret", "not-a-hash"):falseencode 拒绝超过 72 字节,matches 会忽略 72 字节之后;空密码和乱格式哈希不抛异常。
BCrypt 默认(strength 10):encode 中位 67.8 ms,matches 中位 67.6 ms,哈希长度 60 Pbkdf2 defaultsForSpringSecurity_v5_8:encode 中位 38.8 ms,matches 中位 38.9 ms,哈希长度 96 SCrypt defaultsForSpringSecurity_v5_8:encode 中位 112.5 ms,matches 中位 106.3 ms,哈希长度 77 Argon2 defaultsForSpringSecurity_v5_8:encode 中位 16.6 ms,matches 中位 15.5 ms,哈希长度 97
四种默认参数都在几十毫秒量级,但强度不能直接比较。