All Docs
FeaturesNurtureHubUpdated March 25, 2026

Security Advisory: SCR-10 — Webhook Signature Verification Bypass

Security Advisory: SCR-10 — Webhook Signature Verification Bypass

Version fixed: v1.0.88
Control: SCR-10
Category: data_leak
Severity: High


Overview

Prior to v1.0.88, three NurtureHub webhook endpoints would accept incoming HTTP payloads without performing signature verification when the relevant signing secret was not configured in the environment. This created a data injection risk: any actor capable of sending a POST request to one of these endpoints on a misconfigured deployment could inject fabricated events into the platform.

This advisory describes the affected endpoints, the nature of the risk, the fix applied, and the steps operators must take to ensure their deployments are protected.


Affected Endpoints

1. Resend Webhook — POST /api/webhooks/resend

Condition: RESEND_WEBHOOK_SECRET environment variable not set.
Previous behaviour: Request accepted; only a console.warn was emitted.
Risk: An attacker could POST fake email delivery, open, click, bounce, or complaint events, corrupting contact engagement scores and intent signals.

2. agentOS CRM Webhook — POST /api/webhooks/crm/agentOS

Condition: connection.webhookSecret is null for the relevant CRM connection record.
Previous behaviour: Request accepted with a warning log.
Risk: An attacker could inject fabricated CRM contact records or property events, polluting the contact database and triggering unintended nurture sequences.

3. Twilio SMS Webhook

Condition: TWILIO_AUTH_TOKEN environment variable not set.
Previous behaviour: Request accepted with no verification and no warning.
Risk: An attacker could inject fake SMS opt-out events, causing contacts to be incorrectly suppressed from SMS communications.


Root Cause

Each endpoint followed a pattern of treating a missing secret as a soft failure — logging a warning but continuing to process the payload. This is a well-known misconfiguration antipattern in webhook handler design. The correct pattern is to fail closed: if the secret required to verify a payload's authenticity is absent, the request must be rejected before any processing occurs.

The Calmony Pay webhook within the same codebase already implemented the correct pattern and served as the reference for the fix.


Fix Applied (v1.0.88)

All three endpoints have been updated to reject requests when the signing secret is not configured, returning an appropriate HTTP error rather than processing the payload:

HTTP 500 — Webhook secret not configured

Additionally, a startup-time environment variable validation check has been added. If any webhook signing secret is absent when the application starts, the application will log a warning (or optionally fail to start, depending on deployment configuration), making misconfiguration visible before it can be exploited.


Required Action for Operators

If you self-host or manage NurtureHub deployments, you must verify the following before or immediately after upgrading to v1.0.88.

Environment Variables

Ensure the following variables are set in every environment (development, staging, and production):

VariablePurpose
RESEND_WEBHOOK_SECRETSigns Resend email event webhook payloads
TWILIO_AUTH_TOKENUsed to verify Twilio SMS webhook requests

Where to find these values:

  • RESEND_WEBHOOK_SECRET: Generate or retrieve from your Resend dashboard under Webhooks.
  • TWILIO_AUTH_TOKEN: Available in your Twilio Console under Account Info.

agentOS CRM Connection Secrets

For every active agentOS CRM connection in the database, confirm that connection.webhookSecret is populated with a non-null value. Connections with a null secret will now cause the webhook endpoint to return 500 rather than accept payloads. To remediate:

  1. Navigate to Settings → CRM Connections in the NurtureHub admin panel.
  2. For any agentOS connection showing a missing webhook secret, regenerate or enter the secret provided by agentOS.
  3. Confirm the secret is saved and matches the value configured on the agentOS side.

Verifying the Fix

To confirm your deployment is correctly rejecting unauthenticated webhook requests, you can send a test POST with no signature header:

curl -X POST https://<your-domain>/api/webhooks/resend \
  -H "Content-Type: application/json" \
  -d '{"type": "email.delivered"}'

A correctly configured deployment will return:

HTTP 400 or 500

If you receive HTTP 200, the secret is not configured and the endpoint is still accepting unauthenticated payloads.


Timeline

DateEvent
Internal discoveryIdentified during supply chain security review (SCR-10)
Fix shippedv1.0.88

References

  • Affected file: src/app/api/webhooks/resend/route.ts
  • Reference implementation: Calmony Pay webhook handler
  • Security control: SCR-10