Skip to content

fix: replace self-keyed HMAC with AES-256-GCM for webhook secrets#2169

Open
Wachhund wants to merge 1 commit into
TheMorpheus407:masterfrom
Wachhund:fix/webhook-secret-encryption
Open

fix: replace self-keyed HMAC with AES-256-GCM for webhook secrets#2169
Wachhund wants to merge 1 commit into
TheMorpheus407:masterfrom
Wachhund:fix/webhook-secret-encryption

Conversation

@Wachhund

Copy link
Copy Markdown

Summary

Fixes a cryptographic flaw in webhook secret storage:

  • Self-keyed HMAC: hashSecret() used createHmac('sha256', secret).update(secret) where the key equals the input, making it a deterministic hash reversible by brute force on DB leak
  • Hash-as-signing-key: processDelivery() fell back to using the stored hash value itself as the webhook signing key when the in-memory cache was cold, exposing it to recipients
  • Fix: Replaced with AES-256-GCM encryption/decryption (same pattern as TOTP secret storage in totp.ts). Secrets are encrypted at rest and decrypted on demand for webhook signature generation

Test plan

  • New webhook subscriptions store encrypted secrets (format: salt:iv:authTag:ciphertext)
  • Webhook deliveries correctly sign payloads after server restart (cache cold → decrypt from DB)
  • Webhook signature verification still works for recipients
  • Existing webhook tests pass

The previous hashSecret() used createHmac('sha256', secret).update(secret)
where the HMAC key equals the input — degenerating to a reversible hash.
The processDelivery() fallback used the stored hash AS the signing key,
leaking it to webhook recipients.

Now encrypts webhook secrets with AES-256-GCM (same pattern as TOTP
secret storage) and decrypts on demand for signing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant