Skip to content

Commit e4de423

Browse files
fix(jwt-auth): enforce algorithm match before signature verification (#13182)
1 parent 656a8dd commit e4de423

File tree

3 files changed

+75
-4
lines changed

3 files changed

+75
-4
lines changed

apisix/plugins/jwt-auth.lua

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,18 @@ local function find_consumer(conf, ctx)
326326
return nil, nil, "failed to verify jwt"
327327
end
328328

329+
-- Enforce that the JWT header's "alg" matches the consumer's configured algorithm
330+
local expected_alg = consumer.auth_conf.algorithm or "HS256"
331+
local token_alg = jwt.header and jwt.header.alg
332+
if token_alg ~= expected_alg then
333+
local err = "failed to verify jwt: algorithm mismatch, expected " .. expected_alg
334+
if auth_utils.is_running_under_multi_auth(ctx) then
335+
return nil, nil, err
336+
end
337+
core.log.warn(err)
338+
return nil, nil, "failed to verify jwt"
339+
end
340+
329341
-- Now verify the JWT signature
330342
if not jwt:verify_signature(auth_secret) then
331343
local err = "failed to verify jwt: signature mismatch: " .. jwt.signature

t/plugin/jwt-auth-more-algo.t

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,15 +154,15 @@ JWT token invalid: invalid header: invalid-eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
154154
155155
156156
157-
=== TEST 6: verify token with algorithm HS256
157+
=== TEST 6: verify token with algorithm HS384
158158
--- request
159159
GET /hello
160160
--- more_headers
161-
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0.fNtFJnNmJgzbiYmGB0Yjvm-l6A6M4jRV1l4mnVFSYjs
161+
Authorization: eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTg3OTMxODU0MX0.yM8L6f3cGZxKqflMJlf0DqfQVpHUxupqgM3JWETyRQ8Iag1HiRrHILEA-A2rhmEq
162162
--- response_body
163163
hello world
164164
--- error_log
165-
parsed jwt alg: HS256
165+
parsed jwt alg: HS384
166166
167167
168168
@@ -329,7 +329,7 @@ passed
329329
--- request
330330
GET /hello
331331
--- more_headers
332-
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTU2Mzg3MDUwMX0.pPNVvh-TQsdDzorRwa-uuiLYiEBODscp9wv0cwD6c68
332+
Authorization: eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJrZXkiOiJ1c2VyLWtleSIsImV4cCI6MTU2Mzg3MDUwMX0.SJNbFYR1qlIOD7D8aItkB9hYxdhc0d_JGaLgVjOCDOAHd8CSJuHp_R6YQniRDq8S
333333
--- response_body
334334
hello world
335335

t/plugin/jwt-auth.t

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,3 +1242,62 @@ hello world
12421242
}
12431243
--- response_body
12441244
passed
1245+
1246+
1247+
1248+
=== TEST 52: HS256 token against RS256 consumer
1249+
--- config
1250+
location /t {
1251+
content_by_lua_block {
1252+
local t = require("lib.test_admin").test
1253+
-- Set up an RS256 consumer
1254+
local code, body = t('/apisix/admin/consumers',
1255+
ngx.HTTP_PUT,
1256+
[[{
1257+
"username": "kerouac",
1258+
"plugins": {
1259+
"jwt-auth": {
1260+
"key": "user-key-rs256",
1261+
"algorithm": "RS256",
1262+
"public_key": "-----BEGIN PUBLIC KEY-----\nMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKebDxlvQMGyEesAL1r1nIJBkSdqu3Hr\n7noq/0ukiZqVQLSJPMOv0oxQSutvvK3hoibwGakDOza+xRITB7cs2cECAwEAAQ==\n-----END PUBLIC KEY-----"
1263+
}
1264+
}
1265+
}]]
1266+
)
1267+
assert(code < 300, body)
1268+
1269+
-- Set up a route with jwt-auth
1270+
code, body = t('/apisix/admin/routes/1',
1271+
ngx.HTTP_PUT,
1272+
[[{
1273+
"plugins": {
1274+
"jwt-auth": {}
1275+
},
1276+
"upstream": {
1277+
"nodes": {
1278+
"127.0.0.1:1980": 1
1279+
},
1280+
"type": "roundrobin"
1281+
},
1282+
"uri": "/hello"
1283+
}]]
1284+
)
1285+
assert(code < 300, body)
1286+
1287+
-- This JWT has alg=HS256 in the header but targets the RS256 consumer
1288+
-- (key claim = "user-key-rs256"). It is HMAC-signed with the consumer's
1289+
-- public key, which would pass verification without the algorithm check.
1290+
local forged_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
1291+
.. ".eyJrZXkiOiJ1c2VyLWtleS1yczI1NiIsIm5iZiI6MTcyNzI3NDk4M30"
1292+
.. ".6H3x-DNrthHMtsQ72qZNrddTdDjEZuJQX6MNiEBFTOs"
1293+
1294+
local code, body = t('/hello?jwt=' .. forged_token, ngx.HTTP_GET)
1295+
ngx.status = code
1296+
ngx.print(body)
1297+
}
1298+
}
1299+
--- error_code: 401
1300+
--- response_body
1301+
{"message":"failed to verify jwt"}
1302+
--- error_log
1303+
failed to verify jwt: algorithm mismatch, expected RS256

0 commit comments

Comments
 (0)