Verify Webhook
Now that you have an endpoint that is available on the internet, you want to be sure to verify and make sure that only requests coming from us are able to pass through into your systems and avoid dangerous actors. Using Convoy as our webhook provider, we’re able to support multiple verification means:
- IP whitelist
- Signature header validation
IP Whitelist
Requests/events from us can only come from these IP addresses:
- 159.223.160.239
- 147.182.169.205
With this, you block out every other request coming from other IPs, blocking out bad actors before hitting your endpoint at all.
Signature Header Verification
Requests from our systems are always signed with X-Bloc-Webhook
; you can verify this request headers hitting your endpoint as seen below:
app.post("/my/webhook/url", function(req, res) {
//validate event
const hash = crypto.createHmac('sha256', webhook_secret).update(JSON.stringify(req.body)).digest('hex');
if (hash == req.headers['X-Bloc-Webhook']) {
// Retrieve the request's body
const event = req.body;
// Do something with event
}
res.send(200);
});
<?php
// only a post with bloc signature header gets our attention
if ((strtoupper($_SERVER['REQUEST_METHOD']) != 'POST' ) || !array_key_exists('x-bloc-webhook', $_SERVER) )
exit();
// Retrieve the request's body
$input = @file_get_contents("php://input");
define('BLOC_SECRET_KEY','SECRET_KEY');
// validate event do all at once to avoid timing attack
if($_SERVER['HTTP_X_BLOC_WEBHOOK'] !== hash_hmac('sha256', $input, BLOC_SECRET_KEY))
exit();
http_response_code(200);
// parse event (which is json string) as object
// Do something - that will not take long - with $event
$event = json_decode($input);
exit();
?>
class BlocController < ApplicationController
skip_before_action :authorize_request, only: :webhook
def webhook
bloc_instance = BlocObject.instance
valid_event = bloc_instance.verify_webhook_event?(request)
raise StandardError, 'Phony event - Not Bloc' unless valid_event
render status: 200, plain: "Ok\n"
body = params.permit!
body = body.to_hash
HandleBlocWebhookJob.perform_later(body)
end
end
class BlocObject
include Singleton
WHITELISTED_IPS = ['159.89.231.210', '159.223.166.174', '159.65.239.138'].freeze
def initialize
@secret_key = ENV['BLOC_SECRET_KEY']
end
def verify_webhook_event?(request)
verify_ip_address(request.remote_ip) && verify_header_signature(request)
end
private
def verify_ip_address(ip_address)
WHITELISTED_IPS.include?(ip_address)
end
def verify_header_signature(request)
body = request.body.string
hash = OpenSSL::HMAC.hexdigest('SHA512', @secret_key, body)
hash == request.headers['HTTP_X_BLOC_WEBHOOK']
end
end
Updated 4 months ago