fix(webhook): reject generic V2 signature missing timestamp instead o… - #58740
Merged
teknium1 merged 1 commit intoJul 5, 2026
Merged
Conversation
…f falling back to V1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up on #58461, which added a timestamp-bound V2 signature scheme
(
X-Webhook-Signature-V2+X-Webhook-Timestamp) for the genericwebhook route to fix a replay vulnerability in the legacy body-only V1
scheme. While #58461 was open, @teknium1 opened #58508 as a salvage of
it and merged that instead. A downgrade issue in the V2 logic was found
in review and fixed with a follow-up commit on the #58461 branch, but
that commit landed after the salvage snapshot was taken, so it did not
make it into the merged #58508. This PR carries just that fix, rebased
onto current main.
Root cause
The V2 check in
_validate_signature()wasif v2_sig and v2_timestamp:.A sender migrating from V1 to V2 typically sends both signatures
together during the transition. If a captured mixed request has its
X-Webhook-Timestampheader stripped before replay, the conditionbecomes false and validation falls through to the legacy V1 check,
which still validates against the unprotected, non-expiring V1
signature present in the same captured request — silently downgrading
a V2-protected request back into the exact replay hole V2 exists to
close.
Behavioral change
Before: a captured mixed V1+V2 request with the timestamp header
removed validated successfully via the fallback to V1.
After: the presence of
X-Webhook-Signature-V2alone commits to V2validation. A missing, malformed, or expired timestamp now rejects the
request instead of falling back to V1.
What changed
gateway/platforms/webhook.py: changed the V2 guard fromif v2_sig and v2_timestamp:toif v2_sig:, with an explicit reject(and warning) when the timestamp is absent, before the legacy V1
branch.
tests/gateway/test_webhook_adapter.py: added a regression testreproducing the exact stripped-timestamp downgrade scenario.
What is NOT changed
backward compatibility are all unchanged from fix(security): add timestamp-bound V2 signature for generic webhook r… #58461/fix(security): timestamp-bound V2 signature for generic webhook routes (#58461 salvage) #58508
Infographic