1. Introduction

In the previous article (https://www.felord.cn), we explored UserDetails, an important user information body in Spring Security. In the intermediate example, we used a plaintext password by prefixing the password in plaintext with {noop}. This section takes a look at password encoding in Spring Security.

2. Md5 is not recommended

First, MD5 is not an encryption algorithm, it is a hash digest. It was commonly used as a password hash to protect passwords. Digest algorithms like MD5 and SHA1 have become insecure due to rainbow tables. If you don’t believe me, you can go to some decryption sites like CMD5 and you’ll find that MD5 and SHA1 are really easy to crack.

3. Password algorithms in Spring Security

Article on (https://www.felord.cn) we mentioned InMemoryUserDetailsManager initialization Bean needs to transmit a ObjectProvider < PasswordEncoder > parameters. The PasswordEncoder here is our tool interface for encoding passwords. This interface has only two functions: one is match verification. The other is cryptography.

The above is provided by the Spring Security org. Springframework. Security. The crypto. Password. PasswordEncoder some implementation, some have been out of date. One of the things we noticed was an implementation called a delegate cipher encoder.

3.1 the entrusted DelegatingPasswordEncoder password encoder

What is a Delegate? It is the work assigned by Party A to Party B. Party B has a lot of channels in hand, but party B just wants to earn price difference and don’t want to work. So party B entrusts the work to others according to some rules and lets others do it. Party b is DelegatingPasswordEncoder here. This class maintains the following list:

  • final String idForEncodeMatches the encoder by id, which cannot be{}Including the.DelegatingPasswordEncoderInitialization pass, used to provide the default cipher encoder.
  • final PasswordEncoder passwordEncoderForEncodeThrough the aboveidForEncodeMatched toPasswordEncoder Used to encode a password.
  • final Map<String, PasswordEncoder> idToPasswordEncoderUsed to maintain multipleidForEncodeWith a specificPasswordEncoderThe mapping relation of.DelegatingPasswordEncoderIt’s loaded at initialization, and it does some rule checking at initialization.
  • PasswordEncoder defaultPasswordEncoderForMatches = new UnmappedIdPasswordEncoder()Default password matcher, aboveMapExecute it if it does not existmatchesMethod for matching verification. This is an inner class implementation.

DelegatingPasswordEncoder encoding method:

   @Override
   public String encode(CharSequence rawPassword) {
   	return PREFIX + this.idForEncode + SUFFIX + this.passwordEncoderForEncode.encode(rawPassword);
   }

Copy the code

The source code can be seen from above by DelegatingPasswordEncoder encoded password is follow certain rules, follow encodePassword {idForEncode}. That is, the prefix {} contains the encoding method and splices the encoding method of the password string.

DelegatingPasswordEncoder password matching methods:

  @Override
  public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
  	if (rawPassword == null && prefixEncodedPassword == null) {
  		return true;
  	}
  	String id = extractId(prefixEncodedPassword);
  	PasswordEncoder delegate = this.idToPasswordEncoder.get(id);
  	if (delegate == null) {
  		return this.defaultPasswordEncoderForMatches
  			.matches(rawPassword, prefixEncodedPassword);
  	}
  	String encodedPassword = extractEncodedPassword(prefixEncodedPassword);
  	return delegate.matches(rawPassword, encodedPassword);
  }
Copy the code

The password is matched by passing in the original password and the password encoding string following the {idForEncode}encodePassword rule. By getting encoding id (idForEncode) to set the mapping from DelegatingPasswordEncoder idToPasswordEncoder extract specific PasswordEncoder matching check. Use UnmappedIdPasswordEncoder couldn’t find.

This is DelegatingPasswordEncoder workflow. So where is the DelegatingPasswordEncoder instantiation?

3.2 PasswordEncoderFactories

From the name, it can be seen that this is a factory, specializing in the manufacture of PasswordEncoder. But as a static factory only provide a method to initialize DelegatingPasswordEncoder:

	@SuppressWarnings("deprecation")
	public static PasswordEncoder createDelegatingPasswordEncoder(a) {
		String encodingId = "bcrypt";
		Map<String, PasswordEncoder> encoders = new HashMap<>();
		encoders.put(encodingId, new BCryptPasswordEncoder());
		encoders.put("ldap".new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
		encoders.put("MD4".new org.springframework.security.crypto.password.Md4PasswordEncoder());
		encoders.put("MD5".new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
		encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
		encoders.put("pbkdf2".new Pbkdf2PasswordEncoder());
		encoders.put("scrypt".new SCryptPasswordEncoder());
		encoders.put("SHA-1".new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
		encoders.put("SHA-256".new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
		encoders.put("sha256".new org.springframework.security.crypto.password.StandardPasswordEncoder());

		return new DelegatingPasswordEncoder(encodingId, encoders);
	}
Copy the code

From the above can be seen very specifically DelegatingPasswordEncoder provide password encoding. Bcrypt is used for encoding by default. We can see why the {noop}12345 we used in the previous article matches our foreground input 12345. What good would that do? This allows for a scenario where one day we replace or rotate the rules of cryptography. Existing users will not be affected. How does Spring Security configure the PasswordEncoder?

4. Spring Security loads the rules of PasswordEncoder

We are in Spring Security configuration adapter WebSecurityConfigurerAdapter (the class I will carefully analyzing future articles Through https://felord.cn I found a place that references PasswordEncoderFactories, an internal PasswordEncoder implementation of LazyPasswordEncoder. The class is lazily loaded from the source code and is instantiated only when it is used. The rules for PasswordEncoder are found in the class’s internal methods.

        // Get the PasswordEncoder for the final work
		private PasswordEncoder getPasswordEncoder(a) {
			if (this.passwordEncoder ! =null) {
				return this.passwordEncoder;
			}
			PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
			if (passwordEncoder == null) {
				passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
			}
			this.passwordEncoder = passwordEncoder;
			return passwordEncoder;
		}
        // Getting beans from the Spring IoC container may not be available
		private <T> T getBeanOrNull(Class<T> type) {
			try {
				return this.applicationContext.getBean(type);
			} catch(NoSuchBeanDefinitionException notFound) {
				return null; }}Copy the code

The above two methods are summed up: if you can get from from the Spring IoC container PasswordEncoder Bean with the Bean as a coder, did not use DelegatingPasswordEncoder. The default is bcrypt. This algorithm is mentioned several times in this paper. And it’s Spring Security’s default. So what is it?

5. Bcrypt coding algorithm

A quick mention of Bcrypt, which uses The Blowfish encryption algorithm published by Bruce Schnell in 1993. The bcrypt algorithm randomly mixes the salt with the encrypted password, and does not need to provide the previous salt separately during verification, so it does not need to deal with the salt problem separately. The encrypted format is generally as follows:

  $2a$10$/bTVvqqlH9UiE0ZJZ7N2Me3RIgUCdgMheyTgV0B4cMCSokPa.6oCa
Copy the code

Where: $is a separator, meaningless; 2A is the bcrypt encryption version number; 10 is the value of cos (t); The next 22 are salt values; The next string is the ciphertext of the password.

5.1 bcrypt characteristics

  • bcryptOne characteristic is that it’s very slow. This greatly increases the difficulty of using the rainbow table to crack. In other words, this type of cryptic text has an intolerable time cost to crack. It is also important for developers to be aware that the duration is beyond the system’s tolerance. Is usuallyMD5Thousands of times more.
  • Use the same password every timebcryptThe codes, the ciphers are different. So you have two websites if you use bothbcryptThe ciphers are not the same, it does not make one site leak the ciphers because another site leaks the ciphers.

Therefore, from the characteristics of Bcrypt, its security intensity is very guaranteed.

6. Summary

Today we’ll take a look at password encodings in Spring Security. Found that bcrypt is used for encoding by default. The password authentication match is controlled by the encryption mode ID in the password cryptic prefix. You can also inject a Bean of type PasswordEncoder into the Spring IoC container for custom purposes. We also have some simple understanding of bcrypt algorithm and summarized its characteristics. We will learn more about Spring Security later. I have also replaced the demo of the previous article with database administration users. The relevant code can be obtained by following my public account :Felordcn reply ss02.

Follow Felordcn or https://felord.cn for more information