{"record":{"id":"8dda0803fd1d29c4","repo":"jwtk/jjwt","slug":"protected-header-crit-set-references-header-name","errorCode":null,"errorMessage":"Protected Header crit set references header name '%s', but the header does not contain an associated '%s' header parameter as required by https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11. Header: %s","messagePattern":"Protected Header crit set references header name '(.+?)', but the header does not contain an associated '(.+?)' header parameter as required by https://www\\.rfc-editor\\.org/rfc/rfc7515\\.html#section-4\\.1\\.11\\. Header: (.+?)","errorType":"exception","errorClass":"MalformedJwtException","httpStatus":null,"severity":"error","filePath":"impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java","lineNumber":445,"sourceCode":"        }\n        // ----- crit assertions -----\n        if (header instanceof ProtectedHeader) {\n            Set<String> crit = Collections.nullSafe(((ProtectedHeader) header).getCritical());\n            Set<String> supportedCrit = this.critical;\n            String b64Id = DefaultJwsHeader.B64.getId();\n            if (!unencodedPayload.isEmpty() && !this.critical.contains(b64Id)) {\n                // The application developer explicitly indicates they're using a B64 payload, so\n                // ensure that the B64 crit header is supported, even if they forgot to configure it on the\n                // parser builder:\n                supportedCrit = new LinkedHashSet<>(Collections.size(this.critical) + 1);\n                supportedCrit.add(DefaultJwsHeader.B64.getId());\n                supportedCrit.addAll(this.critical);\n            }\n            // assert any values per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11:\n            for (String name : crit) {\n                if (!header.containsKey(name)) {\n                    String msg = String.format(CRIT_MISSING_MSG, name, name, header);\n                    throw new MalformedJwtException(msg);\n                }\n                if (!supportedCrit.contains(name)) {\n                    String msg = String.format(CRIT_UNSUPPORTED_MSG, name, name, header);\n                    throw new UnsupportedJwtException(msg);\n                }\n            }\n        }\n\n        // =============== Payload =================\n        final CharSequence payloadToken = tokenized.getPayload();\n        Payload payload;\n        boolean integrityVerified = false; // only true after successful signature verification or AEAD decryption\n\n        // check if b64 extension enabled:\n        final boolean payloadBase64UrlEncoded = !(header instanceof JwsHeader) || ((JwsHeader) header).isPayloadEncoded();\n        if (payloadBase64UrlEncoded) {\n            // standard encoding, so decode it:\n            byte[] data = decode(payloadToken, \"payload\");","sourceCodeStart":427,"sourceCodeEnd":463,"githubUrl":"https://github.com/jwtk/jjwt/blob/fb71496164c71442d08adec4571d9616ed5e1b8d/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java#L427-L463","documentation":"RFC 7515 Section 4.1.11 requires that every header name listed in the 'crit' set also be present as an actual header parameter in the protected header, so its value is integrity protected. jjwt iterates the crit set during parse and throws MalformedJwtException if any referenced name is absent from the header.","triggerScenarios":"Parsing a JWS/JWE whose header declares e.g. \"crit\":[\"b64\"] (or any custom name) without a corresponding \"b64\":... entry in the same header.","commonSituations":"Hand-built headers where crit was added but the referenced parameter forgotten; generic extension frameworks adding crit entries unconditionally; token transformation/forwarding code that strips headers but keeps crit.","solutions":["Add the referenced header parameter to the protected header (e.g. include \"b64\":false when crit lists \"b64\").","Remove the name from the crit set if the extension is not actually used.","Fix the token producer so crit always mirrors the actual header contents.","At ingestion, validate crit vs header keys before parsing to give callers clearer feedback."],"exampleFix":"// before\n{\"alg\":\"HS256\",\"crit\":[\"b64\"]}\n\n// after\n{\"alg\":\"HS256\",\"b64\":false,\"crit\":[\"b64\"]}","handlingStrategy":"validation","validationCode":"java.util.Map<String,Object> h = parseHeaderJson(token);\n@SuppressWarnings(\"unchecked\") java.util.List<String> crit = (java.util.List<String>) h.get(\"crit\");\nif (crit != null) for (String n : crit) if (!h.containsKey(n)) throw new IllegalArgumentException(\"crit references missing header: \" + n);","typeGuard":null,"tryCatchPattern":"try { return parser.parse(token); }\ncatch (io.jsonwebtoken.MalformedJwtException e) { throw new InvalidTokenException(\"crit references absent header\", e); }","preventionTips":["Keep crit and the actual header keys in sync at the producer","Don't strip headers from forwarded tokens without updating crit","Add producer-side unit tests asserting crit ⊆ header keys","Decode headers in CI when changing extension code"],"tags":["jwt","jws","crit-header","missing-header","malformed-token"],"backgroundTag":"missing-required-config-field","analyzedSha":"fb71496164c71442d08adec4571d9616ed5e1b8d","analyzedAt":"2026-09-09T00:33:09.982Z","contentChangedAt":"2026-09-09T00:33:09.982Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}