Skip to content

fix(jwe-decrypt): reject malformed tokens instead of returning 500 - #13844

Open
AlinsRan wants to merge 1 commit into
apache:masterfrom
AlinsRan:fix/jwe-decrypt-malformed-token
Open

fix(jwe-decrypt): reject malformed tokens instead of returning 500#13844
AlinsRan wants to merge 1 commit into
apache:masterfrom
AlinsRan:fix/jwe-decrypt-malformed-token

Conversation

@AlinsRan

Copy link
Copy Markdown
Contributor

Description

A crafted JWE token makes jwe-decrypt throw a Lua error, so the request ends with a 500 instead of the 400 the plugin already returns for invalid tokens:

  • the header segment is decoded with core.json.decode() and only checked for truthiness. A header of null decodes to the truthy cjson.null userdata and a JSON scalar decodes to a number, so jwe_obj.header_obj.kid throws:
    attempt to index field 'header_obj' (a userdata value)
    
  • an iv, ciphertext or tag that is not valid base64url decodes to nil. aes:new() then returns nil, "iv is needed", and calling decrypt() on it throws:
    attempt to index local 'aes_default' (a nil value)
    

Both are reachable with a single request against any route using the plugin, e.g.

Authorization: Bearer bnVsbA..MTIzNDU2Nzg5MDEy._0DrWD0.vl-ydutnNuMpkYskwNqu-Q
Authorization: Bearer eyJhbGciOiJkaXIiLCJraWQiOiJ1c2VyLWtleSIsImVuYyI6IkEyNTZHQ00ifQ..!!!.!!!.!!!

The consumer secret has the same shape of problem. check_schema() skips the length check when data encryption is enabled — which is the default — so a secret that is not valid base64url is accepted, and get_secret() returns nil at request time, again ending in a 500. When the check does run (data_encryption.enable_encrypt_fields: false), #base64.decode_base64url(conf.secret) throws on such a secret instead of reporting a schema error.

This PR validates the decoded header, the token segments and the secret, and checks the aes:new() return value, so all of these paths end in the 400 the plugin already returns.

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

Tests: 5 new blocks in t/plugin/jwe-decrypt.t cover the null header, a scalar header, invalid base64url token segments, a consumer secret that cannot be decoded at request time, and the schema error for a non-base64url secret. All five fail on master and pass with this change. No documentation change is needed: the plugin already documents 400 for an invalid token, and this only stops the malformed cases from escaping as 500.

@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. bug Something isn't working labels Aug 19, 2026
A crafted JWE token makes the plugin throw a Lua error, so the request
fails with 500 instead of the 400 the plugin already returns for invalid
tokens:

- a header segment that decodes to JSON `null` yields the truthy
  `cjson.null` userdata, and a scalar yields a number, so reading `kid`
  from it throws
- an iv, ciphertext or tag that is not valid base64url decodes to nil,
  so `aes:new()` returns nil and calling `decrypt()` on it throws

The consumer secret has the same problem: when data encryption is
enabled the schema check cannot validate it, so a secret that is not
valid base64url is accepted and `get_secret()` returns nil at request
time. And when the check does run, `#base64.decode_base64url(secret)`
throws on such a secret rather than reporting a schema error.

Validate the decoded header, the token segments and the secret, and
check the `aes:new()` return value, so all of these end up in the
existing 400 response.
@AlinsRan
AlinsRan force-pushed the fix/jwe-decrypt-malformed-token branch from 4b721d3 to af4180a Compare August 19, 2026 06:19
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.

1 participant