Skip to content

fix: use constant-time comparison for Shopify and HubSpot webhook signatures - #4449

Merged
steven-tey merged 6 commits into
mainfrom
cursor/webhook-timing-safe-comparison-32f4
Sep 3, 2026
Merged

fix: use constant-time comparison for Shopify and HubSpot webhook signatures#4449
steven-tey merged 6 commits into
mainfrom
cursor/webhook-timing-safe-comparison-32f4

Conversation

@devkiran

@devkiran devkiran commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Extracts constant-time comparison logic into a shared utility function and applies it consistently across all webhook signature verifications and OAuth token handlers to prevent timing side-channel attacks.

Changes

New Shared Utility

  • apps/web/lib/webhook/timing-safe-compare.ts: Created a reusable timingSafeCompare() function that:
    • Performs constant-time string comparison using crypto.timingSafeEqual
    • Includes length check before comparison
    • Handles null/undefined inputs safely
    • Well-documented with JSDoc for security-sensitive use cases

Updated Webhook Handlers

All webhook signature verifications now use the shared utility:

  • Shopify (apps/web/app/(ee)/api/shopify/integration/webhook/route.ts)
  • HubSpot (apps/web/app/(ee)/api/hubspot/webhook/route.ts)
  • Intercom (apps/web/app/(ee)/api/intercom/webhook/verify-webhook-signature.ts)
  • Slack (apps/web/lib/integrations/slack/verify-request.ts)
  • Dub (apps/web/app/api/dub/webhook/route.ts)

Updated OAuth Handlers

OAuth client secret comparisons now use constant-time comparison:

  • apps/web/app/api/oauth/token/exchange-code-for-token.ts
  • apps/web/app/api/oauth/token/refresh-access-token.ts

Security Impact

Mitigates timing side-channel attacks (CWE-208) by ensuring all security-sensitive string comparisons take constant time regardless of where the strings differ. While timing attacks against HMAC comparisons are difficult to exploit over the network due to jitter, this change:

  • Implements defense-in-depth across the entire codebase
  • Provides a single, well-tested utility for future use
  • Ensures consistency in security practices

Code Quality Benefits

  • DRY principle: Single source of truth for timing-safe comparisons
  • Maintainability: Future updates only need to modify one utility
  • Discoverability: Clear function name and documentation guide developers to use it
  • Type safety: TypeScript ensures correct usage

Fixes #4415

Slack Thread

Open in Web Open in Cursor 

Summary by CodeRabbit

  • Security
    • Improved protection for webhook signature verification across supported integrations, including HubSpot, Intercom, Shopify, Dub, Veriff, and Slack.
    • Strengthened OAuth client-secret validation during authorization-code exchanges and access-token refreshes.
    • Improved unsubscribe-token verification.
    • Verification now uses timing-resistant comparisons to reduce exposure to timing-based attacks.

…natures

- Replace plain !== with crypto.timingSafeEqual in both handlers
- Add length check before comparison (same pattern as Intercom handler)
- Mitigates timing side-channel attacks (CWE-208)

Fixes #4415

Co-authored-by: Kiran K <kiran@dub.co>
@vercel

vercel Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated
dub Ready Ready Preview Sep 3, 2026 8:45pm UTC

Request Review

@CLAassistant

CLAassistant commented Sep 3, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
2 out of 3 committers have signed the CLA.

✅ steven-tey
✅ devkiran
❌ cursoragent
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: 81e327ce-9be3-4db2-8188-7a6fa5d530a7

📥 Commits

Reviewing files that changed from the base of the PR and between f816275 and 942bd2c.

📒 Files selected for processing (3)
  • apps/web/app/(ee)/api/shopify/integration/webhook/route.ts
  • apps/web/app/api/dub/webhook/route.ts
  • apps/web/app/api/veriff/webhook/route.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The change adds a shared constant-time comparison helper. Webhook handlers, OAuth token endpoints, and unsubscribe-token verification now use it for secret and signature checks. Shopify and Dub webhook handlers also use withAxiom.

Changes

Constant-time verification

Layer / File(s) Summary
Shared comparison helper
apps/web/lib/webhook/timing-safe-compare.ts
Adds timingSafeCompare with falsy-input handling, UTF-8 length checks, and crypto.timingSafeEqual.
Webhook signature verification
apps/web/app/(ee)/api/hubspot/webhook/route.ts, apps/web/app/(ee)/api/intercom/webhook/verify-webhook-signature.ts, apps/web/app/(ee)/api/shopify/integration/webhook/route.ts, apps/web/app/api/dub/webhook/route.ts, apps/web/lib/integrations/slack/verify-request.ts, apps/web/app/api/veriff/webhook/route.ts
Replaces direct or inline signature comparisons with the shared helper. Veriff uses it for both the API key and HMAC signature. Shopify and Dub wrap their POST handlers with withAxiom.
Unsubscribe-token verification
apps/web/lib/email/unsubscribe-token.ts
Uses the shared helper for unsubscribe-token signature verification.
OAuth client-secret verification
apps/web/app/api/oauth/token/exchange-code-for-token.ts, apps/web/app/api/oauth/token/refresh-access-token.ts
Compares hashed client secrets with the shared helper.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 942bd

Webhook and secret comparisons now use a shared timing-safe helper, preserving invalid-signature rejection behavior while reducing timing side-channel exposure. No merge-blocking risk is identified.

Suggested reviewers: pepeladeira

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The pull request includes changes beyond issue #4415, including OAuth, Slack, Dub, Veriff, Intercom, unsubscribe-token verification, and unrelated withAxiom instrumentation. Remove the unrelated changes or link issues that explicitly require them. Keep this pull request focused on Shopify and HubSpot signature verification.
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 3 functions across 10 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary change requested by issue #4415: constant-time comparison for Shopify and HubSpot webhook signatures.
Linked Issues check ✅ Passed The Shopify and HubSpot webhook handlers now use the shared timing-safe comparison utility. The implementation addresses the constant-time comparison and length-safety requirements in issue #4415.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/webhook-timing-safe-comparison-32f4

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

- Created timingSafeCompare utility in lib/webhook/timing-safe-compare.ts
- Updated all webhook signature verifications to use the shared utility:
  - Shopify webhook handler
  - HubSpot webhook handler
  - Intercom webhook handler
  - Slack signature verification
  - Dub webhook handler
- Updated OAuth token handlers to use constant-time comparison:
  - exchange-code-for-token (client_secret verification)
  - refresh-access-token (client_secret verification)

This ensures consistent constant-time comparison across the codebase
and prevents timing side-channel attacks (CWE-208).

Co-authored-by: Kiran K <kiran@dub.co>
@devkiran
devkiran marked this pull request as ready for review September 3, 2026 17:01
@steven-tey

Copy link
Copy Markdown
Collaborator

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@steven-tey
steven-tey merged commit 6884316 into main Sep 3, 2026
10 of 13 checks passed
@steven-tey
steven-tey deleted the cursor/webhook-timing-safe-comparison-32f4 branch September 3, 2026 23:57
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.

Use constant-time comparison for Shopify and HubSpot webhook signatures

4 participants