Stripe offer Stripe-Signature in headers when sending webhook notification.
Instead of checking if event exists on the Stripe API, which:
it's possible to use this method https://stripe.com/docs/webhooks#signatures
Few months ago, I switched from the StripeEvent::retrieve($id) approach on 2 larges projects (not with Cashier, my own implementation) to the Signature approach, that I feel more comfortable, and more robust.
It's easy to implement with a recent version of the Stripe library, and it can be done this way :
This require 2 new env variables.
What do you think ?
Without the signature verification, there really is no way to prevent malicious users from spoofing updates from Stripe. Depending on the application and the message spoofed, this could be bad.
Implementing this should be as simple as:
if (config('services.stripe.hook.secret')) {
// throws Stripe\Error\SignatureVerification exception on verification failure
Stripe\WebhookSignature::verifyHeader(
$request->getContent(),
$request->header('HTTP_STRIPE_SIGNATURE'),
config('services.stripe.hook.secret'),
config('services.stripe.hook.tolerance') // default in config file to 300 to match upstream
);
}
This feels like something that would make good middleware:
class WebhookController extends Controller
{
public function __construct()
{
$this->middleware('stripe.webhook');
}
}
Thanks @mcordingley,
The $request->header() method did not work for me, I had to use $request->server() instead.
Here is my working set up in case it helps someone.
// App\Http\Middleware\VerifyStripeWebhookSignature
public function handle($request, Closure $next)
{
if (! config('services.stripe.webhook.secret')) {
return $next($request);
}
try {
WebhookSignature::verifyHeader(
$request->getContent(),
$request->server('HTTP_STRIPE_SIGNATURE'),
config('services.stripe.webhook.secret'),
config('services.stripe.webhook.tolerance')
);
} catch (SignatureVerification $e) {
return abort(404);
}
return $next($request);
}
// config/services.php
'stripe' => [
'model' => App\User::class,
'key' => env('STRIPE_KEY'),
'secret' => env('STRIPE_SECRET'),
'webhook' => [
'secret' => env('STRIPE_WEBHOOK_SECRET'),
'tolerance' => env('STRIPE_WEBHOOK_TOLERANCE', 300),
],
],
// App\Http\Kernel
protected $routeMiddleware = [
// ...
'from.stripe' => \App\Http\Middleware\VerifyStripeWebhookSignature::class,
];
This looks like a valid and good addition 馃憤
Welcoming PRs for this!
I'll put one together later this week, once I have the time.
Thanks @mcordingley for adding this!
Most helpful comment
Thanks @mcordingley,
The
$request->header()method did not work for me, I had to use$request->server()instead.Here is my working set up in case it helps someone.