Skip to content

fix(auth): harden jwt-auth empty claims_to_verify and key-auth anonymous fallback credential stripping#13468

Open
shreemaan-abhishek wants to merge 3 commits into
apache:masterfrom
shreemaan-abhishek:feat/auth-claims-and-keyauth-hardening
Open

fix(auth): harden jwt-auth empty claims_to_verify and key-auth anonymous fallback credential stripping#13468
shreemaan-abhishek wants to merge 3 commits into
apache:masterfrom
shreemaan-abhishek:feat/auth-claims-and-keyauth-hardening

Conversation

@shreemaan-abhishek
Copy link
Copy Markdown
Contributor

Description

This PR contains two small hardening fixes in the auth plugins, each with tests.

1. jwt-auth: treat an empty claims_to_verify array as unset

verify_claims() falls back to the default claims (exp/nbf) when claims_to_verify is not configured. The guard used if not claims then, but an explicitly empty array ("claims_to_verify": []) is truthy in Lua, so it skipped the fallback and the validation loop iterated over nothing — meaning an expired token was accepted when claims_to_verify was set to []. The schema allows an empty array (no minItems), so this configuration is reachable.

The guard now treats an empty array the same as an unset value (if not claims or #claims == 0 then), restoring the default exp/nbf checks. Behaviour for tokens that legitimately omit exp/nbf is unchanged (they are validated only if present).

2. key-auth: strip credentials on anonymous-consumer fallback

When a request carried an invalid API key and key-auth fell back to the configured anonymous_consumer, the (invalid) credential was still forwarded upstream even with hide_credentials enabled: find_consumer() only strips credentials on the successful-auth path. The fix strips the credential before falling back to the anonymous consumer when hide_credentials is true. Since a request can carry the key in both the header and the query string, both are cleaned up.

Which issue(s) this PR fixes:

Fixes #

Checklist

  • I have explained the need for this PR and the problem it solves
  • I have explained the changes or the new features added to this PR
  • I have added tests corresponding to this change
  • I have updated the documentation to reflect this change
  • I have verified that this change is backward compatible (If not, please discuss on the APISIX mailing list first)

An explicitly empty `claims_to_verify` array ([]) is truthy in Lua, so
the `if not claims` guard in verify_claims() did not fall back to the
default claims (exp/nbf). As a result the validation loop iterated over
nothing and expired tokens were accepted when the array was set to [].

Treat an empty array the same as an unset value and fall back to the
default exp/nbf checks. Add tests covering the default-claims path, the
empty-array case, and tokens that legitimately omit exp.
When a request carried an invalid API key and key-auth fell back to the
configured anonymous consumer, the credential was forwarded upstream even
with hide_credentials enabled: find_consumer() only strips credentials on
the successful-auth path.

Strip the credential before falling back to the anonymous consumer when
hide_credentials is true. A request may carry the key in both the header
and the query string, so clean up both. Add tests covering the header,
query, and both-present cases on anonymous fallback.
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. bug Something isn't working labels Jun 3, 2026
The anonymous-fallback tests scanned the error log (no_error_log:
invalid-key) and used a non-existent --- response_args section. nginx
always logs the original client request line (".../echo?auth=invalid-key"),
so no_error_log matched that logged request line rather than a forwarded
credential, failing in CI even though the credential was correctly
stripped. response_args is not a Test::Nginx section, so the query-strip
path was effectively unasserted.

Add a print_request_received upstream that echoes the received request
URI and headers, and assert the proxied request carries the anonymous
marker but not the credential. This checks what actually reaches the
upstream and fails closed if stripping regresses.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants