npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bernierllc/validators-signature-replay

v1.2.0

Published

Primitive validator for webhook signature validation and replay attack protection

Readme

@bernierllc/validators-signature-replay

Webhook signature validation and replay attack protection utilities for Node.js applications.

Overview

Provides comprehensive security validation for webhooks and API requests by validating HMAC signatures, checking timestamp freshness, and preventing replay attacks through nonce validation.

Installation

npm install @bernierllc/validators-signature-replay

Features

  • HMAC Signature Validation - Verify cryptographic signatures using SHA256/384/512
  • Timestamp Freshness - Reject old requests to prevent replay attacks
  • Nonce Validation - Track and reject reused request IDs
  • Comprehensive Replay Protection - All-in-one validation combining signature, timestamp, and nonce
  • Timing-Safe Comparison - Prevent timing attacks on signature validation
  • Automatic Cleanup - Expired nonces are automatically removed from memory
  • TypeScript Support - Full type definitions included

Usage

Quick Start: Complete Replay Protection

import { validateReplayAttack, generateSignature, getCurrentTimestamp } from '@bernierllc/validators-signature-replay';

// In your webhook handler
app.post('/webhook', async (req, res) => {
  const signature = req.headers['x-signature'];
  const timestamp = parseInt(req.headers['x-timestamp']);
  const nonce = req.headers['x-nonce'];
  const payload = JSON.stringify(req.body);

  const result = validateReplayAttack({
    secret: process.env.WEBHOOK_SECRET,
    signature,
    payload,
    timestamp,
    nonce,
    algorithm: 'sha256',
    maxAgeSeconds: 300, // 5 minutes
  });

  if (!result.isValid) {
    console.error('Validation failed:', result.errors);
    return res.status(401).send('Unauthorized');
  }

  // Process webhook safely
  await processWebhook(req.body);
  res.status(200).send('OK');
});

Signature Validation

import { validateSignature, generateSignature } from '@bernierllc/validators-signature-replay';

// Generate signature
const payload = JSON.stringify({ event: 'test' });
const signature = generateSignature(payload, 'secret-key', 'sha256');

// Validate signature
const result = validateSignature({
  secret: 'secret-key',
  signature,
  payload,
  algorithm: 'sha256',
});

if (result.isValid) {
  console.log('Signature valid');
} else {
  console.error('Invalid signature:', result.errors);
}

Timestamp Validation

import { validateTimestamp, getCurrentTimestamp } from '@bernierllc/validators-signature-replay';

// Get current timestamp
const timestamp = getCurrentTimestamp(); // Unix timestamp in seconds

// Validate timestamp freshness
const result = validateTimestamp({
  timestamp,
  maxAgeSeconds: 300, // Reject requests older than 5 minutes
  isMilliseconds: false, // Set true if timestamp is in milliseconds
});

if (!result.isValid) {
  console.error('Timestamp too old:', result.errors);
}

Nonce Validation

import { validateNonce, generateNonce } from '@bernierllc/validators-signature-replay';

// Generate unique nonce
const nonce = await generateNonce();

// Validate nonce (automatically tracks used nonces)
const result = validateNonce({
  nonce: req.headers['x-nonce'],
  ttlSeconds: 300, // How long to remember this nonce
});

if (!result.isValid) {
  console.error('Replay attack detected:', result.errors);
}

Custom Nonce Store Management

import { NonceStore, getGlobalNonceStore, resetGlobalNonceStore } from '@bernierllc/validators-signature-replay';

// Use global singleton store (default)
const globalStore = getGlobalNonceStore();

// Or create your own store
const customStore = new NonceStore();
customStore.addNonce('custom-nonce', 600);

// Reset global store (useful for testing)
resetGlobalNonceStore();

// Cleanup when done
customStore.destroy();

API Reference

validateReplayAttack(options)

Comprehensive validation combining signature, timestamp, and nonce validation.

Parameters:

  • secret (string) - Secret key for HMAC signature
  • signature (string) - Expected signature to validate
  • payload (string) - Payload that was signed
  • timestamp (number) - Request timestamp
  • nonce (string) - Unique request identifier
  • algorithm (string, optional) - HMAC algorithm (default: 'sha256')
  • maxAgeSeconds (number, optional) - Maximum timestamp age (default: 300)
  • isMilliseconds (boolean, optional) - Whether timestamp is in milliseconds (default: false)
  • nonceTtlSeconds (number, optional) - Nonce TTL (default: 300)

