While creating a new webhook, we have an option to provide a Secret key. The secret key is an optional field. Adding a secret key ensures that the webhook is genuinely from Neeto and not from a hacker.
Setting up the secret key on the server
You should set up an environment variable on the server that stores the secret key. Typically, this is as simple as running the following command:
export SECRET_KEY=YOUR-SECRET-KEY
Validating requests are from your Neeto product
When your secret key is set, your Neeto product uses that secret key to create a hash signature with each payload. This hash signature is included with the headers of each request as x-neeto-webhook-signature.
You should calculate a hash using your SECRET_KEY and ensure that the result matches the hash from your Neeto product.
Your language and server implementations may differ from the following examples. However, there are a number of very important things to point out:
No matter which implementation you use, the hash signature starts with
sha256=
.Using a plain
==
operator is not advised. A method likesecure_compare
performs a "constant time" string comparison, which helps mitigate certain timing attacks against regular equality operators.
Ruby example:
For example, you can define the following verify_signature function:
def verify_signature(payload_body)
signature = 'sha256=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ENV['SECRET_KEY'], payload_body)
return halt 500, "Signatures didn't match!" unless Rack::Utils.secure_compare(signature, request.env['HTTP_X_NEETO_CAL_SIGNATURE_256])
end
Then, you can call it when you receive a webhook payload:
post '/payload' do
request.body.rewind
payload_body = request.body.read
verify_signature(payload_body)
push = JSON.parse(payload_body)
"I got some JSON: #{push.inspect}"
end
Python example:
For example, you can define the following verify_signature function and call it when you receive a webhook payload:
import hashlib
import hmac
def verify_signature(payload_body, secret_key, signature_header):
"""Verify that the payload was sent from your neeto product by validating SHA256.
Raise and return 403 if not authorized.
Args:
payload_body: original request body to verify (request.body())
secret_key: neeto product webhook secret key (WEBHOOK_SECRET)
signature_header: header received from neeto product (x-neeto-webhook-signature)
"""
if not signature_header:
raise HTTPException(status_code=403, detail="x-neeto-webhook-signature header is missing!")
hash_object = hmac.new(secret_key.encode('utf-8'), msg=payload_body, digestmod=hashlib.sha256)
expected_signature = "sha256=" + hash_object.hexdigest()
if not hmac.compare_digest(expected_signature, signature_header):
raise HTTPException(status_code=403, detail="Request signatures didn't match!")