From 994abb0d00ff21b221072f1fe9abdc24cc4b69c8 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 31 Jan 2018 16:26:26 -0600 Subject: [PATCH] Document User.withDefaultPasswordEncoder unsafe for production Fixes: gh-4793 --- .../security/core/userdetails/User.java | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/core/src/main/java/org/springframework/security/core/userdetails/User.java b/core/src/main/java/org/springframework/security/core/userdetails/User.java index 1b418fb8d8..96c99e3492 100644 --- a/core/src/main/java/org/springframework/security/core/userdetails/User.java +++ b/core/src/main/java/org/springframework/security/core/userdetails/User.java @@ -28,6 +28,8 @@ import java.util.SortedSet; import java.util.TreeSet; import java.util.function.Function; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.CredentialsContainer; import org.springframework.security.core.SpringSecurityCoreVersion; @@ -61,6 +63,8 @@ public class User implements UserDetails, CredentialsContainer { private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID; + private static final Log logger = LogFactory.getLog(User.class); + // ~ Instance fields // ================================================================================================ private String password; @@ -266,7 +270,65 @@ public class User implements UserDetails, CredentialsContainer { return new UserBuilder(); } + /** + *

+ * WARNING: This method is considered unsafe for production and is only intended + * for sample applications. + *

+ *

+ * Creates a user and automatically encodes the provided password using + * {@code PasswordEncoderFactories.createDelegatingPasswordEncoder()}. For example: + *

+ * + *
+	 * 
+	 * UserDetails user = User.withDefaultPasswordEncoder()
+	 *     .username("user")
+	 *     .password("password")
+	 *     .roles("USER")
+	 *     .build();
+	 * // outputs {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
+	 * System.out.println(user.getPassword());
+	 * 
+	 * 
+ * + * This is not safe for production (it is intended for getting started experience) + * because the password "password" is compiled into the source code and then is + * included in memory at the time of creation. This means there are still ways to + * recover the plain text password making it unsafe. It does provide a slight + * improvement to using plain text passwords since the UserDetails password is + * securely hashed. This means if the UserDetails password is accidentally exposed, + * the password is securely stored. + * + * In a production setting, it is recommended to hash the password ahead of time. + * For example: + * + *
+	 * 
+	 * PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
+	 * // outputs {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG
+	 * // remember the password that is printed out and use in the next step
+	 * System.out.println(encoder.encode("password"));
+	 * 
+	 * 
+ * + *
+	 * 
+	 * UserDetails user = User.witUsername("user")
+	 *     .password("{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG")
+	 *     .roles("USER")
+	 *     .build();
+	 * 
+	 * 
+ * + * @return a UserBuilder that automatically encodes the password with the default + * PasswordEncoder + * @deprecated Using this method is not considered safe for production, but is + * acceptable for demos and getting started. For production purposes, ensure the + * password is encoded externally. See the method Javadoc for additional details. + */ public static UserBuilder withDefaultPasswordEncoder() { + logger.warn("User.withDefaultPasswordEncoder() is considered unsafe for production and is only intended for sample applications."); PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); return builder().passwordEncoder(encoder::encode); }