Returns: ReplayValidationResult

  • isValid (boolean) - Whether all validations passed
  • errors (string[]) - Array of error messages
  • metadata (object) - Detailed validation metadata

validateSignature(options)

Validate HMAC signature with timing-safe comparison.

Parameters:

  • secret (string) - Secret key
  • signature (string) - Signature to validate
  • payload (string) - Payload that was signed
  • algorithm (string, optional) - HMAC algorithm (default: 'sha256')

Returns: SignatureValidationResult

generateSignature(payload, secret, algorithm?)

Generate HMAC signature for a payload.

Parameters:

  • payload (string) - Data to sign
  • secret (string) - Secret key
  • algorithm (string, optional) - HMAC algorithm (default: 'sha256')

Returns: string - Hex-encoded signature

validateTimestamp(options)

Validate timestamp freshness to prevent replay attacks.

Parameters:

  • timestamp (number) - Timestamp to validate
  • maxAgeSeconds (number, optional) - Maximum age in seconds (default: 300)
  • isMilliseconds (boolean, optional) - Whether timestamp is in milliseconds (default: false)

Returns: TimestampValidationResult

validateNonce(options)

Validate nonce uniqueness to prevent replay attacks.

Parameters:

  • nonce (string) - Nonce to validate
  • ttlSeconds (number, optional) - Time to live in seconds (default: 300)

Returns: NonceValidationResult

generateNonce()

Generate cryptographically secure random nonce.

Returns: Promise - Random nonce string

Utility Functions

  • getCurrentTimestamp() - Get current Unix timestamp in seconds
  • getCurrentTimestampMs() - Get current Unix timestamp in milliseconds
  • getGlobalNonceStore() - Get global singleton nonce store
  • resetGlobalNonceStore() - Reset global nonce store (useful for testing)

Real-World Examples

GitHub Webhook Validation

app.post('/github-webhook', async (req, res) => {
  const signature = req.headers['x-hub-signature-256'];
  const deliveryId = req.headers['x-github-delivery'];
  const payload = JSON.stringify(req.body);

  const result = validateReplayAttack({
    secret: process.env.GITHUB_WEBHOOK_SECRET,
    signature: signature.replace('sha256=', ''),
    payload,
    timestamp: Date.now() / 1000,
    nonce: deliveryId,
    algorithm: 'sha256',
  });

  if (!result.isValid) {
    return res.status(401).send('Unauthorized');
  }

  // Process GitHub event
  await handleGitHubEvent(req.body);
  res.status(200).send('OK');
});

Stripe Webhook Validation

app.post('/stripe-webhook', async (req, res) => {
  const signature = req.headers['stripe-signature'];
  const eventId = req.body.id;
  const timestamp = Math.floor(Date.now() / 1000);
  const payload = JSON.stringify(req.body);

  const result = validateReplayAttack({
    secret: process.env.STRIPE_WEBHOOK_SECRET,
    signature,
    payload: timestamp + '.' + payload,
    timestamp,
    nonce: eventId,
  });

  if (!result.isValid) {
    return res.status(401).send('Unauthorized');
  }

  // Process Stripe event
  await handleStripeEvent(req.body);
  res.status(200).send('OK');
});

Security Best Practices

  1. Always Use HTTPS - Signature validation doesn't prevent man-in-the-middle attacks
  2. Short Timestamp Windows - Default 5 minutes is recommended, adjust based on clock skew
  3. Unique Nonces - Use cryptographically secure random generation
  4. Rotate Secrets - Periodically rotate webhook secrets
  5. Monitor Failed Validations - Log and alert on validation failures
  6. Limit Request Rate - Combine with rate limiting for defense in depth

Dependencies

  • @bernierllc/validators-core - Core validation types and utilities
  • @bernierllc/crypto-utils - Cryptographic utilities (optional, used for nonce generation)

Integration Status

  • Logger integration: Not applicable - This is a primitive validator package with no runtime logging needs
  • Docs-Suite: Ready - Full API documentation with JSDoc comments
  • NeverHub integration: Not applicable - This is a primitive validator package with no service discovery needs

License

Copyright (c) 2025 Bernier LLC

This file is licensed to the client under a limited-use license.