mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
The webhook signature verifier used to fall open with a warning log
when META_APP_SECRET was unset — a deliberate soft-rollout choice that
is unsafe for a public template. A forker who forgets the env var
would be running a fully spoofable webhook: anyone knowing the URL
could POST fabricated status updates and drift broadcast counts.
Now:
- `verifyMetaWebhookSignature` returns false when the secret is missing
and logs at error level, so the route responds 401 to every POST
until the operator configures it.
- Docs mark META_APP_SECRET as **required**:
- docs/environment-variables.md lists it under Required, not
Recommended.
- docs/whatsapp-setup.md §6 is now "Signature verification (required)".
- docs/troubleshooting.md updated accordingly.
- .env.local.example lists the var with guidance on where to find it.
Also fixes a pre-existing gitignore bug: `.env*` was swallowing
`.env.local.example`, which meant forkers running
`cp .env.local.example .env.local` from the getting-started guide
hit "no such file". Added a negation for `*.example`.
Upgrade note for existing deploys: set META_APP_SECRET (Meta → App
Settings → Basic → App Secret) before updating, or inbound webhooks
will 401 until it's configured.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
48 lines
1.6 KiB
TypeScript
48 lines
1.6 KiB
TypeScript
import crypto from 'node:crypto'
|
|
|
|
/**
|
|
* Verify the HMAC-SHA256 signature Meta attaches to webhook POSTs.
|
|
*
|
|
* Meta signs the raw request body with your App Secret and sends the
|
|
* result in the `x-hub-signature-256: sha256=<hex>` header. Without
|
|
* verification, anyone who knows our webhook URL can POST fabricated
|
|
* status updates and drift broadcast counts arbitrarily.
|
|
*
|
|
* Reference:
|
|
* https://developers.facebook.com/docs/graph-api/webhooks/getting-started#verify-payloads
|
|
*
|
|
* Contract:
|
|
* `META_APP_SECRET` is **required**. If it's missing we fail closed —
|
|
* every request is rejected until the operator configures the
|
|
* secret. A previous version fell open with a warning log, which is
|
|
* unsafe for a public template: anyone who forgets the env var would
|
|
* be running a fully spoofable webhook.
|
|
*/
|
|
export function verifyMetaWebhookSignature(
|
|
rawBody: string,
|
|
signatureHeader: string | null,
|
|
): boolean {
|
|
const secret = process.env.META_APP_SECRET
|
|
if (!secret) {
|
|
console.error(
|
|
'[webhook] META_APP_SECRET is not set — rejecting request. ' +
|
|
'Configure the env var (Meta → App Settings → Basic → App Secret) ' +
|
|
'to enable signature verification.',
|
|
)
|
|
return false
|
|
}
|
|
|
|
if (!signatureHeader) return false
|
|
if (!signatureHeader.startsWith('sha256=')) return false
|
|
|
|
const expected =
|
|
'sha256=' +
|
|
crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
|
|
|
|
const a = Buffer.from(signatureHeader)
|
|
const b = Buffer.from(expected)
|
|
// Bail if lengths differ — timingSafeEqual throws otherwise.
|
|
if (a.length !== b.length) return false
|
|
return crypto.timingSafeEqual(a, b)
|
|
}
|