Ver código fonte

Update default configuration for Argon2PasswordEncoder

The recommended minimums for Argon2, as per OWASP Cheat Sheet Series (https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html), are:
Use Argon2id with a minimum configuration of 15 MiB of memory, an iteration count of 2, and 1 degree of parallelism.

Previous default configuration:
memory=4, iterations=3, parallelism=1

New default configuration:
memory=16, iterations=2, parallelism=1

Issue gh-10506
Joe Grandja 2 anos atrás
pai
commit
2ea62d0f8b

+ 41 - 4
crypto/src/main/java/org/springframework/security/crypto/argon2/Argon2PasswordEncoder.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -52,9 +52,9 @@ public class Argon2PasswordEncoder implements PasswordEncoder {
 
 	private static final int DEFAULT_PARALLELISM = 1;
 
-	private static final int DEFAULT_MEMORY = 1 << 12;
+	private static final int DEFAULT_MEMORY = 1 << 14;
 
-	private static final int DEFAULT_ITERATIONS = 3;
+	private static final int DEFAULT_ITERATIONS = 2;
 
 	private final Log logger = LogFactory.getLog(getClass());
 
@@ -68,10 +68,24 @@ public class Argon2PasswordEncoder implements PasswordEncoder {
 
 	private final BytesKeyGenerator saltGenerator;
 
+	/**
+	 * Constructs an Argon2 password encoder with a salt length of 16 bytes, a hash length
+	 * of 32 bytes, parallelism of 1, memory cost of 1 << 12 and 3 iterations.
+	 * @deprecated Use {@link #defaultsForSpringSecurity_v5_2()} instead
+	 */
+	@Deprecated
 	public Argon2PasswordEncoder() {
-		this(DEFAULT_SALT_LENGTH, DEFAULT_HASH_LENGTH, DEFAULT_PARALLELISM, DEFAULT_MEMORY, DEFAULT_ITERATIONS);
+		this(16, 32, 1, 1 << 12, 3);
 	}
 
+	/**
+	 * Constructs an Argon2 password encoder with the provided parameters.
+	 * @param saltLength the salt length (in bytes)
+	 * @param hashLength the hash length (in bytes)
+	 * @param parallelism the parallelism
+	 * @param memory the memory cost
+	 * @param iterations the number of iterations
+	 */
 	public Argon2PasswordEncoder(int saltLength, int hashLength, int parallelism, int memory, int iterations) {
 		this.hashLength = hashLength;
 		this.parallelism = parallelism;
@@ -80,6 +94,29 @@ public class Argon2PasswordEncoder implements PasswordEncoder {
 		this.saltGenerator = KeyGenerators.secureRandom(saltLength);
 	}
 
+	/**
+	 * Constructs an Argon2 password encoder with a salt length of 16 bytes, a hash length
+	 * of 32 bytes, parallelism of 1, memory cost of 1 << 12 and 3 iterations.
+	 * @return the {@link Argon2PasswordEncoder}
+	 * @since 5.8
+	 * @deprecated Use {@link #defaultsForSpringSecurity_v5_8()} instead
+	 */
+	@Deprecated
+	public static Argon2PasswordEncoder defaultsForSpringSecurity_v5_2() {
+		return new Argon2PasswordEncoder(16, 32, 1, 1 << 12, 3);
+	}
+
+	/**
+	 * Constructs an Argon2 password encoder with a salt length of 16 bytes, a hash length
+	 * of 32 bytes, parallelism of 1, memory cost of 1 << 14 and 2 iterations.
+	 * @return the {@link Argon2PasswordEncoder}
+	 * @since 5.8
+	 */
+	public static Argon2PasswordEncoder defaultsForSpringSecurity_v5_8() {
+		return new Argon2PasswordEncoder(DEFAULT_SALT_LENGTH, DEFAULT_HASH_LENGTH, DEFAULT_PARALLELISM, DEFAULT_MEMORY,
+				DEFAULT_ITERATIONS);
+	}
+
 	@Override
 	public String encode(CharSequence rawPassword) {
 		byte[] salt = this.saltGenerator.generateKey();

+ 6 - 3
crypto/src/main/java/org/springframework/security/crypto/factory/PasswordEncoderFactories.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2017 the original author or authors.
+ * Copyright 2002-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -58,7 +58,9 @@ public final class PasswordEncoderFactories {
 	 * <li>SHA-256 - {@code new MessageDigestPasswordEncoder("SHA-256")}</li>
 	 * <li>sha256 -
 	 * {@link org.springframework.security.crypto.password.StandardPasswordEncoder}</li>
-	 * <li>argon2 - {@link Argon2PasswordEncoder}</li>
+	 * <li>argon2 - {@link Argon2PasswordEncoder#defaultsForSpringSecurity_v5_2()}</li>
+	 * <li>argon2@SpringSecurity_v5_8 -
+	 * {@link Argon2PasswordEncoder#defaultsForSpringSecurity_v5_8()}</li>
 	 * </ul>
 	 * @return the {@link PasswordEncoder} to use
 	 */
@@ -77,7 +79,8 @@ public final class PasswordEncoderFactories {
 		encoders.put("SHA-256",
 				new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
 		encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
-		encoders.put("argon2", new Argon2PasswordEncoder());
+		encoders.put("argon2", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2());
+		encoders.put("argon2@SpringSecurity_v5_8", Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8());
 		return new DelegatingPasswordEncoder(encodingId, encoders);
 	}
 

+ 12 - 3
crypto/src/test/java/org/springframework/security/crypto/argon2/Argon2PasswordEncoderTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -39,7 +39,7 @@ public class Argon2PasswordEncoderTests {
 	@Mock
 	private BytesKeyGenerator keyGeneratorMock;
 
-	private Argon2PasswordEncoder encoder = new Argon2PasswordEncoder();
+	private Argon2PasswordEncoder encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2();
 
 	@Test
 	public void encodeDoesNotEqualPassword() {
@@ -127,6 +127,15 @@ public class Argon2PasswordEncoderTests {
 				"$argon2id$v=19$m=512,t=5,p=4$QUFBQUFBQUFBQUFBQUFBQQ$PNv4C3K50bz3rmON+LtFpdisD7ePieLNq+l5iUHgc1k");
 	}
 
+	@Test
+	public void encodeWhenUsingPredictableSaltWithDefaultsForSpringSecurity_v5_8ThenEqualTestHash() throws Exception {
+		this.encoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_8();
+		injectPredictableSaltGen();
+		String hash = this.encoder.encode("sometestpassword");
+		assertThat(hash).isEqualTo(
+				"$argon2id$v=19$m=16384,t=2,p=1$QUFBQUFBQUFBQUFBQUFBQQ$zGt5MiNPSUOo4/7jBcJMayCPfcsLJ4c0WUxhwGDIYPw");
+	}
+
 	@Test
 	public void upgradeEncodingWhenSameEncodingThenFalse() {
 		String hash = this.encoder.encode("password");
@@ -135,7 +144,7 @@ public class Argon2PasswordEncoderTests {
 
 	@Test
 	public void upgradeEncodingWhenSameStandardParamsThenFalse() {
-		Argon2PasswordEncoder newEncoder = new Argon2PasswordEncoder();
+		Argon2PasswordEncoder newEncoder = Argon2PasswordEncoder.defaultsForSpringSecurity_v5_2();
 		String hash = this.encoder.encode("password");
 		assertThat(newEncoder.upgradeEncoding(hash)).isFalse();
 	}

+ 7 - 1
crypto/src/test/java/org/springframework/security/crypto/factory/PasswordEncoderFactoriesTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2019 the original author or authors.
+ * Copyright 2002-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -105,4 +105,10 @@ public class PasswordEncoderFactoriesTests {
 		assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
 	}
 
+	@Test
+	public void matchesWhenArgon2SpringSecurity_v5_8ThenWorks() {
+		String encodedPassword = "{argon2@SpringSecurity_v5_8}$argon2id$v=19$m=16384,t=2,p=1$v7fN5p91BQbdbA2HfdSPRg$MULpa02CO/6FKfqwuerCFvS7OhMxGFCKUOoWfzt86Rc";
+		assertThat(this.encoder.matches(this.rawPassword, encodedPassword)).isTrue();
+	}
+
 }