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