(tomcat) branch 9.0.x updated: Improve robustness of DIGEST authentication to system clock jumps.

[email protected]
Newsgroups gmane.comp.jakarta.tomcat.devel
Message-ID <178713031090.846289.12831034436818829186@gitbox3-he-fi.apache.org>
This is an automated email from the ASF dual-hosted git repository.

markt-asf pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/9.0.x by this push:
     new ec24533693 Improve robustness of DIGEST authentication to system clock jumps.
ec24533693 is described below

commit ec24533693c92ba92d8f2ba8d3df1c74f820a271
Author: Mark Thomas <[email protected]>
AuthorDate: Wed Aug 19 10:03:53 2026 +0100

    Improve robustness of DIGEST authentication to system clock jumps.
---
 .../authenticator/DigestAuthenticator.java         | 38 ++++++++++++----------
 webapps/docs/changelog.xml                         |  4 +++
 2 files changed, 24 insertions(+), 18 deletions(-)

diff --git a/java/org/apache/catalina/authenticator/DigestAuthenticator.java b/java/org/apache/catalina/authenticator/DigestAuthenticator.java
index 7890dffbb2..fdcacb2a41 100644
--- a/java/org/apache/catalina/authenticator/DigestAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/DigestAuthenticator.java
@@ -28,6 +28,7 @@ import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -98,7 +99,7 @@ public class DigestAuthenticator extends AuthenticatorBase {
     /**
      * The last timestamp used to generate a nonce. Each nonce should get a unique timestamp.
      */
-    protected long lastTimestamp = 0;
+    protected long lastTimestamp = System.nanoTime() - 1;
 
     /**
      * Lock object used to ensure unique timestamps for nonce generation.
@@ -127,7 +128,7 @@ public class DigestAuthenticator extends AuthenticatorBase {
     /**
      * How long server nonces are valid for in milliseconds. Defaults to 5 minutes.
      */
-    protected long nonceValidity = 5 * 60 * 1000;
+    protected long nonceValidity = TimeUnit.MINUTES.toMillis(5);
 
 
     /**
@@ -431,23 +432,23 @@ public class DigestAuthenticator extends AuthenticatorBase {
      */
     protected String generateNonce(Request request) {
 
-        long currentTime = System.currentTimeMillis();
+        long nanoTime = System.nanoTime();
 
         synchronized (lastTimestampLock) {
-            if (currentTime > lastTimestamp) {
-                lastTimestamp = currentTime;
+            if (nanoTime > lastTimestamp) {
+                lastTimestamp = nanoTime;
             } else {
-                currentTime = ++lastTimestamp;
+                nanoTime = ++lastTimestamp;
             }
         }
 
-        String ipTimeKey = request.getRemoteAddr() + ":" + currentTime + ":" + getKey();
+        String ipTimeKey = request.getRemoteAddr() + ":" + nanoTime + ":" + getKey();
 
         // Note: The digest used to generate the nonce is independent of the digest used for authentication.
         byte[] buffer = ConcurrentMessageDigest.digest(NONCE_DIGEST, ipTimeKey.getBytes(StandardCharsets.ISO_8859_1));
-        String nonce = currentTime + ":" + HexUtils.toHexString(buffer);
+        String nonce = nanoTime + ":" + HexUtils.toHexString(buffer);
 
-        NonceInfo info = new NonceInfo(currentTime, getNonceCountWindowSize());
+        NonceInfo info = new NonceInfo(nanoTime, getNonceCountWindowSize());
         synchronized (nonces) {
             nonces.put(nonce, info);
         }
@@ -531,19 +532,20 @@ public class DigestAuthenticator extends AuthenticatorBase {
         nonces = new LinkedHashMap<String,DigestAuthenticator.NonceInfo>() {
 
             private static final long serialVersionUID = 1L;
-            private static final long LOG_SUPPRESS_TIME = 5 * 60 * 1000;
+            private static final long LOG_SUPPRESS_TIME = TimeUnit.MINUTES.toNanos(5);
 
-            private long lastLog = 0;
+            private long lastLog = System.nanoTime() - 1;
 
             @Override
             protected boolean removeEldestEntry(Map.Entry<String,NonceInfo> eldest) {
                 // This is called from a sync so keep it simple
-                long currentTime = System.currentTimeMillis();
+                long nanoTime = System.nanoTime();
                 if (size() > getNonceCacheSize()) {
-                    if (lastLog < currentTime && currentTime - eldest.getValue().getTimestamp() < getNonceValidity()) {
+                    long nonceValidityNanos = TimeUnit.MILLISECONDS.toNanos(getNonceValidity());
+                    if ((nanoTime - lastLog) > 0 && nanoTime - eldest.getValue().getTimestamp() < nonceValidityNanos) {
                         // Replay attack is possible
                         log.warn(sm.getString("digestAuthenticator.cacheRemove"));
-                        lastLog = currentTime + LOG_SUPPRESS_TIME;
+                        lastLog = nanoTime + LOG_SUPPRESS_TIME;
                     }
                     return true;
                 }
@@ -566,7 +568,7 @@ public class DigestAuthenticator extends AuthenticatorBase {
     public static class DigestInfo {
 
         private final String opaque;
-        private final long nonceValidity;
+        private final long nonceValidityNanos;
         private final String key;
         private final Map<String,NonceInfo> nonces;
         private final boolean validateUri;
@@ -598,7 +600,7 @@ public class DigestAuthenticator extends AuthenticatorBase {
         public DigestInfo(String opaque, long nonceValidity, String key, Map<String,NonceInfo> nonces,
                 boolean validateUri) {
             this.opaque = opaque;
-            this.nonceValidity = nonceValidity;
+            this.nonceValidityNanos = TimeUnit.MILLISECONDS.toNanos(nonceValidity);
             this.key = key;
             this.nonces = nonces;
             this.validateUri = validateUri;
@@ -728,8 +730,8 @@ public class DigestAuthenticator extends AuthenticatorBase {
                 return false;
             }
             String digestclientIpTimeKey = nonce.substring(i + 1);
-            long currentTime = System.currentTimeMillis();
-            if ((currentTime - nonceTime) > nonceValidity) {
+            long nanoTime = System.nanoTime();
+            if ((nanoTime - nonceTime) > nonceValidityNanos) {
                 nonceStale = true;
                 synchronized (nonces) {
                     nonces.remove(nonce);
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 53899a777c..b5d9423779 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -131,6 +131,10 @@
         Align web.xml logging output with the new <code>Context</code> attribute
         <code>urlPatternsProvidedInDecodedForm</code>. (markt)
       </fix>
+      <fix>
+        Improve robustness of DIGEST authentication to system clock jumps.
+        (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Coyote">
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.