LOG4J2-3056 Refactor MD5 usage for sharing sensitive information (#484)
diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/NameUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/NameUtil.java
index 31cbf7e..75cd2d4 100644
--- a/log4j-api/src/main/java/org/apache/logging/log4j/util/NameUtil.java
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/NameUtil.java
@@ -16,19 +16,17 @@
  */
 package org.apache.logging.log4j.util;
 
+import java.nio.charset.Charset;
 import java.security.MessageDigest;
-
-import org.apache.logging.log4j.util.Strings;
+import java.security.NoSuchAlgorithmException;
+import java.util.Objects;
 
 /**
  *
  */
 public final class NameUtil {
 
-    private static final int MASK = 0xff;
-
-    private NameUtil() {
-    }
+    private NameUtil() {}
 
     public static String getSubName(final String name) {
         if (Strings.isEmpty(name)) {
@@ -38,22 +36,41 @@
         return i > 0 ? name.substring(0, i) : Strings.EMPTY;
     }
 
-    public static String md5(final String string) {
+    /**
+     * Calculates the <a href="https://en.wikipedia.org/wiki/MD5">MD5</a> hash
+     * of the given input string encoded using the default platform
+     * {@link Charset charset}.
+     * <p>
+     * <b>MD5 has severe vulnerabilities and should not be used for sharing any
+     * sensitive information.</b> This function should only be used to create
+     * unique identifiers, e.g., configuration element names.
+     *
+     * @param input string to be hashed
+     * @return string composed of 32 hexadecimal digits of the calculated hash
+     */
+    public static String md5(final String input) {
+        Objects.requireNonNull(input, "input");
         try {
+            final byte[] inputBytes = input.getBytes();
             final MessageDigest digest = MessageDigest.getInstance("MD5");
-            digest.update(string.getBytes());
-            final byte[] bytes = digest.digest();
-            final StringBuilder md5 = new StringBuilder();
+            final byte[] bytes = digest.digest(inputBytes);
+            final StringBuilder md5 = new StringBuilder(bytes.length * 2);
             for (final byte b : bytes) {
-                final String hex = Integer.toHexString(MASK & b);
+                final String hex = Integer.toHexString(0xFF & b);
                 if (hex.length() == 1) {
                     md5.append('0');
                 }
                 md5.append(hex);
             }
             return md5.toString();
-        } catch (final Exception ex) {
-            return string;
+        }
+        // Every implementation of the Java platform is required to support MD5.
+        // Hence, this catch block should be unreachable.
+        // See https://docs.oracle.com/javase/8/docs/api/java/security/MessageDigest.html
+        // for details.
+        catch (final NoSuchAlgorithmException error) {
+            throw new RuntimeException(error);
         }
     }
+
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginAttributeVisitor.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginAttributeVisitor.java
index 76e9c31..f746f6a 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginAttributeVisitor.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginAttributeVisitor.java
@@ -19,7 +19,6 @@
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
 import org.apache.logging.log4j.plugins.inject.AbstractConfigurationInjector;
-import org.apache.logging.log4j.util.NameUtil;
 import org.apache.logging.log4j.util.StringBuilders;
 import org.apache.logging.log4j.util.Strings;
 
@@ -86,7 +85,7 @@
     }
 
     private void debugLog(final Object value) {
-        final Object debugValue = annotation.sensitive() ? NameUtil.md5(value + getClass().getName()) : value;
+        final Object debugValue = annotation.sensitive() ? "*****" : value;
         StringBuilders.appendKeyDqValue(debugLog, name, debugValue);
     }
 }
diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginBuilderAttributeVisitor.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginBuilderAttributeVisitor.java
index dc3aa06..d70080e 100644
--- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginBuilderAttributeVisitor.java
+++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginBuilderAttributeVisitor.java
@@ -20,7 +20,6 @@
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
 import org.apache.logging.log4j.plugins.inject.AbstractConfigurationInjector;
-import org.apache.logging.log4j.util.NameUtil;
 import org.apache.logging.log4j.util.StringBuilders;
 
 import java.util.Optional;
@@ -44,7 +43,7 @@
     }
 
     private void debugLog(final Object value) {
-        final Object debugValue = annotation.sensitive() ? NameUtil.md5(value + getClass().getName()) : value;
+        final Object debugValue = annotation.sensitive() ? "*****" : value;
         StringBuilders.appendKeyDqValue(debugLog, name, debugValue);
     }
 }
