From: Remi Tricot-Le Breton Date: Tue, 8 Sep 2026 13:06:24 +0000 (+0200) Subject: MEDIUM: jwt: Reject "none" alg in jwt_verify by default X-Git-Tag: v3.5-dev7~22 X-Git-Url: https://git.kaiwu.me/$%7BISSUES%7D/static/gitweb.js?a=commitdiff_plain;h=3a62334bb5b07c0dff23fe83898b66507b65ceb6;p=haproxy.git MEDIUM: jwt: Reject "none" alg in jwt_verify by default In RFC8725 section 3.2, they specify that "JWT libraries SHOULD NOT consume JWTs using "none" unless explicitly requested by the caller". This patch disables the support for the "none" algorithm by default in jwt_verify converter and adds a global option that explicitely allows it. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index b1a7d1f1c..d276ab238 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -1790,6 +1790,7 @@ The following keywords are supported in the "global" section : - insecure-fork-wanted - insecure-setuid-wanted - issuers-chain-path + - jwt.allow_none_alg - jwt.decrypt_alg_list - jwt.decrypt_enc_list - key-base @@ -2896,6 +2897,12 @@ issuers-chain-path The OCSP features are able to use the completed chain when no .issuer was used, or no chain was provided in the PEM. +jwt.allow_none_alg + Allow JWT signed token with the "none" algorithm in the 'jwt_verify' + converter. They will always be considered valid when this option is set. By + default unsigned tokens are rejected since they are considered unsafe (see + RFC8725 section 3.1). + jwt.decrypt_alg_list Set the list of algorithms allowed in the jwt_decrypt_XXX converters. JWT tokens using an unsupported or disabled algorithms will never be decrypted. @@ -22375,9 +22382,13 @@ jwt_verify(,) | PS256 | RSASSA-PSS using SHA-256 and MGF1 with SHA-256 | | PS384 | RSASSA-PSS using SHA-384 and MGF1 with SHA-384 | | PS512 | RSASSA-PSS using SHA-512 and MGF1 with SHA-512 | - | none | No digital signature or MAC performed | + | none | No digital signature or MAC performed (see note below) | +--------------+---------------------------------------------------------+ + Please note that the "none" algorithm is not allowed by default to follow + the recommendations in RFC8725 section 3.2. Set global option + 'jwt.allow_none_alg' to explicitely allow it. + - can be either a string or a variable name (See also "set-var") that holds a secret or a public key path. diff --git a/reg-tests/jwt/jws_verify.vtc b/reg-tests/jwt/jws_verify.vtc index c67996c9f..724c60c89 100644 --- a/reg-tests/jwt/jws_verify.vtc +++ b/reg-tests/jwt/jws_verify.vtc @@ -32,6 +32,7 @@ haproxy h1 -conf { .endif tune.ssl.capture-buffer-size 1 stats socket "${tmpdir}/h1/stats" level admin + jwt.allow_none_alg defaults mode http diff --git a/src/jwt.c b/src/jwt.c index d10ab1257..68cfc6967 100644 --- a/src/jwt.c +++ b/src/jwt.c @@ -30,6 +30,8 @@ /* Tree into which the public certificates used to validate JWTs will be stored. */ static struct eb_root jwt_cert_tree = EB_ROOT_UNIQUE; +/* Set whether "none" algorithm should be allowed or not */ +static int jwt_verify_allow_none_alg = 0; /* * The possible algorithm strings that can be found in a JWS's JOSE header are @@ -508,6 +510,9 @@ enum jwt_vrfy_status jwt_verify(const struct buffer *token, const struct buffer if (ctx.alg == JWT_ALG_DEFAULT) return JWT_VRFY_UNKNOWN_ALG; + if (ctx.alg == JWS_ALG_NONE && !jwt_verify_allow_none_alg) + return JWT_VRFY_UNMANAGED_ALG; + if (jwt_tokenize(token, items, item_num)) return JWT_VRFY_INVALID_TOKEN; @@ -575,6 +580,23 @@ end: return retval; } +/* Parse 'jwt.allow_none_arg' option */ +static int cfg_parse_global_jwt_none_alg(char **args, int section_type, struct proxy *curpx, + const struct proxy *defpx, const char *file, int linenum, char **err) +{ + if (*args[1]) { + memprintf(err, "parsing [%s:%d]: keyword '%s' in '%s' section does not take any arguments.\n", file, linenum, args[0], cursection); + goto error; + } + + jwt_verify_allow_none_alg = 1; + + return 0; + +error: + return -1; +} + static void jwt_deinit(void) { @@ -592,5 +614,12 @@ static void jwt_deinit(void) } REGISTER_POST_DEINIT(jwt_deinit); +static struct cfg_kw_list cfg_kws = {ILH, { + { CFG_GLOBAL, "jwt.allow_none_alg", cfg_parse_global_jwt_none_alg }, + { 0, NULL, NULL }, +}}; + +INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws); + #endif /* USE_OPENSSL */