Orange Collar Media
Orange Collar Media
DOCS
OCMLABS/MODULE-WEBHOOK · SECURITY & VERIFICATION

Verify webhook signatures on your receiver, and what the module does to stay safe

MAGENTO 2.4.7 - 2.4.8 PHP 8.2 - 8.4 LUMA + HYVÄ COMPOSER

Verifying the signature on your receiver

Every delivery from a webhook that has a secret configured includes:

X-Webhook-Signature: sha256=<hex-encoded HMAC-SHA256 of the raw request body>

The signature is computed over the exact bytes sent on the wire, using the webhook’s secret. It is omitted entirely when no secret is set - never sent empty or fake. On the receiving end:

  1. Read the raw request body before any JSON parsing or middleware that might re-serialize it.
  2. Compute the HMAC and prefix it with sha256=. In PHP:
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $sharedSecret);
  1. Compare it to the received header with a constant-time comparison - PHP’s hash_equals(), not == - to avoid leaking timing information about the secret.
if (!hash_equals($expected, $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '')) {
http_response_code(401);
exit;
}

Dedupe on X-Webhook-Delivery-Id after the signature checks out: delivery is at-least-once, so the same delivery ID can legitimately arrive more than once (retries after a timeout your endpoint actually served, for example).

Other tracing headers sent with every delivery: X-Webhook-Event (the event name), X-Webhook-Id (the subscription ID), X-Webhook-Attempt (attempt number), and X-Webhook-Test: 1 on Send Test deliveries only.

Per-webhook authentication

Independent of the signature, each subscription has an authentication type:

  • None (default)
  • Bearer - sends Authorization: Bearer <token>
  • Basic - sends Authorization: Basic <base64(username:password)>

Bearer tokens, Basic passwords, and HMAC secrets are all encrypted at rest with Magento’s EncryptorInterface. The admin form uses leave-blank-to-keep semantics, so re-saving a webhook never round-trips a plaintext credential back into an HTML form field. Authentication is applied identically on first attempts, cron retries, and Send Test.

SSRF guard

Before a request is sent (and again at save time, for immediate feedback), the destination URL is validated:

  • Scheme must be http or https
  • http:// URLs are rejected unless the development-only “Allow Insecure URL” flag is on
  • The host is resolved and rejected if it falls in a private, reserved, loopback, or link-local range, unless the development-only “Allow Private Networks” flag is on

Both flags default to No. Leaving them off in production is what makes the guard meaningful.

The template sandbox

Admin-authored payload templates run in a locked-down Smarty 5 instance: no static class access, no PHP constants, no superglobals, and a strict modifier allowlist. Templates compile from database-stored strings and are never written to disk. A template using anything outside the sandbox fails validation at save time and will never execute at runtime. Template authorship is gated behind the OCMLabs_Webhook::manage ACL resource, so template code is only ever as trusted as your admin role assignments - which is exactly the boundary Magento admins already reason about.