This is an automated email from the ASF dual-hosted git repository.
ramackri pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push:
new 672332928 RANGER-5693: Stop logging full JWT bearer tokens on
validation failure (#1081)
672332928 is described below
commit 6723329285d1287df59e2424200e218e768e7dda
Author: Ramachandran Krishnan <[email protected]>
AuthorDate: Sun Jul 19 09:52:22 2026 +0530
RANGER-5693: Stop logging full JWT bearer tokens on validation failure
(#1081)
* RANGER-5693: Stop logging full JWT bearer tokens on validation failure
Replace jwtToken.serialize() in the validation-failure WARN path with
safeJwtLogContext() that logs only non-sensitive metadata and a SHA-256
hash for log correlation. Add null-safety for missing audience claims
and unit tests for the safe logging path.
* RANGER-5693: Drop tokenHash from JWT validation failure logs.
Per review feedback, remove sha256Hex/tokenHash from safeJwtLogContext;
jwtId is sufficient for log correlation when present.
* Remove tokenHash assertion from JWT test
---------
Co-authored-by: ramk <[email protected]>
---
.../authz/handler/jwt/RangerJwtAuthHandler.java | 39 ++++++++++++++++----
.../handler/jwt/TestRangerJwtAuthHandler.java | 42 ++++++++++++++++++++++
2 files changed, 74 insertions(+), 7 deletions(-)
diff --git
a/ranger-authn/src/main/java/org/apache/ranger/authz/handler/jwt/RangerJwtAuthHandler.java
b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/jwt/RangerJwtAuthHandler.java
index d921c7063..22c6289fa 100644
---
a/ranger-authn/src/main/java/org/apache/ranger/authz/handler/jwt/RangerJwtAuthHandler.java
+++
b/ranger-authn/src/main/java/org/apache/ranger/authz/handler/jwt/RangerJwtAuthHandler.java
@@ -19,6 +19,7 @@
package org.apache.ranger.authz.handler.jwt;
import com.nimbusds.jose.JOSEException;
+import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier;
@@ -30,6 +31,7 @@
import com.nimbusds.jose.proc.JWSVerificationKeySelector;
import com.nimbusds.jose.proc.SecurityContext;
import com.nimbusds.jose.util.X509CertUtils;
+import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
import org.apache.commons.lang3.StringUtils;
@@ -125,7 +127,7 @@ protected String authenticate(final String jwtAuthHeader) {
}
return userName;
} else {
- LOG.warn("RangerJwtAuthHandler.authenticate():
Validation failed for JWT token: [{}] ", jwtToken.serialize());
+ LOG.warn("JWT validation failed ({})",
safeJwtLogContext(jwtToken));
}
} catch (ParseException pe) {
LOG.warn("RangerJwtAuthHandler.authenticate(): Unable to
parse the JWT token", pe);
@@ -260,13 +262,15 @@ protected boolean validateAudiences(final SignedJWT
jwtToken) {
valid = true;
} else {
// if any of the configured audiences is found then consider
it acceptable
- for (String aud : tokenAudienceList) {
- if (audiences.contains(aud)) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("JWT token audience has been
successfully validated.");
+ if (tokenAudienceList != null) {
+ for (String aud : tokenAudienceList) {
+ if (audiences.contains(aud)) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("JWT token audience has been
successfully validated.");
+ }
+ valid = true;
+ break;
}
- valid = true;
- break;
}
}
if (!valid) {
@@ -304,6 +308,27 @@ protected boolean validateIssuer(final SignedJWT jwtToken)
{
return valid;
}
+ /**
+ * Build non-sensitive JWT metadata for operational logs.
+ * Never log the raw bearer token.
+ *
+ * @param jwtToken parsed JWT used to extract claim metadata
+ * @return safe diagnostic string for log output
+ */
+ protected String safeJwtLogContext(final SignedJWT jwtToken) {
+ try {
+ JWTClaimsSet claims = jwtToken.getJWTClaimsSet();
+ JWSHeader header = jwtToken.getHeader();
+ String keyId = header != null ? header.getKeyID() : null;
+ List<String> tokenAudiences = claims.getAudience();
+ String audience = tokenAudiences == null ||
tokenAudiences.isEmpty() ? null : StringUtils.join(tokenAudiences, ",");
+
+ return String.format("subject=%s, audience=%s, issuer=%s,
keyId=%s, jwtId=%s", claims.getSubject(), audience, claims.getIssuer(), keyId,
claims.getJWTID());
+ } catch (ParseException pe) {
+ return "claims_unparseable";
+ }
+ }
+
/**
* Validate that the expiration time of the JWT has not been violated. If
* it has, then throw an AuthenticationException. Override this method in
diff --git
a/ranger-authn/src/test/java/org/apache/ranger/authz/handler/jwt/TestRangerJwtAuthHandler.java
b/ranger-authn/src/test/java/org/apache/ranger/authz/handler/jwt/TestRangerJwtAuthHandler.java
index 4841a28fa..e2499f194 100644
---
a/ranger-authn/src/test/java/org/apache/ranger/authz/handler/jwt/TestRangerJwtAuthHandler.java
+++
b/ranger-authn/src/test/java/org/apache/ranger/authz/handler/jwt/TestRangerJwtAuthHandler.java
@@ -30,9 +30,11 @@
import javax.servlet.http.HttpServletRequest;
+import java.util.Arrays;
import java.util.Date;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestRangerJwtAuthHandler {
@@ -50,6 +52,14 @@ public RangerAuth authenticate(HttpServletRequest request) {
boolean callValidateIssuer(SignedJWT jwt) {
return validateIssuer(jwt);
}
+
+ boolean callValidateAudiences(SignedJWT jwt) {
+ return validateAudiences(jwt);
+ }
+
+ String callSafeJwtLogContext(SignedJWT jwt) {
+ return safeJwtLogContext(jwt);
+ }
}
private static SignedJWT jwtWithIssuer(String issuer) {
@@ -106,4 +116,36 @@ void validateIssuerFalse_whenJwtClaimsCannotBeParsed()
throws Exception {
assertFalse(handler.callValidateIssuer(badJwt));
}
+
+ @Test
+ void
validateAudiencesFalse_whenTokenAudienceMissingAndAudiencesConfigured() {
+ TestHandler handler = new TestHandler();
+ handler.audiences = Arrays.asList("service-a");
+
+ SignedJWT jwt = jwtWithIssuer("test-issuer");
+
+ assertFalse(handler.callValidateAudiences(jwt));
+ }
+
+ @Test
+ void safeJwtLogContext_includesMetadataWithoutRawToken() throws Exception {
+ TestHandler handler = new TestHandler();
+ String header = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImtpZC1hYmMifQ";
+ String payload =
"eyJzdWIiOiJmMDE1X3JlcGxheV91c2VyIiwiYXVkIjoic2VydmljZS1hIiwiaXNzIjoidGVzdC1pc3N1ZXIiLCJqdGkiOiJqdGktMTIzIiwiZXhwIjoxOTk5OTk5OTk5fQ";
+ String signature = "abcd";
+ String serialized = header + "." + payload + "." + signature;
+
+ String context =
handler.callSafeJwtLogContext(SignedJWT.parse(serialized));
+
+ assertNotNull(context);
+ assertTrue(context.contains("subject=f015_replay_user"));
+ assertTrue(context.contains("audience=service-a"));
+ assertTrue(context.contains("issuer=test-issuer"));
+ assertTrue(context.contains("keyId=kid-abc"));
+ assertTrue(context.contains("jwtId=jti-123"));
+ assertFalse(context.contains(serialized));
+ assertFalse(context.contains(header));
+ assertFalse(context.contains(payload));
+ assertFalse(context.contains(signature));
+ }
}