This is an automated email from the ASF dual-hosted git repository.
markt-asf pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push:
new a7d9a049bd Improve robustness of DIGEST authentication to system clock jumps.
a7d9a049bd is described below
commit a7d9a049bd081d4e3520aa796d7d5324a0bf258d
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 5cdc448bb3..6e175c7907 100644
--- a/java/org/apache/catalina/authenticator/DigestAuthenticator.java
+++ b/java/org/apache/catalina/authenticator/DigestAuthenticator.java
@@ -29,6 +29,7 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -99,7 +100,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.
@@ -128,7 +129,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);
/**
@@ -396,23 +397,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);
}
@@ -497,19 +498,20 @@ public class DigestAuthenticator extends AuthenticatorBase {
@Serial
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;
}
@@ -532,7 +534,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;
@@ -564,7 +566,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;
@@ -688,8 +690,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 a72a483a4d..af3d64da0f 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -258,6 +258,10 @@
active sessions, swap out the least recently used eligible sessions
first. Pull request <pr>1045</pr> provided by sainadh777. (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.