{"record":{"id":"4c73d2854e1ac895","repo":"apache/pulsar","slug":"invalid-signature","errorCode":null,"errorMessage":"Invalid signature","messagePattern":"Invalid signature","errorType":"exception","errorClass":"AuthenticationException","httpStatus":null,"severity":"error","filePath":"pulsar-broker-auth-sasl/src/main/java/org/apache/pulsar/broker/authentication/SaslRoleTokenSigner.java","lineNumber":80,"sourceCode":"    /**\n     * Verifies a signed string and extracts the original string.\n     *\n     * @param signedStr the signed string to verify and extract.\n     *\n     * @return the extracted original string.\n     *\n     * @throws AuthenticationException thrown if the given string is not a signed string or if the signature is invalid.\n     */\n    public String verifyAndExtract(String signedStr) throws AuthenticationException {\n        int index = signedStr.lastIndexOf(SIGNATURE);\n        if (index == -1) {\n            throw new AuthenticationException(\"Invalid signed text: \" + signedStr);\n        }\n        String originalSignature = signedStr.substring(index + SIGNATURE.length());\n        String rawValue = signedStr.substring(0, index);\n        String currentSignature = computeSignature(rawValue);\n        if (!MessageDigest.isEqual(originalSignature.getBytes(), currentSignature.getBytes())){\n            throw new AuthenticationException(\"Invalid signature\");\n        }\n        return rawValue;\n    }\n\n    /**\n     * Returns the signature of a string.\n     *\n     * @param str string to sign.\n     *\n     * @return the signature for the string.\n     */\n    protected String computeSignature(String str) {\n        try {\n            MessageDigest md = MessageDigest.getInstance(\"SHA-512\");\n\n            md.update(str.getBytes());\n\n            md.update(secret);","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker-auth-sasl/src/main/java/org/apache/pulsar/broker/authentication/SaslRoleTokenSigner.java#L62-L98","documentation":"After locating the signature suffix in a signed string, verifyAndExtract recomputes the HMAC over the payload with its configured secret and compares it to the received signature. If they differ the string was signed with a different secret or modified in transit, so an AuthenticationException \"Invalid signature\" is thrown. The comparison uses MessageDigest.isEqual (constant time) to avoid timing attacks.","triggerScenarios":"verifyAndExtract() receives a well-formed signed string whose signature does not match a recomputation with the current secret key — typically after a broker secret-key change/rotation, or the payload was tampered with.","commonSituations":"Multiple brokers with different SaslRoleTokenSigner secret keys behind a load balancer; secretKey config changed on one node but not others; token copied between clusters; MITM tampering.","solutions":["Verify all broker nodes share the identical secretKey for SaslRoleTokenSigner","Re-issue the token: re-authenticate so the client gets a token signed with the current secret","If tampering is suspected, treat it as a security event and reject the request"],"exampleFix":"// server config: keep the same secret everywhere\n// broker.conf (each node)\n// saslRoleTokenSignerSecret=shared-hmac-secret\nString role;\ntry {\n    role = signer.verifyAndExtract(signedToken);\n} catch (AuthenticationException e) {\n    // force client re-authentication\n    throw new RestException(Response.Status.UNAUTHORIZED, \"re-authenticate\");\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    String role = signer.verifyAndExtract(signedStr);\n} catch (AuthenticationException e) {\n    audit.log(\"signature mismatch for \" + clientAddr); // security signal\n    respond(401);\n}","preventionTips":["Keep signer secretKey identical across all broker nodes","Rotate secrets only with coordinated token re-issuance","Monitor 401 spikes — may indicate tampering or key drift"],"tags":["authentication","signature","hmac","security"],"backgroundTag":"signature-mismatch","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}