Verifying webhook signatures
To prevent third parties from impersonating AuthRamp to your application, verify that each payload sent to your application came from AuthRamp by inspecting its HMAC signature. Every time AuthRamp POSTs to your application, an HTTP header,
X-Authramp-Signature, is sent along with the content. It will contain an HMAC+SHA256 signature of the HTTP body payload.
Find your hmac signing key
Find your app's unqiue signing key on the Webhook Config page on AuthRamp.
You can generate a new HMAC signing key at any time, but you must use the new key immediately in your own app to avoid rejected webhook calls.
Included in each POST request AuthRamp sends to your servers, there will be a
X-Authramp-Signature header with a HMAC (SHA-512 digest) that should be validated against the body of the request.
Using secure comparison operators
Do not use a plain equality operator (
==) to compare signatures. Doing so will leave your application vulnerable to a timing attack. Instead, use your language's secure comparison function for a seucre verification of the signature.
import hashlib import hmac from secrets import compare_digest import flask from flask import Flask, request, Response app = Flask(__name__) # Get the secret key from AuthRamp -> Settings -> Webhooks WEBHOOK_SECRET_KEY = "YOUR SECRET KEY".encode("ascii") def verify_signature(): received_sign = request.headers.get('X-Authramp-Signature').strip() expected_sign = hmac.new(WEBHOOK_SECRET_KEY, request.data, digestmod=hashlib.sha512).hexdigest() if not compare_digest(received_sign, expected_sign): flask.abort(400) @app.route("/authramp-webhook/", methods=['POST']) def test_webhook(): verify_signature() # Request is legitimate return Response("All is good", mimetype='text/plain')
Be sure to use
Rack::Utils.secure_compare to compare the signature:
# Get the secret key from AuthRamp -> Settings -> Webhooks WEBHOOK_SECRET_KEY = "YOUR SECRET KEY" def verify_signature(payload, signature) Rack::Utils.secure_compare( Base64.encode64( OpenSSL::HMAC.digest( OpenSSL::Digest::Digest.new('sha512'), SHARED_SECRET, payload ) ).strip, signature) end def webhook_access verified = verify_signature(request.body.read, request.headers["X-Authramp-Signature"]) if verified return else render nothing: true, status: :unauthorized end end