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

@membphis membphis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

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