From ec380945496324c00e7caaf77dd6ec220b12f1fc Mon Sep 17 00:00:00 2001 From: Vacant Date: Mon, 12 Feb 2024 11:31:17 +0800 Subject: [PATCH] fix: jwe-decrypt secret length restriction (#10928) --- apisix/plugins/jwe-decrypt.lua | 21 ++++++- docs/en/latest/plugins/jwe-decrypt.md | 6 ++ docs/zh/latest/plugins/jwe-decrypt.md | 6 ++ t/plugin/jwe-decrypt.t | 88 +++++++++++++++++++-------- 4 files changed, 95 insertions(+), 26 deletions(-) diff --git a/apisix/plugins/jwe-decrypt.lua b/apisix/plugins/jwe-decrypt.lua index f163f4034ee1..0e4447e02432 100644 --- a/apisix/plugins/jwe-decrypt.lua +++ b/apisix/plugins/jwe-decrypt.lua @@ -47,7 +47,7 @@ local consumer_schema = { type = "object", properties = { key = { type = "string" }, - secret = { type = "string", minLength = 32 }, + secret = { type = "string" }, is_base64_encoded = { type = "boolean" }, }, required = { "key", "secret" }, @@ -66,7 +66,24 @@ local _M = { function _M.check_schema(conf, schema_type) if schema_type == core.schema.TYPE_CONSUMER then - return core.schema.check(consumer_schema, conf) + local ok, err = core.schema.check(consumer_schema, conf) + if not ok then + return false, err + end + + -- restrict the length of secret, we use A256GCM for encryption, + -- so the length should be 32 chars only + if conf.is_base64_encoded then + if #base64.decode_base64url(conf.secret) ~= 32 then + return false, "the secret length after base64 decode should be 32 chars" + end + else + if #conf.secret ~= 32 then + return false, "the secret length should be 32 chars" + end + end + + return true end return core.schema.check(schema, conf) end diff --git a/docs/en/latest/plugins/jwe-decrypt.md b/docs/en/latest/plugins/jwe-decrypt.md index 9969094af0a0..6da75d2bb83e 100644 --- a/docs/en/latest/plugins/jwe-decrypt.md +++ b/docs/en/latest/plugins/jwe-decrypt.md @@ -44,6 +44,12 @@ For Consumer: | secret | string | True | | | The decryption key. Must be 32 characters. The key could be saved in a secret manager using the [Secret](../terminology/secret.md) resource. | | is_base64_encoded | boolean | False | false | | Set to true if the secret is base64 encoded. | +:::note + +After enabling `is_base64_encoded`, your `secret` length may exceed 32 chars. You only need to make sure that the length after decoding is still 32 chars. + +::: + For Route: | Name | Type | Required | Default | Description | diff --git a/docs/zh/latest/plugins/jwe-decrypt.md b/docs/zh/latest/plugins/jwe-decrypt.md index ce5c98fd6a8c..cf7363e27adb 100644 --- a/docs/zh/latest/plugins/jwe-decrypt.md +++ b/docs/zh/latest/plugins/jwe-decrypt.md @@ -44,6 +44,12 @@ Consumer 配置: | secret | string | True | | | 解密密钥,必须为 32 位。秘钥可以使用 [Secret](../terminology/secret.md) 资源保存在密钥管理服务中 | | is_base64_encoded | boolean | False | false | | 如果密钥是 Base64 编码,则需要配置为 `true` | +:::note + +注意,在启用 `is_base64_encoded` 后,你的 `secret` 长度可能会超过 32 位,你只需要保证在 Decode 后的长度仍然是 32 位即可。 + +::: + Route 配置: | 名称 | 类型 | 必选项 | 默认值 | 描述 | diff --git a/t/plugin/jwe-decrypt.t b/t/plugin/jwe-decrypt.t index 599fed296359..e7fcf7756f50 100644 --- a/t/plugin/jwe-decrypt.t +++ b/t/plugin/jwe-decrypt.t @@ -54,7 +54,7 @@ qr/{"key":"123","secret":"[a-zA-Z0-9+\\\/]+={0,2}"}/ -=== TEST 2: wrong type of string +=== TEST 2: wrong type of key --- config location /t { content_by_lua_block { @@ -74,13 +74,13 @@ done -=== TEST 3: wrong type of string +=== TEST 3: wrong type of secret --- config location /t { content_by_lua_block { local core = require("apisix.core") local plugin = require("apisix.plugins.jwe-decrypt") - local ok, err = plugin.check_schema({key = "123", secret = "123456"}, core.schema.TYPE_CONSUMER) + local ok, err = plugin.check_schema({key = "123", secret = 12345678901234567890123456789012}, core.schema.TYPE_CONSUMER) if not ok then ngx.say(err) end @@ -89,12 +89,52 @@ done } } --- response_body -property "secret" validation failed: string too short, expected at least 32, got 6 +property "secret" validation failed: wrong type: expected string, got number done -=== TEST 4: add consumer with username and plugins +=== TEST 4: secret length too long +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local plugin = require("apisix.plugins.jwe-decrypt") + local ok, err = plugin.check_schema({key = "123", secret = "123456789012345678901234567890123"}, core.schema.TYPE_CONSUMER) + if not ok then + ngx.say(err) + end + + ngx.say("done") + } + } +--- response_body +the secret length should be 32 chars +done + + + +=== TEST 5: secret length too long(base64 encode) +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local plugin = require("apisix.plugins.jwe-decrypt") + local ok, err = plugin.check_schema({key = "123", secret = "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXphYmNkZWZn", is_base64_encoded = true}, core.schema.TYPE_CONSUMER) + if not ok then + ngx.say(err) + end + + ngx.say("done") + } + } +--- response_body +the secret length after base64 decode should be 32 chars +done + + + +=== TEST 6: add consumer with username and plugins --- config location /t { content_by_lua_block { @@ -123,7 +163,7 @@ passed -=== TEST 5: enable jwe-decrypt plugin using admin api +=== TEST 7: enable jwe-decrypt plugin using admin api --- config location /t { content_by_lua_block { @@ -158,7 +198,7 @@ passed -=== TEST 6: create public API route (jwe-decrypt sign) +=== TEST 8: create public API route (jwe-decrypt sign) --- config location /t { content_by_lua_block { @@ -184,7 +224,7 @@ passed -=== TEST 7: sign / verify in argument +=== TEST 9: sign / verify in argument --- config location /t { content_by_lua_block { @@ -214,14 +254,14 @@ hello world -=== TEST 8: test for unsupported method +=== TEST 10: test for unsupported method --- request PATCH /apisix/plugin/jwe/encrypt?key=user-key --- error_code: 404 -=== TEST 9: verify, missing token +=== TEST 11: verify, missing token --- request GET /hello --- error_code: 403 @@ -230,7 +270,7 @@ GET /hello -=== TEST 10: verify: invalid JWE token +=== TEST 12: verify: invalid JWE token --- request GET /hello --- more_headers @@ -241,7 +281,7 @@ Authorization: invalid-eyJhbGciOiJkaXIiLCJraWQiOiJ1c2VyLWtleSIsImVuYyI6IkEyNTZHQ -=== TEST 11: verify (in header) +=== TEST 13: verify (in header) --- request GET /hello --- more_headers @@ -251,7 +291,7 @@ hello world -=== TEST 12: verify (in header without Bearer) +=== TEST 14: verify (in header without Bearer) --- request GET /hello --- more_headers @@ -261,7 +301,7 @@ hello world -=== TEST 13: verify (header with bearer) +=== TEST 15: verify (header with bearer) --- request GET /hello --- more_headers @@ -271,7 +311,7 @@ hello world -=== TEST 14: verify (invalid bearer token) +=== TEST 16: verify (invalid bearer token) --- request GET /hello --- more_headers @@ -282,7 +322,7 @@ Authorization: bearer invalid-eyJhbGciOiJkaXIiLCJraWQiOiJ1c2VyLWtleSIsImVuYyI6Ik -=== TEST 15: delete a exist consumer +=== TEST 17: delete a exist consumer --- config location /t { content_by_lua_block { @@ -332,7 +372,7 @@ code: true body: passed -=== TEST 16: add consumer with username and plugins with base64 secret +=== TEST 18: add consumer with username and plugins with base64 secret --- config location /t { content_by_lua_block { @@ -362,7 +402,7 @@ passed -=== TEST 17: enable jwt decrypt plugin with base64 secret +=== TEST 19: enable jwt decrypt plugin with base64 secret --- config location /t { content_by_lua_block { @@ -396,7 +436,7 @@ passed -=== TEST 18: create public API route (jwe-decrypt sign) +=== TEST 20: create public API route (jwe-decrypt sign) --- config location /t { content_by_lua_block { @@ -422,7 +462,7 @@ passed -=== TEST 19: sign / verify in argument +=== TEST 21: sign / verify in argument --- config location /t { content_by_lua_block { @@ -454,7 +494,7 @@ hello world -=== TEST 20: verify (in header) +=== TEST 22: verify (in header) --- request GET /hello --- more_headers @@ -464,7 +504,7 @@ hello world -=== TEST 21: verify (in header without Bearer) +=== TEST 23: verify (in header without Bearer) --- request GET /hello --- more_headers @@ -474,7 +514,7 @@ hello world -=== TEST 22: enable jwt decrypt plugin with test upstream route +=== TEST 24: enable jwt decrypt plugin with test upstream route --- config location /t { content_by_lua_block { @@ -508,7 +548,7 @@ passed -=== TEST 23: verify in upstream header +=== TEST 25: verify in upstream header --- request GET /headers --- more_headers