June 09, 2026

Stop Fake Webhooks: HMAC Signatures in Laravel APIs

By Paresh Prajapati • Lead Architect

Stop Fake Webhooks: HMAC Signatures in Laravel APIs

The Webhook Spoofing Vulnerability

When engineering a B2B SaaS platform at Smart Tech Devs, relying on external services like Stripe, Shopify, or GitHub is inevitable. These services communicate with your application via **Webhooks**—sending automated HTTP POST requests to your server when an event occurs (e.g., invoice.paid).

A catastrophic security flaw occurs when developers blindly trust the data hitting their /api/webhooks/billing endpoint. Because webhooks are publicly exposed URLs, any malicious actor can use Postman to send a fake JSON payload to your server claiming that a $10,000 enterprise invoice was successfully paid. If your controller processes that payload without verification, you just granted a hacker free lifetime access to your platform. To build zero-trust APIs, you must implement **HMAC Signature Verification**.

The Cryptographic Solution: HMAC-SHA256

Enterprise webhook providers do not just send a JSON body; they also send a cryptographic signature inside the HTTP headers (e.g., Stripe-Signature). This signature is a hash generated using the payload body and a secret key that only you and the provider know.

When your Laravel application receives the request, it must independently hash the incoming body using your copy of the secret key. If your computed hash matches the header signature exactly, the payload is mathematically proven to be authentic and untampered.

Architecting a Universal Webhook Guard Middleware

We enforce this verification at the Middleware layer, ensuring malicious requests never even reach our business controllers.


namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class VerifyWebhookSignature
{
    public function handle(Request $request, Closure $next): Response
    {
        // 1. Extract the signature provided by the external vendor
        $signatureHeader = $request->header('X-Vendor-Signature');

        if (! $signatureHeader) {
            \Log::warning('Webhook rejected: Missing signature header.', ['ip' => $request->ip()]);
            return response()->json(['error' => 'Unauthorized access'], 401);
        }

        // 2. Retrieve your secure webhook signing secret from the .env file
        $secret = config('services.vendor.webhook_secret');

        // 3. Compute the HMAC-SHA256 hash using the raw unparsed request body
        $computedSignature = hash_hmac('sha256', $request->getContent(), $secret);

        // 4. CRITICAL: Use hash_equals() to prevent Timing Attacks!
        // Standard string comparison (===) fails faster on incorrect characters, 
        // allowing hackers to guess the hash character by character.
        if (! hash_equals($computedSignature, $signatureHeader)) {
            \Log::alert('Webhook rejected: Invalid signature detected.', ['ip' => $request->ip()]);
            return response()->json(['error' => 'Invalid signature'], 403);
        }

        // The payload is authentic. Proceed to the controller.
        return $next($request);
    }
}

The Engineering ROI

By enforcing HMAC signature validation across your webhook routes, you achieve absolute cryptographic certainty over your incoming data streams. You completely eliminate the risk of payload spoofing, replay attacks, and unauthorized system mutations, securing your financial and operational pipelines against external manipulation.

Paresh Prajapati
Lead Architect, Smart Tech Devs
Insights Discussion Portal (0)
No discussions dispatched to this configuration matrix yet. Be the first to analyze!