fix(hmac): add per-request nonce to prevent replay attacks#24
Merged
Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
5 tasks
Extends the HMAC signature scheme to include a nonce (16 random bytes,
hex-encoded) in every request. The new message formula is:
HMAC-SHA256(secret, "{timestamp}:{nonce}:{pathname}")
The nonce is sent in a new `pmux-nonce` header and validated for correct
format (32 lowercase hex chars). This eliminates the replay window that
existed in v1 where any request could be replayed within the ±60s
clock-skew tolerance.
Full replay prevention via server-side nonce uniqueness enforcement
requires an HMAC_NONCES KV binding; that is tracked as a follow-up.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Terry Howe <terrylhowe@gmail.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
rbonestell
added a commit
that referenced
this pull request
May 26, 2026
…gents) The nonce PR (#24) inadvertently changed validateClientSignature (legacy unversioned paths) to also require the pmux-nonce header, breaking agents distributed via Homebrew that were built before nonce support was added. Restore validateClientSignature to the original formula: HMAC-SHA256(secret, "{timestamp}:{pathname}") Only validateClientSignatureV1 (/v1/ paths) requires the nonce: HMAC-SHA256(secret, "{timestamp}:{nonce}:{pathname}") This preserves backward compatibility for older agents using legacy paths while keeping replay-attack protection for current agents on /v1/ paths. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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
pmux-nonceheader{timestamp}:{pathname}(v1) to{timestamp}:{nonce}:{pathname}(v2), eliminating the replay window within the ±60s clock-skew toleranceDetails
Breaking change: clients must now send
pmux-noncealongsidepmux-signatureandpmux-timestamp. Requests missing this header receive401 { error: "missing client signature" }.Follow-up: Full replay prevention (server-side nonce uniqueness enforcement) requires an
HMAC_NONCESKV namespace binding. Nonce format is validated but uniqueness is not yet enforced — this is documented inhmac.tsand tracked as a follow-up.This PR is coordinated with TerryHowe/pmux-agent#fix/hmac-nonce which updates the agent (Go) side to generate and send the nonce.
Test plan
npm test)rejects when pmux-nonce header is missingrejects a nonce that is not 32 lowercase hex charsrejects a nonce with uppercase hex chars🤖 Generated with Claude Code