Skip to content

fix(hmac): add per-request nonce to prevent replay attacks#24

Merged
rbonestell merged 1 commit into
ShiftinBits:mainfrom
TerryHowe:fix/hmac-nonce
May 24, 2026
Merged

fix(hmac): add per-request nonce to prevent replay attacks#24
rbonestell merged 1 commit into
ShiftinBits:mainfrom
TerryHowe:fix/hmac-nonce

Conversation

@TerryHowe

Copy link
Copy Markdown
Contributor

Summary

  • Extends the HMAC signature scheme with a per-request nonce (16 random bytes, 32-char lowercase hex string) sent in a new pmux-nonce header
  • Updates the signature message formula from {timestamp}:{pathname} (v1) to {timestamp}:{nonce}:{pathname} (v2), eliminating the replay window within the ±60s clock-skew tolerance
  • Validates nonce format on every request: must be exactly 32 lowercase hex characters

Details

Breaking change: clients must now send pmux-nonce alongside pmux-signature and pmux-timestamp. Requests missing this header receive 401 { error: "missing client signature" }.

Follow-up: Full replay prevention (server-side nonce uniqueness enforcement) requires an HMAC_NONCES KV namespace binding. Nonce format is validated but uniqueness is not yet enforced — this is documented in hmac.ts and 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

  • All 283 existing tests pass (npm test)
  • Added rejects when pmux-nonce header is missing
  • Added rejects a nonce that is not 32 lowercase hex chars
  • Added rejects a nonce with uppercase hex chars
  • Cross-platform vector test updated to v2 formula (round-trip validation; absolute hex to be pinned once Go and mobile both adopt v2)

🤖 Generated with Claude Code

@snyk-io

snyk-io Bot commented Apr 20, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

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-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@TerryHowe TerryHowe marked this pull request as ready for review May 23, 2026 16:47
@rbonestell rbonestell merged commit 9cfbdf1 into ShiftinBits:main May 24, 2026
3 of 4 checks passed
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants