Webhook Signature Bypass: Breaking HMAC Verification Safely
Webhook endpoints turn an HTTP request into trusted business events: mark an invoice paid, provision a subscription, merge a deployment, close a ticket. The signature is often the only thing separating that action from the public internet. A verifier that is almost correct is still broken.
Start by learning the exact contract. Which header carries the signature? Is it hex or base64? Does the provider sign the raw bytes, a timestamp plus body, or selected fields? Guessing wastes time and can make valid verification look faulty.
Raw body versus parsed body
HMAC must be computed over the same bytes the sender signed. Framework middleware often parses JSON first, then the application serializes it again for verification. That creates ambiguity.
Take a valid event sent to a listener you control. Keep its semantic JSON unchanged but vary whitespace, key order, duplicate keys, Unicode escapes, or number formatting. If the verifier signs a reconstructed object while the event handler consumes the original—or the reverse—the two components may disagree about what was authenticated.
Duplicate keys deserve care:
{"plan":"free","plan":"enterprise"}
One parser may keep the first value, another the last. A signature over one interpretation paired with business logic using the other can authorize the wrong event. Demonstrate with harmless fields in your own test integration.
Weak comparisons and selectable algorithms
Look for endpoints that accept alg, a signature version, or several headers. Try missing signatures, empty values, malformed encodings, duplicate signature headers, and an unsupported algorithm. Failure must be closed; parser errors should never fall through to event processing.
A plain == comparison can leak timing, though proving a remote timing attack through internet noise is hard. Do not oversell it. The stronger bugs are accepting a caller-selected none, confusing asymmetric and HMAC modes, truncating a digest, or checking only a prefix.
Search client bundles, public examples, mobile packages, logs, and error messages for webhook secrets. A secret shipped to a browser is no longer a secret. Rotate it after your proof if the program provides a test tenant.
Replay is its own bypass
A perfectly valid HMAC does not say when an event was sent. Capture a legitimate event for your own account and replay it. Does a payment credit twice, does a coupon apply again, or does a deleted integration still process it?
Timestamped schemes should reject old events within a narrow tolerance and bind the timestamp into the signature. Event IDs need atomic deduplication. A database check followed by processing can race; send two synchronized copies in a test environment and see whether both win.
Build a responsible proof
Use provider sandbox events or a tenant you own. Replace financial actions with a test invoice or zero-value object. Show one forged, modified, or replayed request and the resulting state change. Stop there.
Include the raw request bytes, signature calculation, response, and before-and-after object state. Redact and rotate secrets. Recommend verification over untouched raw bytes, a fixed documented algorithm, constant-time full-digest comparison, strict parsing, signed timestamps, replay windows, and atomic event-ID deduplication.
The important sentence in the report is not "signature accepted." It is "an unauthenticated caller can create this trusted business event." Name that event and its consequence.