🔧 ProgrammierungBolt.new launches Forge to widen who gets to build with AI(17.09.2026 um 22:12 Uhr)
🔧 ProgrammierungGlobal Workspace Theory The J-Space of Claude(17.09.2026 um 22:12 Uhr)
🔧 AI Nachrichten I let a local 27B LLM audit and fix my Splunk + Sysmon stack(17.09.2026 um 22:15 Uhr)
🔧 ProgrammierungHow to Run Docker/Containers With Termux(17.09.2026 um 22:20 Uhr)
🔧 ProgrammierungI Accidentally Built a Dark Software Factory. Here's How.(17.09.2026 um 22:21 Uhr)
🔧 ProgrammierungCongrats to the DEV Weekend Challenge: Dog Days Edition Winners!(17.09.2026 um 22:22 Uhr)
🔧 ProgrammierungBolt.new launches Forge to widen who gets to build with AI(17.09.2026 um 22:12 Uhr)
🔧 ProgrammierungGlobal Workspace Theory The J-Space of Claude(17.09.2026 um 22:12 Uhr)
🔧 AI Nachrichten I let a local 27B LLM audit and fix my Splunk + Sysmon stack(17.09.2026 um 22:15 Uhr)
🔧 ProgrammierungHow to Run Docker/Containers With Termux(17.09.2026 um 22:20 Uhr)
🔧 ProgrammierungI Accidentally Built a Dark Software Factory. Here's How.(17.09.2026 um 22:21 Uhr)
🔧 ProgrammierungCongrats to the DEV Weekend Challenge: Dog Days Edition Winners!(17.09.2026 um 22:22 Uhr)
🔧 Programmierung 🕛 vor 1 Monat 4 Min Lesezeit
0

Signing webhook payloads in Laravel - and the mistake that breaks it silently

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

Hi, I'm Boris a PHP developer whose deepest experience is TYPO3, with Laravel and Filament being a newer, growing focus. A lot of that work across both has been building admin tooling and integrations that quietly need to just work: payments, webhooks, notifications. This post is about one small piece of that: signing webhook payloads correctly, and a mistake that's easy to make even once you already know HMAC signing in theory.






The problem



If your app sends webhooks to another service - or receives them - you eventually need the receiver to be able to trust that a payload really came from you and wasn't tampered with in transit. The standard answer is HMAC signing: hash the payload with a shared secret, send the hash alongside the request, and have the receiver recompute it and compare.



Sounds simple. It is simple until you sign the wrong bytes.






Signing correctly



The basic recipe:




CODE
$body = json_encode($payload);
$signature = hash_hmac('sha256', $body, $secret);






Send $signature in a header (e.g. X-Signature), and the receiver recomputes the HMAC over the body it received and compares. That part almost everyone gets right.






The gotcha: sign the exact bytes that go over the wire



Here's the version that looks correct but isn't:




CODE
// Looks fine, but signs and sends two different serializations
$payload = ['event' => 'order.created', 'total' => 42.00];
$signature = hash_hmac('sha256', json_encode($payload), $secret);

Http::withHeaders(['X-Signature' => $signature])
->post($url, $payload); // this re-serializes $payload internally






The bug: you called json_encode($payload) once, by hand, to compute the signature - and then handed the array to Laravel's HTTP client, which JSON-encodes it again, internally, when it builds the actual request body. That's two separate serialization passes. Most of the time they produce identical bytes, so this works in testing and even in production for a long while. Then one day it doesn't - a float gets formatted differently, or some encoding option differs and the signature silently stops matching. It's an infuriating bug to track down because nothing throws an error; the receiver just starts rejecting your webhooks with no obvious cause.



The fix is to serialize once and send that exact string:




CODE
// Serialize once, sign that string, send that exact string
$body = json_encode($payload);
$signature = hash_hmac('sha256', $body, $secret);

Http::withHeaders(['X-Signature' => $signature])
->withBody($body, 'application/json')
->post($url);






withBody() sends the raw string as-is, bypassing the client's own array-to-JSON step entirely. Now the bytes you signed and the bytes you sent are guaranteed to be the same bytes, by construction, not by coincidence.






Verifying on the receiving end



The exact-bytes principle applies just as much on the receiving side, and it's just as easy to get wrong there:




CODE
public function verify(Request $request, string $secret): bool
{
$expected = hash_hmac('sha256', $request->getContent(), $secret);

return hash_equals($expected, $request->header('X-Signature', ''));
}






Two details worth calling out:





  • $request->getContent(), not $request->all() or json_encode($request->json()->all()). The moment you decode the JSON and re-encode it for "readability," you've reintroduced the exact same bug on the receiving side - you're now comparing against a re-serialized version instead of the raw bytes that were actually signed.


  • hash_equals(), not ===. A plain string comparison short-circuits on the first mismatched byte, which leaks timing information an attacker could in theory use to guess the correct signature one byte at a time. hash_equals() runs in constant time regardless of where the strings diverge.






Where this came from



I ended up writing this up properly while building the webhook channel for on top for managing endpoints, browsing send history, and retrying failures - but the free package works standalone in any Laravel app, no Filament required.






Wrap-up



If you're signing webhooks anywhere in your stack, it's worth a quick audit for this exact pattern - a hand-rolled json_encode() followed by handing the array to an HTTP client is an easy thing to write without noticing the double serialization. Curious if others have hit this one, or have a cleaner pattern for it - let me know in the comments.

Vollständiger Original-Artikel
Den kompletten Beitrag mit allen Details direkt auf dev.to lesen.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
Bolt.new launches Forge to widen who gets to build with AI
1 Quelle
Common Pitfalls in RAG Applications: What to Avoid When Using Vector Search and Embeddings
1 Quelle
Turn Your Android Phone Into a Local Development Server With Termux
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Signing webhook payloads in Laravel - and the mistake that breaks it silently

Thematisch verwandte Begriffe: Signing, webhook, payloads, Laravel · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...