Skip to content

fix(security): add webhook signature verification for Webflow, HubSpot, and Airtable#4612

Open
waleedlatif1 wants to merge 5 commits into
stagingfrom
fix/webhook-signature-verification
Open

fix(security): add webhook signature verification for Webflow, HubSpot, and Airtable#4612
waleedlatif1 wants to merge 5 commits into
stagingfrom
fix/webhook-signature-verification

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

@waleedlatif1 waleedlatif1 commented May 15, 2026

Summary

Three webhook handlers were accepting any POST to their endpoint without verifying the provider's cryptographic signature, enabling spoofed events to trigger arbitrary workflow executions.

  • Webflow: Capture secretToken from subscription creation response (previously discarded) and verify X-Webflow-Signature using HMAC-SHA256
  • HubSpot: Verify X-HubSpot-Signature using SHA256(clientSecret + rawBody) — the clientSecret was already collected in providerConfig but never used for verification
  • Airtable: Capture macSecretBase64 from subscription creation response (previously discarded) and verify X-Airtable-Content-Mac using HMAC-SHA256 with base64-decoded key

All three use timing-safe comparison via safeCompare and follow the existing verifyAuth provider pattern. If the secret is absent (e.g. a pre-existing webhook subscription that predates this fix), verification is skipped (return null) to avoid breaking existing integrations.

Test plan

  • Webflow: create a new trigger and send a webhook with an invalid signature — should receive 401
  • Webflow: send a webhook with correct HMAC-SHA256 signature — should process normally
  • HubSpot: send a webhook without X-HubSpot-Signature — should receive 401
  • HubSpot: send with SHA256(clientSecret + body) — should process normally
  • Airtable: verify macSecretBase64 is persisted after trigger creation
  • Airtable: send a webhook with invalid X-Airtable-Content-Mac — should receive 401

…t, and Airtable

All three handlers accepted any POST to the webhook path without verifying
the provider's signature, enabling spoofed events to trigger arbitrary
workflow executions.

Webflow:
- Capture `secretToken` from the subscription creation response and persist
  it as `webhookSecret` in providerConfigUpdates (previously discarded)
- Add `verifyAuth` using HMAC-SHA256(rawBody, webhookSecret), comparing
  against `X-Webflow-Signature`

HubSpot:
- Add `verifyAuth` using SHA256(clientSecret + rawBody) → hex, comparing
  against `X-HubSpot-Signature` (v1 scheme; `clientSecret` was already
  collected in providerConfig but never used for verification)

Airtable:
- Capture `macSecretBase64` from the subscription creation response and
  persist it as `webhookSecret` (previously discarded)
- Add `verifyAuth` using HMAC-SHA256(rawBody, base64decode(webhookSecret))
  → base64, comparing against `X-Airtable-Content-Mac: hmac-sha256=<hash>`

All implementations use timing-safe comparison via `safeCompare` and return
null on success / 401 on failure, matching the existing provider pattern.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped May 15, 2026 2:45am

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 15, 2026

PR Summary

Medium Risk
Adds cryptographic verification (and timestamp replay protection for HubSpot/Webflow) to inbound webhook handling, which could block legitimate events if headers/URL canonicalization differ from provider expectations. Fallback behavior skips verification when secrets are missing to avoid breaking existing subscriptions, reducing rollout risk.

Overview
Hardens inbound webhooks for Airtable, HubSpot, and Webflow by adding verifyAuth checks that reject unsigned/invalid requests with 401 responses.

Airtable and Webflow now persist provider-issued signing secrets during subscription creation (macSecretBase64 / secretKey) and use them to validate HMAC signatures (with timing-safe safeCompare). HubSpot now verifies v1/v2 SHA-256 signatures and v3 HMAC signatures, including a 5-minute timestamp window to mitigate replay attacks.

Reviewed by Cursor Bugbot for commit c7cd4ae. Configure here.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 15, 2026

Greptile Summary

