Skip to content

Server does not track sign-in nonces — auth headers are replayable #88

@sacha-l

Description

@sacha-l

Observed

Every x-siws-auth payload carries a nonce in its signed message, but the server never checks it. authenticateRequest in server/api/middleware/auth.middleware.js verifies the signature, statement, and domain — but not nonce uniqueness.

Consequence: a captured x-siws-auth header can be replayed:

Proposed fix

Single-use nonces + a hard expiry on every chain:

  1. Nonce store — a auth_nonces table (nonce, expires_at); the first time a nonce is seen it is recorded, a repeat is rejected (403, replay).
  2. Expiry — require expirationTime on every sign-in message (add it to the Substrate SiwsMessage client-side; the Substrate verifier rejects expired/missing-expiry messages, matching the Ethereum/Solana verifiers).
  3. Expired nonce rows are cleaned up opportunistically.

Acceptance criteria

  • A nonce reused within its validity window is rejected with 403.
  • A sign-in message with no / past expirationTime is rejected, on all three chains.
  • Migration adds the auth_nonces table.
  • Vitest coverage for the nonce store and a middleware replay case.

Notes

A full server-issued challenge/response (server hands out the nonce) would be stronger but is a larger flow change. Seen-nonce tracking + expiry is the proportionate fix and closes the practical replay window. Identified during a launch-readiness review.

Metadata

Metadata

Assignees

No one assigned

    Labels

    claude-suggestedIssue suggested by the Claude agent during a /ship-issue run

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions