diff --git a/apisix/plugins/jwe-decrypt.lua b/apisix/plugins/jwe-decrypt.lua index 1ecef6795380..4321e73b3a5f 100644 --- a/apisix/plugins/jwe-decrypt.lua +++ b/apisix/plugins/jwe-decrypt.lua @@ -142,8 +142,8 @@ local function jwe_decrypt_with_obj(o, consumer) {iv = dec(o.iv)} ) - local decrypted = aes_default:decrypt(dec(o.ciphertext), dec(o.tag)) - return decrypted + local decrypted, err = aes_default:decrypt(dec(o.ciphertext), dec(o.tag)) + return decrypted, err end @@ -214,7 +214,8 @@ function _M.rewrite(conf, ctx) end local plaintext, err = jwe_decrypt_with_obj(jwe_obj, consumer) - if err ~= nil then + if not plaintext then + core.log.info("failed to decrypt JWE token: ", err) return 400, { message = "failed to decrypt JWE token" } end core.request.set_header(ctx, conf.forward_header, plaintext) diff --git a/t/plugin/jwe-decrypt.t b/t/plugin/jwe-decrypt.t index b606f6dd5234..eaf1b5b5226d 100644 --- a/t/plugin/jwe-decrypt.t +++ b/t/plugin/jwe-decrypt.t @@ -322,7 +322,7 @@ GET /hello --- request GET /hello --- more_headers -Authorization: invalid-eyJhbGciOiJkaXIiLCJraWQiOiJ1c2VyLWtleSIsImVuYyI6IkEyNTZHQ00ifQ..MTIzNDU2Nzg5MDEy.6JeRgm02rpOJdg.4nkSYJgwMKYgTeacatgmRw +Authorization: invalid-eyJraWQiOiJ1c2VyLWtleSIsImFsZyI6ImRpciIsImVuYyI6IkEyNTZHQ00ifQ..MTIzNDU2Nzg5MDEy.6JeRgm0.rNt131nG5wMvUD1KXbwLGA --- error_code: 400 --- response_body {"message":"JWE token invalid"} @@ -333,7 +333,7 @@ Authorization: invalid-eyJhbGciOiJkaXIiLCJraWQiOiJ1c2VyLWtleSIsImVuYyI6IkEyNTZHQ --- request GET /hello --- more_headers -Authorization: Bearer eyJhbGciOiJkaXIiLCJraWQiOiJ1c2VyLWtleSIsImVuYyI6IkEyNTZHQ00ifQ..MTIzNDU2Nzg5MDEy.6JeRgm02rpOJdg.4nkSYJgwMKYgTeacatgmRw +Authorization: Bearer eyJraWQiOiJ1c2VyLWtleSIsImFsZyI6ImRpciIsImVuYyI6IkEyNTZHQ00ifQ..MTIzNDU2Nzg5MDEy.6JeRgm0.rNt131nG5wMvUD1KXbwLGA --- response_body hello world @@ -343,7 +343,7 @@ hello world --- request GET /hello --- more_headers -Authorization: eyJhbGciOiJkaXIiLCJraWQiOiJ1c2VyLWtleSIsImVuYyI6IkEyNTZHQ00ifQ..MTIzNDU2Nzg5MDEy.6JeRgm02rpOJdg.4nkSYJgwMKYgTeacatgmRw +Authorization: eyJraWQiOiJ1c2VyLWtleSIsImFsZyI6ImRpciIsImVuYyI6IkEyNTZHQ00ifQ..MTIzNDU2Nzg5MDEy.6JeRgm0.rNt131nG5wMvUD1KXbwLGA --- response_body hello world @@ -353,7 +353,7 @@ hello world --- request GET /hello --- more_headers -Authorization: bearer eyJhbGciOiJkaXIiLCJraWQiOiJ1c2VyLWtleSIsImVuYyI6IkEyNTZHQ00ifQ..MTIzNDU2Nzg5MDEy.6JeRgm02rpOJdg.4nkSYJgwMKYgTeacatgmRw +Authorization: bearer eyJraWQiOiJ1c2VyLWtleSIsImFsZyI6ImRpciIsImVuYyI6IkEyNTZHQ00ifQ..MTIzNDU2Nzg5MDEy.6JeRgm0.rNt131nG5wMvUD1KXbwLGA --- response_body hello world @@ -363,7 +363,7 @@ hello world --- request GET /hello --- more_headers -Authorization: bearer invalid-eyJhbGciOiJkaXIiLCJraWQiOiJ1c2VyLWtleSIsImVuYyI6IkEyNTZHQ00ifQ..MTIzNDU2Nzg5MDEy.6JeRgm02rpOJdg.4nkSYJgwMKYgTeacatgmRw +Authorization: bearer invalid-eyJraWQiOiJ1c2VyLWtleSIsImFsZyI6ImRpciIsImVuYyI6IkEyNTZHQ00ifQ..MTIzNDU2Nzg5MDEy.6JeRgm0.rNt131nG5wMvUD1KXbwLGA --- error_code: 400 --- response_body {"message":"JWE token invalid"} @@ -619,3 +619,83 @@ Authorization: eyJhbGciOiJkaXIiLCJraWQiOiJ1c2VyLWtleSIsImVuYyI6IkEyNTZHQ00ifQ..M .*"Authorization": "hello".* --- no_error_log fo4XKdZ1xSrIZyms4q2BwPrW5lMpls9qqy5tiAk2esc= + + + +=== TEST 27: setup route protected by jwe-decrypt +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code = t('/apisix/admin/consumers', + ngx.HTTP_PUT, + [[{ + "username": "jwe_fail_user", + "plugins": { + "jwe-decrypt": { + "key": "jwe-fail-key", + "secret": "12345678901234567890123456789012" + } + } + }]] + ) + if code >= 300 then + ngx.status = code + ngx.say("failed to add consumer") + return + end + + code = t('/apisix/admin/routes/10', + ngx.HTTP_PUT, + [[{ + "plugins": { + "jwe-decrypt": { + "header": "Authorization", + "forward_header": "Authorization" + }, + "proxy-rewrite": { + "uri": "/hello" + } + }, + "upstream": { + "nodes": { "127.0.0.1:1980": 1 }, + "type": "roundrobin" + }, + "uri": "/jwe-decrypt-fail" + }]] + ) + if code >= 300 then + ngx.status = code + end + ngx.say("done") + } + } +--- response_body +done + + + +=== TEST 28: well-formed token whose ciphertext does not decrypt is rejected +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local core = require("apisix.core") + local enc = require("ngx.base64").encode_base64url + + -- a structurally valid JWE whose ciphertext and tag were not + -- produced with the consumer secret, so AES-256-GCM verification + -- fails when the plugin tries to decrypt it + local header = enc(core.json.encode({ + alg = "dir", enc = "A256GCM", kid = "jwe-fail-key", + })) + local token = header .. ".." .. enc("123456789012") .. "." + .. enc("undecryptable") .. "." .. enc("0123456789abcdef") + + local code = t('/jwe-decrypt-fail', ngx.HTTP_GET, nil, nil, + { Authorization = "Bearer " .. token }) + ngx.say("status: ", code) + } + } +--- response_body +status: 400