This PR adds cryptographic webhook signature verification to three previously unprotected providers — Webflow, HubSpot, and Airtable — following the existing verifyAuth handler pattern.

  • Webflow: Captures secretKey from the creation-response, stores it as webhookSecret, and validates X-Webflow-Signature using HMAC-SHA256(secret, timestamp:rawBody) with a 5-minute replay window and an isNaN guard on the timestamp.
  • HubSpot: Dispatches across all three signature versions (v1: SHA-256, v2: SHA-256 with URL, v3: HMAC with replay protection), keyed on the presence of X-HubSpot-Signature-v3 and the value of X-HubSpot-Signature-Version. All paths use timing-safe comparison via safeCompare.
  • Airtable: Captures macSecretBase64 from the creation response, base64-decodes it as the HMAC key, and verifies X-Airtable-Content-Mac (hmac-sha256=<hex>) using hmacSha256Hex. All three providers emit a logger.warn and return null (skip) when the stored secret is absent, preserving backward compatibility with pre-existing subscriptions.

Confidence Score: 5/5

All three verification paths are correctly implemented against each provider's documented algorithm; no logic errors or missing guards found.

The HMAC algorithms, key decoding, timing-safe comparisons, replay-protection windows, and isNaN guards are all correct. Backward compatibility (skip when secret absent + warn) is handled consistently across all three providers. The Airtable MAC format and pyAirtable reference implementation confirm hmacSha256Hex is the right function to use.

No files require special attention.

Important Files Changed

Filename Overview
apps/sim/lib/webhooks/providers/webflow.ts Adds verifyAuth with HMAC-SHA256 signature verification, 5-minute replay protection, isNaN timestamp guard, and captures secretKey from creation response into webhookSecret.
apps/sim/lib/webhooks/providers/hubspot.ts Adds full v1/v2/v3 HubSpot signature verification: v3 detected by X-HubSpot-Signature-v3 header with replay protection, v1/v2 dispatched via X-HubSpot-Signature-Version; all paths use safeCompare.
apps/sim/lib/webhooks/providers/airtable.ts Adds verifyAuth using HMAC-SHA256 with base64-decoded macSecretBase64 key (hex comparison against hmac-sha256= header prefix), and captures macSecretBase64 from subscription creation response.

Reviews (5): Last reviewed commit: "fix(security): guard NaN timestamp befor..." | Re-trigger Greptile

Comment thread apps/sim/lib/webhooks/providers/hubspot.ts Outdated
Comment thread apps/sim/lib/webhooks/providers/webflow.ts
Comment thread apps/sim/lib/webhooks/providers/webflow.ts
Comment thread apps/sim/lib/webhooks/providers/webflow.ts
…irtable

- Webflow: sign `${timestamp}:${rawBody}` per official docs (was rawBody only),
  capture `secretKey` not `secretToken` from create response, add 5-minute
  replay protection via X-Webflow-Timestamp
- Airtable: use hmacSha256Hex for hmac-sha256=<hex> format (was Base64)
…t absent

- HubSpot: dispatch on X-HubSpot-Signature-Version (v1/v2/v3); v1 keeps
  existing SHA-256(secret+body) logic, v2 adds httpMethod+url to hash,
  v3 uses HMAC-SHA256 with timestamp replay protection (5 min window)
- Webflow/Airtable/HubSpot: log warn when webhookSecret absent so operators
  can identify webhooks that still need re-registration
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/webhooks/providers/hubspot.ts Outdated
Comment thread apps/sim/lib/webhooks/providers/hubspot.ts
Comment thread apps/sim/lib/webhooks/providers/webflow.ts
Comment thread apps/sim/lib/webhooks/providers/hubspot.ts Outdated
- Detect v3 by presence of X-HubSpot-Signature-v3 header (HubSpot does
  not send X-HubSpot-Signature-Version for v3 requests)
- Use X-HubSpot-Request-Timestamp for replay protection (not the
  non-existent X-HubSpot-Signature-Timestamp header)
- Simplify v1/v2 path: both use X-HubSpot-Signature, read once before
  the version branch
Number(timestamp) returns NaN for non-numeric strings; Math.abs(Date.now() - NaN)
is NaN which is never > FIVE_MINUTES_MS, silently bypassing replay protection.
Add isNaN guard in both Webflow and HubSpot v3 timestamp checks.
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/webhooks/providers/webflow.ts
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/webhooks/providers/webflow.ts
Comment thread apps/sim/lib/webhooks/providers/webflow.ts
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit c7cd4ae. Configure here.

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