A webhook endpoint accepting bodies up to eight megabytes, and the signature check running after the decode.
// the order that matters
if (! json_validate($raw)) {
return response()->json(['error' => 'invalid json'], 400);
}
if (! $this->signatureMatches($raw, $request->header('X-Signature'))) {
return response()->json(['error' => 'bad signature'], 401);
}
$payload = json_decode($raw, true, flags: JSON_THROW_ON_ERROR);
Validating before decoding costs one pass over the string and allocates nothing, which on an eight-megabyte body is the difference between a rejected request costing a kilobyte and costing sixty megabytes. Checking the signature against the raw string rather than the re-encoded payload is the other half — a decode-and-re-encode round trip changes key order and whitespace, and the signature no longer matches.