Verify a webhook signature against the exact raw body and a bounded timestamp before processing the event.
The examples introduce the underlying control before showing a verification and an application-level safeguard.
What You Will Build
Verify a webhook signature against the exact raw body and a bounded timestamp before processing the event.
Prerequisites
Use PHP 8.4 or newer, HTTPS for all authentication operations, and an application session or database where required. Example configuration values are placeholders, never production secrets.
Example 1: Basic Operation
Compare an HMAC against the exact raw request bytes.
<?php
$secret = getenv('WEBHOOK_HMAC_KEY');
$rawBody = file_get_contents('php://input');
$received = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
if (!is_string($secret) || $secret === '' || !is_string($received)) { http_response_code(401); exit; }
$expected = hash_hmac('sha256', $rawBody, $secret);
if (!hash_equals($expected, $received)) { http_response_code(401); exit; }
Example 2: A Different Use Case
Reject stale signed webhook timestamps.
<?php
$timestamp = $_SERVER['HTTP_X_WEBHOOK_TIMESTAMP'] ?? '';
if (!is_string($timestamp) || !ctype_digit($timestamp) || abs(time() - (int) $timestamp) > 300) {
http_response_code(401); exit;
}
// Include the timestamp in the provider-defined signed payload, not as an unsigned check.
Practical Example: Checking the Result
Use an atomic uniqueness constraint to prevent concurrent replays.
<?php
$eventId = $_SERVER['HTTP_X_EVENT_ID'] ?? '';
if (!is_string($eventId) || !preg_match('/^[A-Za-z0-9_-]{8,128}$/D', $eventId)) { http_response_code(400); exit; }
// Atomically insert $eventId into a UNIQUE-indexed table before processing.
// A duplicate-key conflict indicates a replay and must not trigger side effects.
Important Behavior and Edge Cases
Never reserialize JSON before checking an HMAC, and persist event IDs or nonces atomically to block replay across workers.
Enforce controls on the server, test denial paths and do not assume that a snippet alone implements an entire production workflow.
Comparison with Related Functions
| Approach | Key distinction |
|---|---|
| PHP Webhook Signature Verification and Replay Protection | Verify a webhook signature against the exact raw body and a bounded timestamp before processing the event. |
| Related technique | A signature establishes message authenticity; it does not provide idempotent business processing by itself. |
Common Beginner Mistakes
| Mistake | Correct approach |
|---|---|
| Confusing this operation with a neighboring cryptographic or security mechanism | A signature establishes message authenticity; it does not provide idempotent business processing by itself. |
| Ignoring a security prerequisite or failure case | Never reserialize JSON before checking an HMAC, and persist event IDs or nonces atomically to block replay across workers. |
Key Takeaways
- Verify a webhook signature against the exact raw body and a bounded timestamp before processing the event.
- Never reserialize JSON before checking an HMAC, and persist event IDs or nonces atomically to block replay across workers.
- A signature establishes message authenticity; it does not provide idempotent business processing by itself.
Apply this behavior to a real application only after confirming the required extension, configuration and failure handling on the deployment runtime.