Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions apisix/plugins/jwt-auth/parser.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ local ipairs = ipairs
local type = type
local error = error
local pcall = pcall
local tostring = tostring

local default_claims = {
"nbf",
Expand Down Expand Up @@ -223,8 +224,28 @@ end


function _M.verify_signature(self, key)
return alg_verify[self.header.alg](self.raw_header .. "." ..
self.raw_payload, base64_decode(self.signature), key)
local verifier = alg_verify[self.header.alg]
if not verifier then
return false, "unsupported algorithm: " .. tostring(self.header.alg)
end

local signature = base64_decode(self.signature)
if not signature then
return false, "failed to decode signature"
end

-- the per-algorithm verifiers assert on signature length and key validity,
-- so guard with pcall to turn a malformed token into a clean rejection
-- instead of letting the error propagate as a 500 response
local ok, verified, verify_err = pcall(verifier,
self.raw_header .. "." .. self.raw_payload, signature, key)
if not ok then
-- verifier raised: `verified` holds the caught error message
return false, verified
end

-- preserve the verifier's own (verified, err) return contract
return verified, verify_err
end


Expand Down
62 changes: 62 additions & 0 deletions t/plugin/jwt-auth.t
Original file line number Diff line number Diff line change
Expand Up @@ -1428,3 +1428,65 @@ GET /hello?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4
{"message":"failed to verify jwt"}
--- error_log
failed to verify jwt: 'exp' claim expired at Tue, 23 Jul 2019 08:28:21 GMT



=== TEST 59: malformed signatures must be rejected with 401, not crash with 500
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
-- ES256 consumer: the per-algorithm verifier asserts the signature
-- length and decodes it from base64url, so a malformed signature
-- used to raise a Lua error and surface as a 500 instead of a 401.
local code, body = t('/apisix/admin/consumers',
ngx.HTTP_PUT,
[[{
"username": "kerouac",
"plugins": {
"jwt-auth": {
"key": "user-key-es256",
"algorithm": "ES256",
"public_key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEVs/o5+uQbTjL3chynL4wXgUg2R9\nq9UU8I5mEovUf86QZ7kOBIjJwqnzD1omageEHWwHdBO6B+dFabmdT9POxg==\n-----END PUBLIC KEY-----"
}
}
}]]
)
assert(code < 300, body)

code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"plugins": {
"jwt-auth": {}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/hello"
}]]
)
assert(code < 300, body)

-- valid ES256 header + payload (key claim = user-key-es256), with two
-- malformed signatures: "YWJj" decodes to 3 bytes (not the required
-- 64), and "@@@@" is not valid base64url so decoding returns nil
local header_payload = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9"
.. ".eyJrZXkiOiJ1c2VyLWtleS1lczI1NiIsIm5iZiI6MTcyNzI3NDk4M30"
for _, sig in ipairs({"YWJj", "@@@@"}) do
local rc, rb = t('/hello?jwt=' .. header_payload .. "." .. sig,
ngx.HTTP_GET)
assert(rc == 401, "signature '" .. sig .. "' expected 401 but got "
.. tostring(rc))
assert(string.find(rb, "failed to verify jwt", 1, true), rb)
end
ngx.say("passed")
}
}
--- response_body
passed
--- no_error_log
[error]
Loading