Verify a webhook event
Verify that data received is unaltered and from Modern Treasury.
To verify that a webhook was actually sent by Modern Treasury, every payload is signed with a signature that is passed through as the HTTP header.
Webhook Key
Please contact support if your webhook key is compromised or accidentally made public. We will rotate the key and coordinate the change with you.
1. Retrieve your webhook key
You can find your webhook key in your Developer Settings.
2. Generate signature
The signature is hex encoded and can be replicated by applying HMAC-SHA-256 to the body of the webhook with your webhook key.
echo -n "{...}" | openssl dgst -sha256 -hmac "WEBHOOK_KEY"
3. Confirm signature
Webhook signatures are sent in the X-Signature
header. You can verify that Modern Treasury sent the event by comparing the signatures. If you feel your URL may be compromised, we recommend updating your Webhook URL. It is important not to parse the request body or manipulate the data before performing signature verification.
const express = require('express');
const { createHmac } = require('crypto');
const PORT = 3000;
const app = express();
app.use(
express.json({
verify: (req, res, buf) => {
// Sign the raw body with secret key
const hmac = createHmac('sha256', 'MY_WEBHOOK_KEY');
// Read the raw body
hmac.update(buf);
if ( hmac.digest('hex') !== req.headers['X-Signature'] ) {
throw new Error('Invalid signature!');
}
}
})
);
app.post('/', (req, res) => {
console.log(req.body);
res.send('OK')
})
app.listen(PORT, () => {
console.log(`Example app listening on port ${PORT}`)
})
require 'bundler/inline'
gemfile do
source 'https://rubygems.org'
gem 'rails', '~> 6.0'
end
require 'action_controller/railtie'
require 'logger'
Rails.logger = Logger.new(STDOUT)
class App < ::Rails::Application
routes.append do
root to: 'root#index'
end
end
class RootController < ActionController::Base
def index
# Read the raw body
body = request.body.read
Rails.logger.info "body #{body}"
# Sign the body with the secret key
hash = OpenSSL::HMAC.hexdigest("SHA256", "MY_WEBHOOK_KEY", body)
Rails.logger.info "signed hash #{hash}"
render json: { body: body, hash: hash }
end
end
App.initialize!
Rack::Server.start(app: App)
from flask import Flask
from flask import request
import sys
import hmac
import hashlib
import hmac
import hashlib
app = Flask(__name__)
@app.route('/', methods = ['POST'])
def entry_point():
# Read the raw body
data = request.get_data()
print(data, file=sys.stderr)
# Sign the raw body with secret key
digest = hmac.new(b'MY_WEBHOOK_KEY', msg=data, digestmod=hashlib.sha256).hexdigest()
print(digest, file=sys.stderr)
return { "body": data.decode('UTF-8'), "hash": digest }
if __name__ == '__main__':
app.run(debug=True)
Updated about 1 year ago