php 37 lines · 7 steps

Verifying webhook signatures in Laravel

A middleware that rejects forged or replayed webhooks before they reach your app.

Explained by highlit
1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Http\Request;
7use Symfony\Component\HttpFoundation\Response;
8 
9class VerifyWebhookSignature
10{
11 public function __construct(private readonly string $secret)
12 {
13 }
14 
15 public function handle(Request $request, Closure $next): Response
16 {
17 $signature = $request->header('X-Webhook-Signature', '');
18 $timestamp = $request->header('X-Webhook-Timestamp', '');
19 
20 if ($signature === '' || $timestamp === '') {
21 abort(400, 'Missing signature headers.');
22 }
23 
24 if (abs(time() - (int) $timestamp) > 300) {
25 abort(408, 'Signature timestamp expired.');
26 }
27 
28 $payload = $timestamp . '.' . $request->getContent();
29 $expected = hash_hmac('sha256', $payload, $this->secret);
30 
31 if (! hash_equals($expected, $signature)) {
32 abort(401, 'Invalid webhook signature.');
33 }
34 
35 return $next($request);
36 }
37}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Signing the timestamp alongside the payload lets you reject stale requests and stop replay attacks.
  2. 2Always compare HMACs with a constant-time function like hash_equals to avoid timing leaks.
  3. 3Middleware is the right place to enforce authenticity so route handlers only ever see trusted requests.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Verifying webhook signatures in Laravel — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code