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">


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to