Spring 安全性:无论版本和强度值是否更改,BCryptPasswordEncoder 都能正常工作

Spring Security: BCryptPasswordEncoder works without matter if the Version and Strength values are changed

使用 Spring 引导 CLI 可以执行以下操作:

spring encodepassword secret

该命令打印的位置

{bcrypt}a$ZjFpLGhApSqM1ftCBOPvt.3aV3l5dsRawW61ZCX2lbIqRq6afgzk6

因此密码secret被编码为{bcrypt}a$ZjFpLGhApSqM1ftCBOPvt.3aV3l5dsRawW61ZCX2lbIqRq6afgzk6,观察$2a$10{bcrypt} 份数

已声明:

@Bean
PasswordEncoder encoder() {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

是否强制使用以下内容:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
            .withUser("some_user")
            .password("{bcrypt}a$ZjFpLGhApSqM1ftCBOPvt.3aV3l5dsRawW61ZCX2lbIqRq6afgzk6")
            .authorities("ROLE_SOME_ROLE");

Observe 是强制使用 {bcrypt}PasswordEncoderFactories class (for the createDelegatingPasswordEncoder method), it because the DelegatingPasswordEncoder 中有更多详细信息 class 在幕后使用。

如果我不使用

@Bean
PasswordEncoder encoder() {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

并仅替换为:

@Bean
PasswordEncoder bcryptEncoder() {
    return new BCryptPasswordEncoder(BCryptVersion.A, 10);// or new BCryptPasswordEncoder()
}

观察: 从上方

现在强制使用:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
            .withUser("some_user")
            .password("a$ZjFpLGhApSqM1ftCBOPvt.3aV3l5dsRawW61ZCX2lbIqRq6afgzk6")
            .authorities("ROLE_SOME_ROLE");

Observe is mandatory not use {bcrypt}, 因为DelegatingPasswordEncoder class is not 在幕后使用了。否则无法登录。

到这里都懂了

现在的困惑是:

如果使用:

@Bean
PasswordEncoder bcryptEncoder() {
    return new BCryptPasswordEncoder(BCryptVersion.Y, 12);
}

观察:现在使用 Y, 12,它与 A, 10 完全不同(因此与 a 密码 part/section).

情况 可以做登录过程呢。我认为那一定是不可能的,因为模式不一样。顺便说一句,我确实在项目中做了 clean compile

发生了什么事?

Observation: now Y, 12 is used and it is totally different than A, 10 (and therefore than the a password part/section).

是的,但是我们在比较时不生成新的盐,我们在从数据库中获取的密码上使用盐。

如果我们查找 wikipedia 关于 bcrypt 我们会看到以下关于 Y

In June 2011, a bug was discovered in crypt_blowfish, a PHP implementation of BCrypt. It was mis-handling characters with the 8th bit set. They suggested that system administrators update their existing password database, replacing a$ with x$, to indicate that those hashes are bad (and need to use the old broken algorithm). They also suggested the idea of having crypt_blowfish emit y$ for hashes generated by the fixed algorithm.

Nobody else, including canonical OpenBSD, adopted the idea of 2x/2y. This version marker change was limited to crypt_blowfish.

因为没有这个漏洞的spring实现Y$A$基本相同。

当我们查看 BCrypt#checkpw 的源代码时,我们看到以下内容:

/**
 * Check that a password (as a byte array) matches a previously hashed one
 * @param passwordb the password to verify, as a byte array
 * @param hashed the previously-hashed password
 * @return true if the passwords match, false otherwise
 * @since 5.3
 */
public static boolean checkpw(byte[] passwordb, String hashed) {
    return equalsNoEarlyReturn(hashed, hashpw(passwordb, hashed));
}

将获取的密码作为第二个参数传递给 this function

public static String hashpw(byte passwordb[], String salt) {
    
    // omitted code

    // Here we extract the salt from the provided hashed password 
    real_salt = salt.substring(off + 3, off + 25);

    // omitted code
}

他们只会检查您提供的版本是否有效。之后,他们从存储在数据库中的密码中提取盐,然后用该盐对密码进行哈希处理,然后进行比较。

构造函数设置将仅应用于新散列的密码。