Get Started
StateSet One Guides
- Orders Management Quickstart
- Order settlements
- Wholesale Quickstart
- Returns Quickstart
- Warranties Quickstart
- Subscriptions Quickstart
- Manufacturing and Production Quickstart
- Warehouse Quickstart
- COGS Quickstart Guide
- Supplier Quickstart
- Inventory Management Quickstart
- Error Handling Best Practices
- API Rate Limiting & Optimization
- Performance Optimization Guide
- Comprehensive Testing Guide
StateSet ResponseCX Guides
- Getting Started with ResponseCX
- Agent Training Guide
- Agent Objectives, Goals, Metrics & Rewards Guide
- Multi-Agent System Architectures
- Reinforcement Learning Platform Overview
- GRPO Agent Framework
- Knowledge Base Quickstart
- RAG Quickstart
- Agents Quickstart
- Agent Attributes & Personality
- Agent Rules Quickstart
- Examples Quickstart
- Evaluations
- Agent Schedules & Automation
- Agent Functions Quickstart
- Shopify Product Quickstart
- Gorgias Ticket Quickstart
- Vision Quickstart
- Voice AI Quickstart
- Synthetic Data Studio
- StateSet Synthetic Data Studio Architecture Guide
StateSet Commerce Network Guides
StateSet ReSponse API Documentation
Webhook Security Guide
Comprehensive guide to securing Stateset webhooks with signature verification, replay protection, and security best practices
Webhook Security Guide
Learn how to securely handle Stateset webhooks with comprehensive security patterns, signature verification, replay attack prevention, and production-ready implementation examples.
Prerequisites
Before you begin, ensure you have:
- A Stateset account with webhook endpoints configured
- Understanding of HMAC signature verification
- HTTPS endpoint for receiving webhooks
- Basic knowledge of Node.js/Express (examples provided)
- Production environment security considerations
Security Fundamentals
Webhook Secret Configuration
Configure your webhook secret in the Stateset dashboard:
# .env
STATESET_WEBHOOK_SECRET=whsec_3rK9pL7nQ2xS5mT8...
WEBHOOK_ENDPOINT_URL=https://api.yourstore.com/webhooks/stateset
# Security Settings
WEBHOOK_TIMEOUT_MS=5000
MAX_WEBHOOK_BODY_SIZE=1048576 # 1MB
ENABLE_REPLAY_PROTECTION=true
REPLAY_TOLERANCE_SECONDS=300 # 5 minutes
HTTPS Requirements
Ensure your webhook endpoint uses HTTPS:
// webhook-server.js
import express from 'express';
import https from 'https';
import fs from 'fs';
const app = express();
// Production HTTPS setup
if (process.env.NODE_ENV === 'production') {
const options = {
key: fs.readFileSync('/path/to/private-key.pem'),
cert: fs.readFileSync('/path/to/certificate.pem'),
// Additional security headers
secureProtocol: 'TLSv1_2_method',
ciphers: [
'ECDHE-RSA-AES128-GCM-SHA256',
'ECDHE-RSA-AES256-GCM-SHA384',
'ECDHE-RSA-AES128-SHA256',
'ECDHE-RSA-AES256-SHA384'
].join(':'),
honorCipherOrder: true
};
https.createServer(options, app).listen(443, () => {
console.log('Secure webhook server running on port 443');
});
} else {
app.listen(3000, () => {
console.log('Development webhook server running on port 3000');
});
}
Request Validation Middleware
Implement comprehensive request validation:
import rateLimit from 'express-rate-limit';
import helmet from 'helmet';
// Security middleware
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'none'"],
styleSrc: ["'none'"],
imgSrc: ["'none'"]
}
}
}));
// Rate limiting for webhook endpoints
const webhookLimiter = rateLimit({
windowMs: 1 * 60 * 1000, // 1 minute
max: 100, // Limit each IP to 100 requests per windowMs
message: 'Too many webhook requests from this IP',
standardHeaders: true,
legacyHeaders: false,
skip: (req) => {
// Skip rate limiting for known Stateset IPs
const statesetIPs = ['52.89.214.238', '54.187.174.169', '54.187.205.235'];
return statesetIPs.includes(req.ip);
}
});
app.use('/webhooks', webhookLimiter);
Signature Verification
Basic HMAC Verification
Implement secure signature verification:
import crypto from 'crypto';
import express from 'express';
class WebhookVerifier {
constructor(secret) {
this.secret = secret;
this.tolerance = 300; // 5 minutes in seconds
}
verifySignature(payload, signature, timestamp) {
try {
// Verify timestamp to prevent replay attacks
const currentTime = Math.floor(Date.now() / 1000);
if (Math.abs(currentTime - timestamp) > this.tolerance) {
throw new Error('Webhook timestamp is too old');
}
// Create expected signature
const expectedSignature = this.computeSignature(payload, timestamp);
// Use constant-time comparison to prevent timing attacks
return this.secureCompare(signature, expectedSignature);
} catch (error) {
console.error('Signature verification failed:', error.message);
return false;
}
}
computeSignature(payload, timestamp) {
const signedPayload = `${timestamp}.${payload}`;
return crypto
.createHmac('sha256', this.secret)
.update(signedPayload, 'utf8')
.digest('hex');
}
secureCompare(a, b) {
if (a.length !== b.length) {
return false;
}
let result = 0;
for (let i = 0; i < a.length; i++) {
result |= a.charCodeAt(i) ^ b.charCodeAt(i);
}
return result === 0;
}
extractSignature(header) {
const elements = header.split(',');
const signatures = {};
for (const element of elements) {
const [key, value] = element.split('=');
if (key === 'v1') {
signatures.v1 = value;
} else if (key === 't') {
signatures.timestamp = parseInt(value, 10);
}
}
return signatures;
}
}
// Initialize verifier
const verifier = new WebhookVerifier(process.env.STATESET_WEBHOOK_SECRET);
// Middleware to capture raw body
app.use('/webhooks/stateset', express.raw({ type: 'application/json', limit: '1mb' }));
// Webhook verification middleware
function verifyWebhook(req, res, next) {
const signature = req.headers['stateset-signature'];
const payload = req.body.toString();
if (!signature) {
return res.status(401).json({ error: 'Missing signature header' });
}
try {
const { v1: sig, timestamp } = verifier.extractSignature(signature);
if (!sig || !timestamp) {
return res.status(401).json({ error: 'Invalid signature format' });
}
const isValid = verifier.verifySignature(payload, sig, timestamp);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Parse JSON only after verification
req.body = JSON.parse(payload);
next();
} catch (error) {
console.error('Webhook verification error:', error);
return res.status(400).json({ error: 'Webhook verification failed' });
}
}
Advanced Signature Verification
Production-ready implementation with enhanced security:
class AdvancedWebhookVerifier extends WebhookVerifier {
constructor(secret, options = {}) {
super(secret);
this.tolerance = options.tolerance || 300;
this.enableReplayProtection = options.enableReplayProtection !== false;
this.processedWebhooks = new Map(); // In production, use Redis
this.maxCacheSize = options.maxCacheSize || 10000;
}
async verifyWebhookWithReplayProtection(payload, signature, timestamp, webhookId) {
// Check for replay attacks
if (this.enableReplayProtection && this.processedWebhooks.has(webhookId)) {
throw new Error('Webhook has already been processed (replay attack detected)');
}
// Verify signature
const isValid = this.verifySignature(payload, signature, timestamp);
if (isValid && this.enableReplayProtection) {
// Store webhook ID to prevent replay
this.processedWebhooks.set(webhookId, Date.now());
// Clean up old entries to prevent memory leaks
this.cleanupCache();
}
return isValid;
}
cleanupCache() {
if (this.processedWebhooks.size > this.maxCacheSize) {
const cutoff = Date.now() - (this.tolerance * 1000);
for (const [id, timestamp] of this.processedWebhooks.entries()) {
if (timestamp < cutoff) {
this.processedWebhooks.delete(id);
}
}
}
}
// Verify multiple signature versions for backward compatibility
verifyMultipleSignatures(payload, signatureHeader, timestamp) {
const signatures = this.extractSignature(signatureHeader);
// Try v1 signature first
if (signatures.v1) {
const isValid = this.secureCompare(
signatures.v1,
this.computeSignature(payload, timestamp)
);
if (isValid) return true;
}
// Fallback to other versions if needed
// This allows for graceful migration between signature versions
return false;
}
}
// Production webhook handler
const advancedVerifier = new AdvancedWebhookVerifier(
process.env.STATESET_WEBHOOK_SECRET,
{
tolerance: 300,
enableReplayProtection: true,
maxCacheSize: 10000
}
);
async function advancedVerifyWebhook(req, res, next) {
const signature = req.headers['stateset-signature'];
const webhookId = req.headers['stateset-webhook-id'];
const payload = req.body.toString();
if (!signature || !webhookId) {
return res.status(401).json({
error: 'Missing required headers',
required: ['stateset-signature', 'stateset-webhook-id']
});
}
try {
const { v1: sig, timestamp } = advancedVerifier.extractSignature(signature);
const isValid = await advancedVerifier.verifyWebhookWithReplayProtection(
payload,
sig,
timestamp,
webhookId
);
if (!isValid) {
// Log security event
console.warn('Webhook security violation:', {
ip: req.ip,
userAgent: req.headers['user-agent'],
timestamp: new Date().toISOString(),
webhookId,
signatureProvided: !!signature
});
return res.status(401).json({ error: 'Webhook verification failed' });
}
req.body = JSON.parse(payload);
req.webhookId = webhookId;
next();
} catch (error) {
console.error('Advanced webhook verification error:', error);
return res.status(400).json({ error: error.message });
}
}
Event Processing Security
Secure Event Handler
Implement secure and resilient event processing:
class SecureWebhookProcessor {
constructor() {
this.eventHandlers = new Map();
this.processingQueue = [];
this.maxRetries = 3;
this.retryDelays = [1000, 5000, 15000]; // Progressive backoff
}
registerHandler(eventType, handler, options = {}) {
this.eventHandlers.set(eventType, {
handler,
requiresAuth: options.requiresAuth !== false,
timeout: options.timeout || 30000,
retryable: options.retryable !== false
});
}
async processWebhook(webhookData, context = {}) {
const { event, data, id: webhookId } = webhookData;
// Input validation
if (!event || !data) {
throw new Error('Invalid webhook payload structure');
}
const handlerConfig = this.eventHandlers.get(event);
if (!handlerConfig) {
console.warn(`No handler registered for event: ${event}`);
return { status: 'ignored', reason: 'no_handler' };
}
// Security context validation
if (handlerConfig.requiresAuth && !context.authenticated) {
throw new Error('Authentication required for this event type');
}
return await this.executeWithRetry(
handlerConfig,
data,
{ ...context, event, webhookId }
);
}
async executeWithRetry(handlerConfig, data, context) {
let lastError;
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
try {
// Set timeout for handler execution
const result = await Promise.race([
handlerConfig.handler(data, context),
this.timeoutPromise(handlerConfig.timeout)
]);
return {
status: 'success',
result,
attempt: attempt + 1
};
} catch (error) {
lastError = error;
console.error(`Webhook handler failed (attempt ${attempt + 1}):`, {
event: context.event,
webhookId: context.webhookId,
error: error.message,
stack: error.stack
});
// Don't retry certain types of errors
if (!handlerConfig.retryable || this.isNonRetryableError(error)) {
break;
}
// Wait before retrying (except on last attempt)
if (attempt < this.maxRetries) {
await this.delay(this.retryDelays[attempt] || 15000);
}
}
}
throw new Error(
`Webhook processing failed after ${this.maxRetries + 1} attempts: ${lastError.message}`
);
}
timeoutPromise(timeout) {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error('Handler timeout')), timeout);
});
}
isNonRetryableError(error) {
const nonRetryablePatterns = [
/validation/i,
/authentication/i,
/authorization/i,
/not found/i,
/duplicate/i
];
return nonRetryablePatterns.some(pattern =>
pattern.test(error.message)
);
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
// Initialize processor and register handlers
const processor = new SecureWebhookProcessor();
// Order event handlers
processor.registerHandler('order.created', async (orderData, context) => {
// Validate order data
if (!orderData.id || !orderData.customer_email) {
throw new Error('Invalid order data: missing required fields');
}
// Process order creation
console.log(`Processing order creation: ${orderData.id}`);
// Your business logic here
await processNewOrder(orderData);
return { processed: true, orderId: orderData.id };
}, { timeout: 30000 });
processor.registerHandler('order.cancelled', async (orderData, context) => {
// Handle order cancellation
console.log(`Processing order cancellation: ${orderData.id}`);
await processOrderCancellation(orderData);
return { processed: true, orderId: orderData.id };
});
processor.registerHandler('payment.failed', async (paymentData, context) => {
// Handle payment failures (non-retryable)
console.log(`Processing payment failure: ${paymentData.payment_id}`);
await handlePaymentFailure(paymentData);
return { processed: true, paymentId: paymentData.payment_id };
}, { retryable: false });
Complete Webhook Endpoint
Production-ready webhook endpoint with all security measures:
// Complete secure webhook endpoint
app.post('/webhooks/stateset',
advancedVerifyWebhook,
async (req, res) => {
const startTime = Date.now();
try {
// Add request context
const context = {
ip: req.ip,
userAgent: req.headers['user-agent'],
timestamp: new Date().toISOString(),
authenticated: true, // Set by verification middleware
webhookId: req.webhookId
};
// Process webhook
const result = await processor.processWebhook(req.body, context);
// Log successful processing
console.log('Webhook processed successfully:', {
event: req.body.event,
webhookId: req.webhookId,
processingTime: Date.now() - startTime,
result: result.status
});
// Return success response
res.status(200).json({
received: true,
processed: result.status === 'success',
webhookId: req.webhookId,
processingTime: Date.now() - startTime
});
} catch (error) {
// Log error with context
console.error('Webhook processing error:', {
event: req.body?.event,
webhookId: req.webhookId,
error: error.message,
stack: error.stack,
processingTime: Date.now() - startTime
});
// Return appropriate error response
const statusCode = error.message.includes('authentication') ? 401 :
error.message.includes('validation') ? 400 : 500;
res.status(statusCode).json({
received: true,
processed: false,
error: error.message,
webhookId: req.webhookId
});
}
}
);
IP Allowlisting
Stateset IP Ranges
Configure IP allowlisting for enhanced security:
class IPAllowlist {
constructor() {
// Stateset webhook IP ranges (update as needed)
this.allowedRanges = [
'52.89.214.238/32',
'54.187.174.169/32',
'54.187.205.235/32',
// Add more Stateset IP ranges as provided
];
this.compiledRanges = this.compileRanges();
}
compileRanges() {
return this.allowedRanges.map(range => {
const [ip, cidr] = range.split('/');
const mask = ~(0xFFFFFFFF >>> parseInt(cidr, 10));
return {
network: this.ipToInt(ip) & mask,
mask: mask
};
});
}
ipToInt(ip) {
return ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet, 10), 0) >>> 0;
}
isAllowed(ip) {
const ipInt = this.ipToInt(ip);
return this.compiledRanges.some(range =>
(ipInt & range.mask) === range.network
);
}
}
// IP allowlist middleware
const ipAllowlist = new IPAllowlist();
function checkIPAllowlist(req, res, next) {
// Skip in development
if (process.env.NODE_ENV !== 'production') {
return next();
}
const clientIP = req.ip || req.connection.remoteAddress;
if (!ipAllowlist.isAllowed(clientIP)) {
console.warn('Webhook request from unauthorized IP:', {
ip: clientIP,
userAgent: req.headers['user-agent'],
timestamp: new Date().toISOString()
});
return res.status(403).json({
error: 'IP not allowed',
ip: clientIP
});
}
next();
}
// Apply IP allowlist before webhook processing
app.use('/webhooks/stateset', checkIPAllowlist);
Monitoring and Alerting
Security Event Monitoring
Implement comprehensive security monitoring:
import winston from 'winston';
class WebhookSecurityMonitor {
constructor() {
this.logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
),
transports: [
new winston.transports.File({ filename: 'webhook-security.log' }),
new winston.transports.Console()
]
});
this.securityEvents = new Map();
this.alertThresholds = {
failedVerifications: 10,
suspiciousIPs: 5,
timeWindow: 300000 // 5 minutes
};
}
logSecurityEvent(eventType, data) {
const event = {
type: eventType,
timestamp: Date.now(),
data: data
};
this.logger.warn('Security event detected', event);
// Track events for alerting
this.trackEventForAlerting(eventType, data);
}
trackEventForAlerting(eventType, data) {
const key = `${eventType}:${data.ip || 'unknown'}`;
const now = Date.now();
if (!this.securityEvents.has(key)) {
this.securityEvents.set(key, []);
}
const events = this.securityEvents.get(key);
events.push(now);
// Clean old events
const cutoff = now - this.alertThresholds.timeWindow;
const recentEvents = events.filter(timestamp => timestamp > cutoff);
this.securityEvents.set(key, recentEvents);
// Check for alert conditions
this.checkAlertConditions(eventType, data, recentEvents.length);
}
checkAlertConditions(eventType, data, eventCount) {
let shouldAlert = false;
if (eventType === 'signature_verification_failed' &&
eventCount >= this.alertThresholds.failedVerifications) {
shouldAlert = true;
}
if (eventType === 'suspicious_ip' &&
eventCount >= this.alertThresholds.suspiciousIPs) {
shouldAlert = true;
}
if (shouldAlert) {
this.sendSecurityAlert(eventType, data, eventCount);
}
}
async sendSecurityAlert(eventType, data, eventCount) {
const alert = {
type: 'webhook_security_alert',
eventType,
count: eventCount,
timeWindow: this.alertThresholds.timeWindow / 1000 / 60, // minutes
data,
timestamp: new Date().toISOString()
};
this.logger.error('Security alert triggered', alert);
// Send to monitoring service (PagerDuty, Slack, etc.)
try {
await this.sendToMonitoringService(alert);
} catch (error) {
console.error('Failed to send security alert:', error);
}
}
async sendToMonitoringService(alert) {
// Example: Send to Slack webhook
if (process.env.SLACK_WEBHOOK_URL) {
const message = {
text: `🚨 Webhook Security Alert: ${alert.eventType}`,
attachments: [{
color: 'danger',
fields: [
{ title: 'Event Type', value: alert.eventType, short: true },
{ title: 'Count', value: alert.count, short: true },
{ title: 'Time Window', value: `${alert.timeWindow} minutes`, short: true },
{ title: 'IP Address', value: alert.data.ip || 'Unknown', short: true }
],
timestamp: Math.floor(Date.now() / 1000)
}]
};
// Send to Slack (implement HTTP request)
// await fetch(process.env.SLACK_WEBHOOK_URL, { ... });
}
}
}
// Initialize security monitor
const securityMonitor = new WebhookSecurityMonitor();
// Enhanced verification middleware with monitoring
function monitoredVerifyWebhook(req, res, next) {
const signature = req.headers['stateset-signature'];
const webhookId = req.headers['stateset-webhook-id'];
const payload = req.body.toString();
const clientIP = req.ip;
if (!signature || !webhookId) {
securityMonitor.logSecurityEvent('missing_headers', {
ip: clientIP,
userAgent: req.headers['user-agent'],
missingHeaders: [
!signature && 'stateset-signature',
!webhookId && 'stateset-webhook-id'
].filter(Boolean)
});
return res.status(401).json({
error: 'Missing required headers'
});
}
try {
const { v1: sig, timestamp } = advancedVerifier.extractSignature(signature);
const isValid = advancedVerifier.verifySignature(payload, sig, timestamp);
if (!isValid) {
securityMonitor.logSecurityEvent('signature_verification_failed', {
ip: clientIP,
userAgent: req.headers['user-agent'],
webhookId,
hasSignature: !!signature,
hasTimestamp: !!timestamp
});
return res.status(401).json({ error: 'Invalid signature' });
}
req.body = JSON.parse(payload);
req.webhookId = webhookId;
next();
} catch (error) {
securityMonitor.logSecurityEvent('verification_error', {
ip: clientIP,
error: error.message,
webhookId
});
return res.status(400).json({ error: 'Webhook verification failed' });
}
}
Testing Webhook Security
Security Test Suite
Comprehensive test suite for webhook security:
// webhook-security.test.js
import crypto from 'crypto';
import request from 'supertest';
import app from '../webhook-server.js';
describe('Webhook Security', () => {
const webhookSecret = 'test_webhook_secret';
function createValidSignature(payload, timestamp) {
const signedPayload = `${timestamp}.${payload}`;
const signature = crypto
.createHmac('sha256', webhookSecret)
.update(signedPayload, 'utf8')
.digest('hex');
return `t=${timestamp},v1=${signature}`;
}
describe('Signature Verification', () => {
it('should accept valid signatures', async () => {
const payload = JSON.stringify({ event: 'order.created', data: { id: '123' } });
const timestamp = Math.floor(Date.now() / 1000);
const signature = createValidSignature(payload, timestamp);
const response = await request(app)
.post('/webhooks/stateset')
.set('stateset-signature', signature)
.set('stateset-webhook-id', 'test-webhook-id')
.send(payload)
.expect(200);
expect(response.body.received).toBe(true);
});
it('should reject invalid signatures', async () => {
const payload = JSON.stringify({ event: 'order.created', data: { id: '123' } });
await request(app)
.post('/webhooks/stateset')
.set('stateset-signature', 'invalid-signature')
.set('stateset-webhook-id', 'test-webhook-id')
.send(payload)
.expect(401);
});
it('should reject old timestamps', async () => {
const payload = JSON.stringify({ event: 'order.created', data: { id: '123' } });
const oldTimestamp = Math.floor(Date.now() / 1000) - 600; // 10 minutes ago
const signature = createValidSignature(payload, oldTimestamp);
await request(app)
.post('/webhooks/stateset')
.set('stateset-signature', signature)
.set('stateset-webhook-id', 'test-webhook-id')
.send(payload)
.expect(401);
});
it('should prevent replay attacks', async () => {
const payload = JSON.stringify({ event: 'order.created', data: { id: '123' } });
const timestamp = Math.floor(Date.now() / 1000);
const signature = createValidSignature(payload, timestamp);
const webhookId = 'unique-webhook-id';
// First request should succeed
await request(app)
.post('/webhooks/stateset')
.set('stateset-signature', signature)
.set('stateset-webhook-id', webhookId)
.send(payload)
.expect(200);
// Second request with same ID should fail
await request(app)
.post('/webhooks/stateset')
.set('stateset-signature', signature)
.set('stateset-webhook-id', webhookId)
.send(payload)
.expect(401);
});
});
describe('Rate Limiting', () => {
it('should rate limit excessive requests', async () => {
const payload = JSON.stringify({ event: 'test.event', data: {} });
const timestamp = Math.floor(Date.now() / 1000);
// Make many requests quickly
const requests = Array.from({ length: 150 }, (_, i) => {
const signature = createValidSignature(payload, timestamp);
return request(app)
.post('/webhooks/stateset')
.set('stateset-signature', signature)
.set('stateset-webhook-id', `webhook-${i}`)
.send(payload);
});
const responses = await Promise.allSettled(requests);
const rateLimited = responses.filter(r =>
r.status === 'fulfilled' && r.value.status === 429
);
expect(rateLimited.length).toBeGreaterThan(0);
});
});
describe('Input Validation', () => {
it('should reject oversized payloads', async () => {
const largePayload = 'x'.repeat(2 * 1024 * 1024); // 2MB
const timestamp = Math.floor(Date.now() / 1000);
const signature = createValidSignature(largePayload, timestamp);
await request(app)
.post('/webhooks/stateset')
.set('stateset-signature', signature)
.set('stateset-webhook-id', 'test-webhook-id')
.send(largePayload)
.expect(413); // Payload too large
});
it('should reject malformed JSON', async () => {
const invalidJson = '{ invalid json }';
const timestamp = Math.floor(Date.now() / 1000);
const signature = createValidSignature(invalidJson, timestamp);
await request(app)
.post('/webhooks/stateset')
.set('stateset-signature', signature)
.set('stateset-webhook-id', 'test-webhook-id')
.send(invalidJson)
.expect(400);
});
});
});
Best Practices Summary
Security Checklist
✅ Always verify webhook signatures using HMAC-SHA256 ✅ Use constant-time comparison to prevent timing attacks ✅ Implement timestamp validation to prevent replay attacks ✅ Store processed webhook IDs to prevent duplicates
✅ Use HTTPS for all webhook endpoints ✅ Implement IP allowlisting for Stateset IP ranges ✅ Use rate limiting to prevent abuse ✅ Validate request headers and content types
✅ Log security events for monitoring ✅ Return appropriate HTTP status codes ✅ Implement retry logic with exponential backoff ✅ Set reasonable timeouts for webhook processing
✅ Track failed verification attempts ✅ Alert on suspicious activity patterns ✅ Monitor processing times and error rates ✅ Log all webhook events with context
Production Deployment
# Environment variables for production
STATESET_WEBHOOK_SECRET=whsec_prod_9K2xL5pN...
WEBHOOK_TIMEOUT_MS=30000
MAX_WEBHOOK_BODY_SIZE=1048576
ENABLE_REPLAY_PROTECTION=true
REPLAY_TOLERANCE_SECONDS=300
# Security headers
ENABLE_HELMET=true
ENABLE_RATE_LIMITING=true
ALLOWED_IPS=52.89.214.238,54.187.174.169,54.187.205.235
# Monitoring
ENABLE_SECURITY_MONITORING=true
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/your/slack/webhook
ALERT_EMAIL=security@yourstore.com
# SSL/TLS
SSL_CERT_PATH=/path/to/ssl/cert.pem
SSL_KEY_PATH=/path/to/ssl/private-key.pem
Next Steps
API Testing Guide
Learn how to test your webhook implementations
Monitoring Guide
Set up comprehensive monitoring and alerting
Integration Patterns
Best practices for integrating with external systems
Performance Optimization
Optimize webhook processing performance
Conclusion
Implementing robust webhook security is critical for protecting your application and ensuring data integrity. This guide provides comprehensive security patterns including signature verification, replay protection, IP allowlisting, and monitoring.
Key takeaways:
- ✅ Always verify webhook signatures using HMAC-SHA256
- ✅ Implement replay attack prevention with timestamp validation
- ✅ Use HTTPS and IP allowlisting for network security
- ✅ Monitor security events and set up alerting
- ✅ Test your security implementation thoroughly
With these security measures in place, you can confidently process Stateset webhooks while maintaining the highest security standards.
- Webhook Security Guide
- Prerequisites
- Security Fundamentals
- Signature Verification
- Basic HMAC Verification
- Advanced Signature Verification
- Event Processing Security
- Secure Event Handler
- Complete Webhook Endpoint
- IP Allowlisting
- Stateset IP Ranges
- Monitoring and Alerting
- Security Event Monitoring
- Testing Webhook Security
- Security Test Suite
- Best Practices Summary
- Security Checklist
- Production Deployment
- Next Steps
- Conclusion