]> git.kaiwu.me - haproxy.git/commitdiff
MEDIUM: jwt: Reject "none" alg in jwt_verify by default
authorRemi Tricot-Le Breton <rlebreton@haproxy.com>
Tue, 8 Sep 2026 13:06:24 +0000 (15:06 +0200)
committerWilliam Lallemand <wlallemand@haproxy.com>
Fri, 11 Sep 2026 12:44:39 +0000 (14:44 +0200)
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.

doc/configuration.txt
reg-tests/jwt/jws_verify.vtc
src/jwt.c

index b1a7d1f1c36d69792e18ede45ebbfed80e51c142..d276ab238db5cf499de7c7235212a64d8a495a55 100644 (file)
@@ -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 <dir>
   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 <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(<alg>,<key>)
    | 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.
+
   - <key> can be either a string or a variable name (See also "set-var") that
     holds a secret or a public key path.
 
index c67996c9ff1b46b4ea53f3b69d86b8237d25739f..724c60c893cbbab35f798522b6ae41d0f93c10d2 100644 (file)
@@ -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
index d10ab1257feccf4966e4b729308ef125543a5d7b..68cfc6967135ed9639d99c6b0474f3789a91b4cf 100644 (file)
--- 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 */