← Back to list
● General attack technique · replaying an intercepted request

A request with a valid signature, sent twice —
I'm publishing the code that tells the difference

A request with a perfectly valid signature can still be captured and resent as-is — a system that only checks the signature processes it again, identically. I reproduced this with an actual capture-and-replay script and verified a nonce (one-time token) defense stops it.

See live demo → Jump to code

Who this is for

A learning resource for backend developers building APIs that assume signature verification alone guarantees request integrity.

OWASP
A classic attack pattern under OWASP's Cryptographic Failures category
3x
The same signed request was resent 3 times during testing — the vulnerable side processed all 3
$0
What this code costs right now — free

Code — signature-only-transfer.js

A vulnerable transfer API that checks the signature but never whether the request was already processed.

signature-only-transfer.js
// Vulnerable transfer API — a valid signature is executed as-is.
// A signature proves WHO authored a request, not whether it has
// already run — replaying a captured request runs it again.
class SignatureOnlyTransfer {
  constructor(verifySignature) {
    this.verifySignature = verifySignature;
  }

  transfer(request) {
    if (!this.verifySignature(request)) {
      return { status: 'bad_signature' };
    }
    executeTransfer(request.from, request.to, request.amount);
    return { status: 'ok' };
  }
}

module.exports = { SignatureOnlyTransfer };

Code — nonce-guard.js

The fix: even a validly-signed request is rejected if its nonce has already been used.

nonce-guard.js
// The fix: a nonce (one-time token) rides inside the signed payload,
// and a nonce the server has already seen is rejected even though
// the signature itself is still perfectly valid.
class NonceGuardedTransfer {
  constructor(verifySignature) {
    this.verifySignature = verifySignature;
    this.usedNonces = new Set();
  }

  transfer(request) {
    if (!this.verifySignature(request)) {
      return { status: 'bad_signature' };
    }
    if (this.usedNonces.has(request.nonce)) {
      return { status: 'replayed_nonce' }; // signature valid, but reused
    }
    this.usedNonces.add(request.nonce);
    executeTransfer(request.from, request.to, request.amount);
    return { status: 'ok' };
  }
}

module.exports = { NonceGuardedTransfer };

Price

Free (for now)
A deeper version (timestamp windows, distributed nonce storage) ships later — pay-what-you-want, starting at $0.
goalsgo7574@gmail.com
This isn’t a purchase — just email me "notify me about the paid version" and I’ll reach out when it ships.
Note: This is a scaled-down learning implementation. Nonces live in a single in-memory Set — a real system needs a storage layer safe across distributed instances, plus an expiry policy.

Leave feedback

Questions or feedback about this product? Leave your email if you'd like a reply (optional).