GHSA-5p54-whvp-x327: Medium severity go/github.com/anycable/anycable vulnerability
Summary The Pusher-compatible REST API includes bodymd5 in the HMAC signature string but never computes or verifies the MD5 of the received HTTP body, allowing anyone who observes a signed request to replay it with an entirely different body.
Details In pusher/http.go, the Handler function extracts bodymd5 from the URL query string (line 169) and includes it verbatim in stringToSign (line 175). It then verifies HMAC(stringToSign, secret) == authsignature. After verification succeeds, handleEvents reads and parses r.Body (lines 201-212) without ever computing md5(body) and comparing it against the bodymd5 that was signed. The Pusher protocol specification explicitly requires the server to verify this digest to prevent body-substitution attacks. There is also no authtimestamp staleness check, so replays are valid indefinitely.
PoC 1. Capture a legitimate signed POST to /apps/<appid>/events?authkey=K&authtimestamp=T&authversion=1.0&bodymd5=LEGITMD5&authsignature=SIG carrying body {"name":"safe-event","channel":"ch","data":"..."} (e.g., from TLS-terminating load-balancer logs). 2. Send a new request with the same query string parameters but a different body: {"name":"injected-event","channel":"admin","data":"malicious-payload"} 3. The server accepts the request (HMAC over stringToSign matches the original) and broadcasts the injected event to all subscribers of admin.
Impact An attacker who can read any single signed Pusher API request (from logs, a shared proxy, or a network tap) can broadcast arbitrary events to any channel indefinitely, potentially forging server-side events, corrupting application state, or delivering phishing messages to WebSocket clients.
Fix After reading r.Body, compute hex(md5(body)) and compare it to the bodymd5 query parameter using a constant-time comparison before proceeding. Additionally, reject requests whose authtimestamp is more than 600 seconds from the current time.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
go/github.com/anycable/anycableto a version that resolves this vulnerability.Fixed in 1.6.15 - Configuration
Add a server-side check in pusher/http.go to reject requests whose auth_timestamp is more than 600 seconds from the current time.
Pusher-compatible REST API (pusher/http.go Handler) auth_timestamp staleness validation = Reject if (current_time - auth_timestamp) > 600 seconds - Configuration
After reading r.Body in pusher/http.go, compute hex(md5(body)) and compare it to the body_md5 query parameter using a constant-time comparison before proceeding to signature verification/handleEvents.
Pusher-compatible REST API (pusher/http.go Handler) body_md5 verification = hex(md5(r.Body)) must constant-time equal body_md5 query parameter - Configuration
Ensure the request path verifies body_md5 by computing the MD5 digest of the received HTTP body and comparing it to the body_md5 query parameter; do not allow handleEvents to proceed without verifying that digest.
pusher/http.go Handler signature coverage behavior = Verify HMAC using a request digest that includes verified body_md5 (do not accept body substitution) - Compensating control
Limit exposure of signed Pusher API requests (e.g., avoid logging auth_signature/auth_key/auth_timestamp/body_md5, and prevent access to TLS-terminating proxy/load-balancer logs or request traces) since observing a single signed request can enable replay with a different body when body_md5 is not verified.
Event History
Frequently Asked Questions
What does an attacker need to exploit this issue?
The attacker needs to observe a legitimate signed POST request to the Pusher-compatible REST API, including its query parameters and HMAC signature. They can then reuse those values with a different request body because the server does not verify that body_md5 matches the received body.
Can a captured request be reused after it was originally sent?
Yes. The API does not perform an auth_timestamp staleness check, so captured signed requests remain replayable indefinitely.
Who is realistically exposed?
Deployments exposing AnyCable's Pusher-compatible REST API are exposed if an attacker can obtain a valid signed request. The advisory's example notes that such requests could be captured from TLS-terminating load-balancer logs.
What should be prioritized for remediation?
Update to the release containing the fix referenced by the advisory, v1.6.15. The referenced fix should ensure the received HTTP body's MD5 is computed and checked against the signed body_md5 value, and replay protection should be considered because timestamps were not validated.