Apache Pulsar: Timing attack in SASL token signature verification
Description
Observable timing discrepancy vulnerability in Apache Pulsar SASL Authentication Provider can allow an attacker to forge a SASL Role Token that will pass signature verification. Users are recommended to upgrade to version 2.11.3, 3.0.2, or 3.1.1 which fixes the issue. Users should also consider updating the configured secret in the saslJaasServerRoleTokenSignerSecretPath file.
Any component matching an above version running the SASL Authentication Provider is affected. That includes the Pulsar Broker, Proxy, Websocket Proxy, or Function Worker.
2.11 Pulsar users should upgrade to at least 2.11.3. 3.0 Pulsar users should upgrade to at least 3.0.2. 3.1 Pulsar users should upgrade to at least 3.1.1. Any users running Pulsar 2.8, 2.9, 2.10, and earlier should upgrade to one of the above patched versions.
For additional details on this attack vector, please refer to https://codahale.com/a-lesson-in-timing-attacks/ .
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Apache Pulsar's SASL authentication provider contains a timing side-channel that lets an attacker forge role tokens and bypass signature verification.
Vulnerability
CVE-2023-51437 is an observable timing discrepancy vulnerability in Apache Pulsar's SASL Authentication Provider [2]. The SASL token signature verification process takes measurably different amounts of time depending on how many bytes of the expected signature match, which constitutes a classic timing side-channel [3]. This allows an attacker to iteratively forge a valid SASL Role Token by observing response times and adjusting the guess byte by byte [2][3].
Exploitation
The attacker does not need any prior authentication; the attack is performed remotely against any exposed SASL-authenticated endpoint (Pulsar Broker, Proxy, Websocket Proxy, or Function Worker) [2]. The only prerequisite is network access to a service that uses the SASL Authentication Provider and that accepts role tokens for verification [3]. By sending many token candidates and measuring the verification latency, the attacker can deduce the correct signature and forge a token that will pass all checks [1][3].
Impact
Successful exploitation allows an unauthenticated attacker to impersonate any Pulsar role (e.g., a superuser or tenant admin) and gain all the associated privileges on the cluster [2][3]. This includes publishing and consuming messages, modifying topic configurations, managing subscriptions, and performing administrative operations—effectively full control over the targeted Pulsar deployment [1].
Mitigation
Apache has released fixed versions: 2.11.3, 3.0.2, and 3.1.1 [2][3]. Users are strongly advised to upgrade immediately and also rotate the secret stored in the saslJaasServerRoleTokenSignerSecretPath file to invalidate any previously forged tokens [2][3]. Versions 2.8 through 2.10 and earlier are end-of-life and must migrate to a patched release [2].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.pulsar:pulsar-broker-auth-saslMaven | < 2.11.3 | 2.11.3 |
org.apache.pulsar:pulsar-broker-auth-saslMaven | >= 3.0.0, < 3.0.2 | 3.0.2 |
org.apache.pulsar:pulsar-broker-auth-saslMaven | >= 3.1.0, < 3.1.1 | 3.1.1 |
Affected products
2- Range: 0
Patches
46274fa01a75d[fix][broker] Use MessageDigest.isEqual when comparing digests (#21061)
1 file changed · +1 −1
pulsar-broker-auth-sasl/src/main/java/org/apache/pulsar/broker/authentication/SaslRoleTokenSigner.java+1 −1 modified@@ -76,7 +76,7 @@ public String verifyAndExtract(String signedStr) throws AuthenticationException String originalSignature = signedStr.substring(index + SIGNATURE.length()); String rawValue = signedStr.substring(0, index); String currentSignature = computeSignature(rawValue); - if (!originalSignature.equals(currentSignature)) { + if (!MessageDigest.isEqual(originalSignature.getBytes(), currentSignature.getBytes())){ throw new AuthenticationException("Invalid signature"); } return rawValue;
bc1019fa8ed3[fix][broker] Use MessageDigest.isEqual when comparing digests (#21061)
1 file changed · +1 −1
pulsar-broker-auth-sasl/src/main/java/org/apache/pulsar/broker/authentication/SaslRoleTokenSigner.java+1 −1 modified@@ -76,7 +76,7 @@ public String verifyAndExtract(String signedStr) throws AuthenticationException String originalSignature = signedStr.substring(index + SIGNATURE.length()); String rawValue = signedStr.substring(0, index); String currentSignature = computeSignature(rawValue); - if (!originalSignature.equals(currentSignature)) { + if (!MessageDigest.isEqual(originalSignature.getBytes(), currentSignature.getBytes())){ throw new AuthenticationException("Invalid signature"); } return rawValue;
c05954e66ff3[fix][broker] Use MessageDigest.isEqual when comparing digests (#21061)
1 file changed · +1 −1
pulsar-broker-auth-sasl/src/main/java/org/apache/pulsar/broker/authentication/SaslRoleTokenSigner.java+1 −1 modified@@ -76,7 +76,7 @@ public String verifyAndExtract(String signedStr) throws AuthenticationException String originalSignature = signedStr.substring(index + SIGNATURE.length()); String rawValue = signedStr.substring(0, index); String currentSignature = computeSignature(rawValue); - if (!originalSignature.equals(currentSignature)) { + if (!MessageDigest.isEqual(originalSignature.getBytes(), currentSignature.getBytes())){ throw new AuthenticationException("Invalid signature"); } return rawValue;
c27beca64cc9[fix][broker] Use MessageDigest.isEqual when comparing digests (#21061)
1 file changed · +1 −1
pulsar-broker-auth-sasl/src/main/java/org/apache/pulsar/broker/authentication/SaslRoleTokenSigner.java+1 −1 modified@@ -76,7 +76,7 @@ public String verifyAndExtract(String signedStr) throws AuthenticationException String originalSignature = signedStr.substring(index + SIGNATURE.length()); String rawValue = signedStr.substring(0, index); String currentSignature = computeSignature(rawValue); - if (!originalSignature.equals(currentSignature)) { + if (!MessageDigest.isEqual(originalSignature.getBytes(), currentSignature.getBytes())){ throw new AuthenticationException("Invalid signature"); } return rawValue;
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
10- github.com/advisories/GHSA-c57v-4vg5-cm2xghsaADVISORY
- lists.apache.org/thread/5kgmvvolf5tzp5rz9xjwfg2ncwvqqgl5ghsavendor-advisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2023-51437ghsaADVISORY
- www.openwall.com/lists/oss-security/2024/02/07/1ghsaWEB
- github.com/apache/pulsar/commit/6274fa01a75d74d559bb7e514c970f1fc07d15bcghsaWEB
- github.com/apache/pulsar/commit/bc1019fa8ed37b8a4c8bb01e3662c6c015e1bc27ghsaWEB
- github.com/apache/pulsar/commit/c05954e66ff33098aeb848f4bde51613ace7e47eghsaWEB
- github.com/apache/pulsar/commit/c27beca64cc93848c40a374f19eaf4d3cc4f4f03ghsaWEB
- github.com/apache/pulsar/pull/21061ghsaWEB
- www.openwall.com/lists/oss-security/2024/02/07/1ghsaWEB
News mentions
0No linked articles in our index yet.