ramackri opened a new pull request, #1081: URL: https://github.com/apache/ranger/pull/1081
## Summary - Remove logging of the full serialized JWT bearer token (`header.payload.signature`) when `RangerJwtAuthHandler` validation fails (CWE-532). - Log only non-sensitive metadata plus a SHA-256 hash for correlation: `subject`, `audience`, `issuer`, `keyId`, `jwtId`, `tokenHash`. - Add null-safety in `validateAudiences()` when a token has no `aud` claim but audiences are configured. - Add unit tests for safe logging and missing-audience handling. **JIRA:** https://issues.apache.org/jira/browse/RANGER-5693 ## Problem On validation failure, `RangerJwtAuthHandler.authenticate()` logged: ``` RangerJwtAuthHandler.authenticate(): Validation failed for JWT token: [<full serialized JWT>] ``` `validateToken()` checks claims in order: **expiration → signature → audience → issuer**. A cryptographically valid, unexpired token rejected only for **audience mismatch** still hit this WARN path. The full bearer credential in logs could be replayed against another Ranger JWT consumer that trusts the same signing material and accepts the token's audience. Reported via ASF Security (Andrew Rukin, Arenadata). Verified on Ranger 2.8.0; same pattern existed on 2.8, 2.9, and master. ## Fix **Before:** ```java LOG.warn("... Validation failed for JWT token: [{}] ", jwtToken.serialize()); ``` **After:** ```java LOG.warn("JWT validation failed ({})", safeJwtLogContext(jwtToken, serializedJWT)); ``` `safeJwtLogContext()` emits diagnostic fields only, for example: ``` JWT validation failed (subject=f015_replay_user, audience=service-a, issuer=test-issuer, keyId=kid-abc, jwtId=jti-123, tokenHash=<sha256-hex>) ``` The raw bearer string is never written to operational logs. `tokenHash` is a one-way SHA-256 digest used only for log correlation. ### Null-safety - `validateAudiences()`: guard `tokenAudienceList != null` before iterating (avoids NPE when `aud` claim is absent). - `safeJwtLogContext()`: guard `jwtToken.getHeader()` before reading `keyId`; use `StringUtils.join()` for audience list (JDK 8 compatible, tolerates null list entries). ## Files changed (2) | File | Change | |------|--------| | `ranger-authn/.../RangerJwtAuthHandler.java` | Safe logging, `safeJwtLogContext()`, `sha256Hex()`, audience null guard | | `ranger-authn/.../TestRangerJwtAuthHandler.java` | Unit tests for safe context and missing audience | ## Test plan ### Unit tests (automated) ```bash mvn -pl ranger-authn test -Dtest=TestRangerJwtAuthHandler ``` **Result:** 6/6 tests pass. | Test | Verifies | |------|----------| | `validateIssuerTrue_whenIssuerNotConfigured` | Issuer validation baseline | | `validateIssuerTrue_whenIssuerMatches` | Matching issuer accepted | | `validateIssuerFalse_whenIssuerDoesNotMatch` | Mismatched issuer rejected | | `validateIssuerFalse_whenJwtClaimsCannotBeParsed` | Malformed claims handled | | `validateAudiencesFalse_whenTokenAudienceMissingAndAudiencesConfigured` | No NPE when `aud` missing; returns false | | `safeJwtLogContext_includesMetadataWithoutRawToken` | Metadata present; raw token/header/payload/signature absent | ### Static analysis ```bash mvn -pl ranger-authn checkstyle:check pmd:check ``` | Check | Result | |-------|--------| | **PMD** | PASS — no violations in changed files | | **Checkstyle** | `TestRangerJwtAuthHandler.java`: 0 violations. `RangerJwtAuthHandler.java`: only pre-existing module violations plus `LineLength` on new single-line statements (intentional, no line-wrapping) | ### Reporter-equivalent manual E2E (recommended before release) Mirrors the private security report verification: 1. Generate RS256 key pair and PEM certificate for `jwt.public-key`. 2. Create JWT: `sub=f015_replay_user`, `aud=service-a`, future `exp`, valid RS256 signature. 3. **Handler A** (`jwt.audiences=service-b`): call `RangerDefaultJwtAuthHandler.authenticate("Bearer <token>")` → expect `null`. 4. Inspect logs → expect `JWT validation failed (subject=..., audience=service-a, keyId=..., tokenHash=...)` and **no** full serialized JWT. 5. **Handler B** (`jwt.audiences=service-a`): same token → expect `f015_replay_user` (proves token was live; only logging changed). ### Live HTTP E2E (optional) Deploy patched `ranger-authn` with PDP or Admin JWT auth enabled: ```bash # Wrong audience — expect 401, no raw token in logs curl -H "Authorization: Bearer $TOKEN" http://localhost:6500/policy/evaluate grep -F "$TOKEN" pdp.log && echo FAIL || echo PASS # Correct audience — expect authenticated response # (reconfigure jwt.audiences=service-a or use second instance) ``` ### Regression scope - `RangerDefaultJwtAuthHandler` (Admin `RangerJwtAuthFilter`, PDP `JwtAuthNHandler`, audit paths) all delegate to the fixed `authenticate(String)` path. - Other JWT filters (`RangerSSOAuthenticationFilter`, `AuditJwtAuthFilter`) already log failure reasons without serializing tokens; unchanged. ## Security / disclosure Private security report (CWE-532). Coordinate advisory/CVE and backports per PMC process. Credit reporter **Andrew Rukin (Arenadata)** in advisory if accepted. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