\ No newline at end of file
diff --git a/log4j-couchdb/src/main/java/org/apache/logging/log4j/couchdb/CouchDbProvider.java b/log4j-couchdb/src/main/java/org/apache/logging/log4j/couchdb/CouchDbProvider.java
index 6eb207e..179096c 100644
--- a/log4j-couchdb/src/main/java/org/apache/logging/log4j/couchdb/CouchDbProvider.java
+++ b/log4j-couchdb/src/main/java/org/apache/logging/log4j/couchdb/CouchDbProvider.java
@@ -28,7 +28,6 @@
 import org.apache.logging.log4j.plugins.validation.constraints.ValidPort;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.LoaderUtil;
-import org.apache.logging.log4j.util.NameUtil;
 import org.apache.logging.log4j.util.Strings;
 import org.lightcouch.CouchDbClient;
 import org.lightcouch.CouchDbProperties;
@@ -106,7 +105,6 @@
                     final CouchDbProperties properties = (CouchDbProperties) object;
                     client = new CouchDbClient(properties);
                     description = "uri=" + client.getDBUri() + ", username=" + properties.getUsername()
-                            + ", passwordHash=" + NameUtil.md5(password + CouchDbProvider.class.getName())
                             + ", maxConnections=" + properties.getMaxConnections() + ", connectionTimeout="
                             + properties.getConnectionTimeout() + ", socketTimeout=" + properties.getSocketTimeout();
                 } else if (object == null) {
@@ -149,8 +147,7 @@
             }
 
             client = new CouchDbClient(databaseName, false, protocol, server, portInt, username, password);
-            description = "uri=" + client.getDBUri() + ", username=" + username + ", passwordHash="
-                    + NameUtil.md5(password + CouchDbProvider.class.getName());
+            description = "uri=" + client.getDBUri() + ", username=" + username;
         } else {
             LOGGER.error("No factory method was provided so the database name is required.");
             return null;
diff --git a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEmbeddedManager.java b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEmbeddedManager.java
index 7147a41..c20dcfb 100644
--- a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEmbeddedManager.java
+++ b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeEmbeddedManager.java
@@ -82,6 +82,17 @@
             throw new IllegalArgumentException("Cannot configure both Agents and Properties.");

         }

 

+        final String managerName = createManagerName(name, agents, properties);

+        return getManager(managerName, factory,

+                new FactoryData(name, agents, properties, batchSize, dataDir));

+

+    }

+

+    private static String createManagerName(

+            final String name,

+            final Agent[] agents,

+            final Property[] properties) {

+

         final StringBuilder sb = new StringBuilder();

         boolean first = true;

 

@@ -106,8 +117,9 @@
             }

             sb.append(NameUtil.md5(props.toString()));

         }

-        return getManager(sb.toString(), factory,

-                new FactoryData(name, agents, properties, batchSize, dataDir));

+

+        return sb.toString();

+

     }

 

     @Override

diff --git a/log4j-mongodb3/src/main/java/org/apache/logging/log4j/mongodb3/MongoDb3Provider.java b/log4j-mongodb3/src/main/java/org/apache/logging/log4j/mongodb3/MongoDb3Provider.java
index d38cab8..52e05b1 100644
--- a/log4j-mongodb3/src/main/java/org/apache/logging/log4j/mongodb3/MongoDb3Provider.java
+++ b/log4j-mongodb3/src/main/java/org/apache/logging/log4j/mongodb3/MongoDb3Provider.java
@@ -32,7 +32,6 @@
 import org.apache.logging.log4j.plugins.validation.constraints.ValidPort;
 import org.apache.logging.log4j.status.StatusLogger;
 import org.apache.logging.log4j.util.LoaderUtil;
-import org.apache.logging.log4j.util.NameUtil;
 import org.apache.logging.log4j.util.Strings;
 import org.bson.codecs.configuration.CodecRegistries;
 import org.bson.codecs.configuration.CodecRegistry;
