This is an automated email from the ASF dual-hosted git repository. ramackri pushed a commit to branch RANGER-5693-backport-ranger-2.9 in repository https://gitbox.apache.org/repos/asf/ranger.git
commit a81bbaa7ef0445ccd1a4f684de9b02ad60b1eee9 Author: ramk <[email protected]> AuthorDate: Sun Jul 19 09:56:15 2026 +0530 RANGER-5693: Stop logging full JWT bearer tokens on validation failure Back-port of 6723329285d1287df59e2424200e218e768e7dda from master. Replace jwtToken.serialize() in the validation-failure WARN path with safeJwtLogContext() that logs only non-sensitive metadata. Add null-safety for missing audience claims and unit tests for the safe logging path. --- .../authz/handler/jwt/RangerJwtAuthHandler.java | 42 +++++++-- .../handler/jwt/TestRangerJwtAuthHandler.java | 102 +++++++++++++++++++++ 2 files changed, 135 insertions(+), 9 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 5480fcd79..651006b10 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 @@ -33,6 +33,7 @@ import org.slf4j.LoggerFactory; 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; @@ -42,6 +43,7 @@ import com.nimbusds.jose.proc.JWSKeySelector; import com.nimbusds.jose.proc.JWSVerificationKeySelector; import com.nimbusds.jose.proc.SecurityContext; +import com.nimbusds.jwt.JWTClaimsSet; import com.nimbusds.jwt.SignedJWT; import com.nimbusds.jwt.proc.ConfigurableJWTProcessor; @@ -113,7 +115,7 @@ protected AuthenticationToken authenticate(final String jwtAuthHeader) { } token = new AuthenticationToken(userName, userName, TYPE); } 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); @@ -244,15 +246,16 @@ protected boolean validateAudiences(final SignedJWT jwtToken) { if (audiences == null) { 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 any of the configured audiences is found then consider it acceptable + 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) { @@ -265,6 +268,27 @@ protected boolean validateAudiences(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 token 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 new file mode 100644 index 000000000..ef032bb25 --- /dev/null +++ b/ranger-authn/src/test/java/org/apache/ranger/authz/handler/jwt/TestRangerJwtAuthHandler.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.ranger.authz.handler.jwt; + +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.proc.JWSKeySelector; +import com.nimbusds.jose.proc.SecurityContext; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; +import com.nimbusds.jwt.proc.ConfigurableJWTProcessor; +import org.apache.ranger.authz.handler.RangerAuth; +import org.junit.jupiter.api.Test; + +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 { + static class TestHandler extends RangerJwtAuthHandler { + @Override + public ConfigurableJWTProcessor<SecurityContext> getJwtProcessor(JWSKeySelector<SecurityContext> keySelector) { + return null; + } + + @Override + public RangerAuth authenticate(HttpServletRequest request) { + return null; + } + + boolean callValidateAudiences(SignedJWT jwt) { + return validateAudiences(jwt); + } + + String callSafeJwtLogContext(SignedJWT jwt) { + return safeJwtLogContext(jwt); + } + } + + private static SignedJWT jwtWithIssuer(String issuer) { + JWTClaimsSet claims = new JWTClaimsSet.Builder() + .issuer(issuer) + .subject("user") + .expirationTime(new Date(System.currentTimeMillis() + 60_000)) + .build(); + + return new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claims); + } + + @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)); + } +}
