{"record":{"id":"dabb5bf9e5914c67","repo":"jwtk/jjwt","slug":"the-jws-header-references-signature-algorithm-non","errorCode":null,"errorMessage":"The JWS header references signature algorithm 'none' yet the compact JWS string contains a signature. This is not permitted per https://tools.ietf.org/html/rfc7518#section-3.6.","messagePattern":"The JWS header references signature algorithm 'none' yet the compact JWS string contains a signature\\. This is not permitted per https://tools\\.ietf\\.org/html/rfc7518#section-3\\.6\\.","errorType":"exception","errorClass":"MalformedJwtException","httpStatus":null,"severity":"error","filePath":"impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java","lineNumber":417,"sourceCode":"        if (!Strings.hasText(alg)) {\n            String msg = tokenized instanceof TokenizedJwe ? MISSING_JWE_ALG_MSG : MISSING_JWS_ALG_MSG;\n            throw new MalformedJwtException(msg);\n        }\n        final boolean unsecured = Jwts.SIG.NONE.getId().equalsIgnoreCase(alg);\n\n        final CharSequence base64UrlDigest = tokenized.getDigest();\n        final boolean hasDigest = Strings.hasText(base64UrlDigest);\n        if (unsecured) {\n            if (tokenized instanceof TokenizedJwe) {\n                throw new MalformedJwtException(JWE_NONE_MSG);\n            }\n            // Unsecured JWTs are disabled by default per the RFC:\n            if (!this.unsecured) {\n                String msg = UNSECURED_DISABLED_MSG_PREFIX + header;\n                throw new UnsupportedJwtException(msg);\n            }\n            if (hasDigest) {\n                throw new MalformedJwtException(JWS_NONE_SIG_MISMATCH_MSG);\n            }\n            if (header.containsKey(DefaultProtectedHeader.CRIT.getId())) {\n                String msg = String.format(CRIT_UNSECURED_MSG, header);\n                throw new MalformedJwtException(msg);\n            }\n        } else if (!hasDigest) { // something other than 'none'.  Must have a digest component:\n            String fmt = tokenized instanceof TokenizedJwe ? MISSING_JWE_DIGEST_MSG_FMT : MISSING_JWS_DIGEST_MSG_FMT;\n            String msg = String.format(fmt, alg);\n            throw new MalformedJwtException(msg);\n        }\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","sourceCodeStart":399,"sourceCodeEnd":435,"githubUrl":"https://github.com/jwtk/jjwt/blob/fb71496164c71442d08adec4571d9616ed5e1b8d/impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java#L399-L435","documentation":"A JWS whose header declares alg='none' must carry no signature (RFC 7518 Section 3.6). jjwt throws MalformedJwtException when the compact string declares 'none' but still contains a non-empty signature (digest) component, because the token contradicts its own header.","triggerScenarios":"Parsing a JWS with alg='none' in the header but a third compact segment (signature) present — i.e. unsecured=true enabled on the parser and hasDigest is true.","commonSituations":"Tokens assembled by concatenating an unsigned header/payload with a leftover signature segment; buggy custom serializers that always append a signature; corruption or tampering where the alg was changed to none without removing the signature.","solutions":["Re-issue the token correctly: either sign it with a real algorithm, or use alg=none with an empty trailing segment (header.payload.).","Remove the signature segment if the token is meant to be unsecured (and ensure the parser permits unsecured tokens).","Check the producing library/configuration for always appending signatures regardless of algorithm.","Treat mismatched none-alg-with-signature tokens as potentially tampered and reject them."],"exampleFix":"// before: alg=none but signature present\nString bad = b64(headerNone) + \".\" + b64(payload) + \".\" + b64(sig);\n\n// after: unsecured JWS has empty signature segment\nString good = b64(headerNone) + \".\" + b64(payload) + \".\";","handlingStrategy":"validation","validationCode":"String[] parts = token.split(\"\\\\.\", -1);\nString headerJson = new String(java.util.Base64.getUrlDecoder().decode(parts[0]), java.nio.charset.StandardCharsets.UTF_8);\nif (headerJson.contains(\"alg\\\":\\\"none\\\"\") && parts.length > 2 && !parts[2].isEmpty()) throw new IllegalArgumentException(\"alg=none token must not carry a signature\");","typeGuard":null,"tryCatchPattern":"try { return parser.parse(token); }\ncatch (io.jsonwebtoken.MalformedJwtException e) { throw new InvalidTokenException(\"none-alg token with signature\", e); }","preventionTips":["Unsecured JWS must end with an empty signature segment","Never change a token's alg header without recomputing/removing the signature","Use a library builder instead of string concatenation","Treat inconsistent none+signature tokens as potentially tampered"],"tags":["jwt","jws","alg-none","signature-mismatch","malformed-token"],"backgroundTag":"invalid-argument-format","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"}