Easily check webhook signatures
Ruby on RailsOne thing I had to do with all my projects that accept webhook events from my payment provider is to make sure that the incoming request is indeed legitimate.
That means, ahead of time, I know the signing secret from the payment provider and then can compare the payload to their signature. If it matches I accept the webhook event, and if it fails... erm, yeah, bye bye.
So, in my Rails application I need to check the request headers for any
extra data for example X-Signature
and also the request body. Then I
compare the two and see if it matches the signing secret. Huh?
Here are the important parts of the request:
request.headers["X-Signature"] request.body.read
request.body.read
is basically just the payload.
Next I have a private method called check_webhook_is_valid
. Here I
pass in the above, which will then return either true
or false
.
check_webhook_is_valid(request.headers["X-Signature"], request.body.read)
Nothing too bad here, and nothing complicated — I could do some nil checks here, but I don't want to over complicate anything.
Now to the hard bit... but don't worry, I will give you the answer 😬
def check_webhook_is_valid(signature, payload) hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ENV["SIGNING_SECRET"], payload) hmac == signature end
All these are built into Rails already so you don't need anything extra,
I pass in the signature (which you got from
request.headers["X-Signature"]
and then of course the payload.
Because I'm a good citizen I use an ENV[]
variable so I don't store
credentials in source.
The above code calculates a hmac
, which basically uses the signing
secret and the payload — that's what the payment provider does on their
end, and they send it through as the signature
.
Then I just compare my calculated hmac
signature with their
signature
— if they match, happy days and proceed with processing. If
not :unauthorized
🥸