@@ -177,8 +176,7 @@
                 MongoCredential mongoCredential = null;
                 description = "database=" + databaseName;
                 if (Strings.isNotEmpty(userName) && Strings.isNotEmpty(password)) {
-                    description += ", username=" + userName + ", passwordHash="
-                            + NameUtil.md5(password + MongoDb3Provider.class.getName());
+                    description += ", username=" + userName;
                     mongoCredential = MongoCredential.createCredential(userName, databaseName, password.toCharArray());
                 }
                 try {
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/inject/PluginAttributeInjector.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/inject/PluginAttributeInjector.java
index af1144f..87aad2e 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/inject/PluginAttributeInjector.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/inject/PluginAttributeInjector.java
@@ -81,7 +81,7 @@
     }
 
     private void debugLog(final Object value) {
-        final Object debugValue = annotation.sensitive() ? NameUtil.md5(value + getClass().getName()) : value;
+        final Object debugValue = annotation.sensitive() ? "*****" : value;
         StringBuilders.appendKeyDqValue(debugLog, name, debugValue);
     }
 
diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/inject/PluginBuilderAttributeInjector.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/inject/PluginBuilderAttributeInjector.java
index f1d734d..adf41f6 100644
--- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/inject/PluginBuilderAttributeInjector.java
+++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/inject/PluginBuilderAttributeInjector.java
@@ -38,7 +38,7 @@
     }
 
     private void debugLog(final Object value) {
-        final Object debugValue = annotation.sensitive() ? NameUtil.md5(value + getClass().getName()) : value;
+        final Object debugValue = annotation.sensitive() ? "*****" : value;
         StringBuilders.appendKeyDqValue(debugLog, name, debugValue);
     }
 }
diff --git a/log4j-smtp/src/main/java/org/apache/logging/log4j/smtp/appender/SmtpManager.java b/log4j-smtp/src/main/java/org/apache/logging/log4j/smtp/appender/SmtpManager.java
index d44a35d..f8c0154 100644
--- a/log4j-smtp/src/main/java/org/apache/logging/log4j/smtp/appender/SmtpManager.java
+++ b/log4j-smtp/src/main/java/org/apache/logging/log4j/smtp/appender/SmtpManager.java
@@ -108,7 +108,30 @@
             protocol = "smtp";
         }
 
+        final String managerName = createManagerName(to, cc, bcc, from, replyTo, subject, protocol, host, username, password, isDebug, filterName);
+        final Serializer subjectSerializer = PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(subject).build();
+
+        return getManager(managerName, FACTORY, new FactoryData(to, cc, bcc, from, replyTo, subjectSerializer,
+            protocol, host, port, username, password, isDebug, numElements, sslConfiguration));
+
+    }
+
+    private static String createManagerName(
+            final String to,
+            final String cc,
+            final String bcc,
+            final String from,
+            final String replyTo,
+            final String subject,
+            final String protocol,
+            final String host,
+            final String username,
+            final String password,
+            final boolean isDebug,
+            final String filterName) {
+
         final StringBuilder sb = new StringBuilder();
+
         if (to != null) {
             sb.append(to);
         }
@@ -144,11 +167,9 @@
         sb.append(isDebug ? ":debug:" : "::");
         sb.append(filterName);
 
-        final String name = "SMTP:" + NameUtil.md5(sb.toString());
-        final Serializer subjectSerializer = PatternLayout.newSerializerBuilder().setConfiguration(config).setPattern(subject).build();
+        final String hash = NameUtil.md5(sb.toString());
+        return "SMTP:" + hash;
 
-        return getManager(name, FACTORY, new FactoryData(to, cc, bcc, from, replyTo, subjectSerializer,
-            protocol, host, port, username, password, isDebug, numElements, sslConfiguration));
     }
 
     /**
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index ad73bf7..56f48ce 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -170,6 +170,9 @@
     </release>
     <release version="2.15.0" date="2021-MM-DD" description="GA Release 2.15.0">
       <!-- ADDS -->
+      <action issue="LOG4J2-3056" dev="vy" type="add" due-to="Marcono1234">
+        Refactor MD5 usage for sharing sensitive information.
+      </action>
       <action issue="LOG4J2-3004" dev="vy" type="add">
         Add plugin support to JsonTemplateLayout.
       </action>