Webhook Notification Service - Security and Privacy
Authentication and Authorization
Webhook Registration Authentication
Methods:
1. API Keys:
POST /api/v1/webhooks
Authorization: Bearer sk_live_abc123def456
Pros: Simple, stateless
Cons: Key theft risk
2. OAuth 2.0:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Pros: Standard, secure, token expiration
Cons: More complex
3. Service Accounts:
- Machine-to-machine authentication
- Certificate-based
- No passwords
Recommendation: OAuth 2.0 for users, service accounts for systemsWebhook Delivery Authentication
Customer Endpoint Authentication:
1. HMAC Signature (Recommended):
X-Signature: sha256=abc123def456...
Verification:
expected = HMAC-SHA256(secret, payload)
valid = compare_digest(expected, received_signature)
2. Bearer Token:
Authorization: Bearer customer_token_xyz
3. Basic Auth:
Authorization: Basic base64(username:password)
4. mTLS:
- Client certificate authentication
- Highest security
- Complex setup
Recommendation: HMAC signature for simplicity and securitySignature Generation and Verification
HMAC-SHA256 Implementation
Signature Generation (Service Side):
import hmac
import hashlib
def generate_signature(payload, secret):
signature = hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
return f"sha256={signature}"
# Add to webhook request
headers = {
'X-Signature': generate_signature(payload, webhook.secret),
'X-Webhook-ID': webhook.id,
'X-Event-ID': event.id
}
Signature Verification (Customer Side):
def verify_signature(payload, signature, secret):
expected = generate_signature(payload, secret)
return hmac.compare_digest(expected, signature)
# Verify in webhook handler
@app.route('/webhooks', methods=['POST'])
def handle_webhook():
payload = request.get_data(as_text=True)
signature = request.headers.get('X-Signature')
secret = get_webhook_secret()
if not verify_signature(payload, signature, secret):
return 'Invalid signature', 401
process_webhook(request.json)
return 'OK', 200
Security Best Practices:
- Use constant-time comparison (compare_digest)
- Include timestamp in signature (prevent replay)
- Rotate secrets regularly
- Use strong secrets (32+ characters)Replay Attack Prevention
Timestamp-Based Protection:
{
"event_id": "evt_123",
"timestamp": 1704708000,
"data": {...}
}
signature = HMAC-SHA256(secret, payload_with_timestamp)
Verification:
def verify_webhook(payload, signature, secret):
# Verify signature
if not verify_signature(payload, signature, secret):
return False
# Check timestamp (reject if >5 minutes old)
event_time = payload['timestamp']
if abs(now() - event_time) > 300:
return False
return True
Benefits:
- Prevents replay attacks
- Time-bound validity
- Simple implementationSecret Management
Secure Secret Storage
Secret Generation:
import secrets
def generate_webhook_secret():
return 'whsec_' + secrets.token_urlsafe(32)
Secret Storage:
- Encrypt at rest (AES-256)
- Store in secrets manager (Vault, AWS Secrets Manager)
- Never log secrets
- Mask in API responses
Implementation:
class SecretManager {
String storeSecret(String webhookId, String secret) {
// Encrypt secret
String encrypted = encrypt(secret, masterKey);
// Store in database
database.execute(
"UPDATE webhooks SET secret = ? WHERE webhook_id = ?",
encrypted, webhookId
);
// Cache encrypted secret
redis.setex("secret:" + webhookId, 3600, encrypted);
return webhookId;
}
String getSecret(String webhookId) {
// Try cache first
String encrypted = redis.get("secret:" + webhookId);
if (encrypted == null) {
// Load from database
encrypted = database.query(
"SELECT secret FROM webhooks WHERE webhook_id = ?",
webhookId
);
redis.setex("secret:" + webhookId, 3600, encrypted);
}
// Decrypt and return
return decrypt(encrypted, masterKey);
}
}
Secret Rotation:
- Rotate every 90 days
- Support dual secrets during rotation
- Notify customers before rotation
- Automatic rotation processNetwork Security
TLS/SSL Enforcement
Requirements:
- Enforce HTTPS for all webhooks
- Reject HTTP endpoints
- Validate SSL certificates
- Support custom CA certificates
Implementation:
class WebhookValidator {
boolean isValidURL(String url) {
// Must be HTTPS
if (!url.startsWith("https://")) {
throw new InvalidURLException("HTTPS required");
}
// Validate SSL certificate
try {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, null);
URL endpoint = new URL(url);
HttpsURLConnection conn = (HttpsURLConnection) endpoint.openConnection();
conn.connect();
conn.disconnect();
return true;
} catch (SSLException e) {
throw new InvalidCertificateException("Invalid SSL certificate");
}
}
}
Certificate Validation:
- Verify certificate chain
- Check expiration date
- Validate hostname
- Check revocation status (OCSP)IP Allowlisting
Configuration:
{
"webhook_id": "wh_123",
"ip_allowlist": [
"203.0.113.0/24",
"198.51.100.42"
]
}
Implementation:
class IPFilter {
boolean isAllowed(String webhookId, String sourceIP) {
List<String> allowlist = getIPAllowlist(webhookId);
if (allowlist.isEmpty()) {
return true; // No restrictions
}
for (String cidr : allowlist) {
if (ipInRange(sourceIP, cidr)) {
return true;
}
}
return false;
}
}
Benefits:
- Additional security layer
- Prevent unauthorized access
- Compliance requirementsData Privacy
PII in Webhook Payloads
Problem:
Webhook payload may contain PII:
- User emails
- Phone numbers
- Addresses
- Payment details
Solutions:
1. Tokenization:
{
"event_type": "user.created",
"data": {
"user_id": 123,
"email": "token:email:abc123", // Tokenized
"phone": "token:phone:def456" // Tokenized
}
}
2. Encryption:
{
"event_type": "payment.success",
"data": {
"payment_id": "pay_123",
"card_number": "encrypted:AES256:...",
"cvv": "encrypted:AES256:..."
}
}
3. Minimal Data:
{
"event_type": "user.created",
"data": {
"user_id": 123,
"profile_url": "https://api.example.com/users/123"
}
}
// Customer fetches full data via API
4. Field-Level Permissions:
{
"webhook_id": "wh_123",
"allowed_fields": ["user_id", "email"],
"denied_fields": ["ssn", "credit_card"]
}
Recommendation: Minimal data + API referencesGDPR Compliance
Requirements:
1. Right to Access:
- Export user's webhook data
- Include all deliveries
2. Right to Erasure:
- Delete user's webhooks
- Anonymize delivery logs
3. Data Minimization:
- Store only necessary data
- Minimize retention period
4. Consent Management:
- Track user consent
- Respect opt-outs
Implementation:
class GDPRService {
void exportUserData(String userId) {
// Export webhooks
List<Webhook> webhooks = getWebhooks(userId);
// Export deliveries
List<Delivery> deliveries = getDeliveries(userId);
// Export as JSON
return JSON.stringify({
webhooks: webhooks,
deliveries: deliveries
});
}
void deleteUserData(String userId) {
// Delete webhooks
deleteWebhooks(userId);
// Anonymize deliveries
anonymizeDeliveries(userId);
// Audit log
auditLog("user_data_deleted", userId);
}
}Audit Logging
Comprehensive Audit Trail
Events to Log:
- Webhook creation/deletion
- Webhook updates
- Delivery attempts
- Delivery failures
- Configuration changes
- Authentication attempts
- Rate limit violations
Log Format:
{
"timestamp": "2024-01-08T10:00:00Z",
"event_type": "webhook_delivery",
"webhook_id": "wh_abc123",
"delivery_id": "del_xyz789",
"user_id": "user_123",
"endpoint_url": "https://customer.example.com/webhooks",
"status": "success",
"response_status": 200,
"response_time_ms": 250,
"attempt_number": 1,
"ip_address": "203.0.113.42"
}
Storage:
- Immutable append-only log
- Long retention (1 year+)
- Indexed for fast queries
- Exported to SIEM
Compliance:
- SOC 2 Type II
- ISO 27001
- PCI DSS (for payment webhooks)Abuse Prevention
Rate Limiting and Throttling
API Rate Limits:
- Free tier: 1,000 webhooks/day
- Pro tier: 100,000 webhooks/day
- Enterprise: Unlimited
Webhook Limits:
- Max 100 webhooks per user
- Max 50 event subscriptions per webhook
- Max 10 MB payload size
- Max 30 second timeout
Implementation:
class AbuseDetector {
void detectAbuse(String userId) {
// Check webhook creation rate
int webhooksToday = getWebhookCount(userId, today());
if (webhooksToday > 100) {
alertSecurityTeam("Excessive webhook creation", userId);
throttleUser(userId);
}
// Check delivery rate
int deliveriesToday = getDeliveryCount(userId, today());
if (deliveriesToday > 1000000) {
alertSecurityTeam("Excessive deliveries", userId);
throttleUser(userId);
}
// Check failure rate
double failureRate = getFailureRate(userId);
if (failureRate > 0.5) {
alertUser(userId, "High webhook failure rate");
}
}
}DDoS Protection
Mitigation Strategies:
1. Rate Limiting:
- Limit webhook registrations
- Limit event publishing
- Throttle excessive traffic
2. IP Reputation:
- Block known malicious IPs
- Challenge suspicious IPs
- Allowlist trusted IPs
3. Payload Validation:
- Max payload size: 10 MB
- Validate JSON structure
- Sanitize inputs
4. Resource Limits:
- Max concurrent deliveries per user
- Max queue depth per user
- Automatic throttlingIncident Response
Security Incident Procedures
1. Detection:
- Monitor audit logs
- Alert on suspicious activity
- Automated threat detection
2. Containment:
- Disable compromised webhooks
- Block malicious IPs
- Revoke compromised secrets
3. Investigation:
- Analyze audit logs
- Identify attack vector
- Assess damage
4. Recovery:
- Rotate all secrets
- Restore from backups
- Apply security patches
5. Post-Mortem:
- Document incident
- Update procedures
- Implement preventive measures
Runbook:
# Disable compromised webhook
UPDATE webhooks SET enabled = FALSE WHERE webhook_id = 'wh_123';
# Rotate secret
UPDATE webhooks SET secret = generate_secret() WHERE webhook_id = 'wh_123';
# Block malicious IP
iptables -A INPUT -s 198.51.100.42 -j DROPThis comprehensive security approach ensures the webhook service is protected against threats while maintaining compliance with privacy regulations.