Once your server is configured to receive payloads, it’ll listen for any payload sent to the endpoint you configured. For security reasons, you probably want to limit requests to those coming from Yardman. There are a few ways to go about this. You could opt to whitelist requests from Yardman’s IP address, but a far easier method is to set up a secret token and validate the information.
Secret token set up
You’ll need to set up your secret token in two places: Yardman and your server listening to incoming payloads.
Only the Account Admin is able to manage that. If you do not see the following options, you’re not the Account Admin.
To set your secret token on Yardman, click
Account & Settings
button across the top of your
account, then click Webhooks
.
Find desired webhook endpoint and click Edit
(or
set up a new one).
Fill out the Secret token
textbox. Use a random string with high entropy.
You may generate it by taking the output of the following command at the
terminal:
ruby -rsecurerandom -e 'puts SecureRandom.hex(20)'
Click Update Endpoint
.
Next, set up an environment variable on your server that stores this token. Typically, this is as simple as running the following command at the terminal:
export SECRET_TOKEN=your_token
Never ever hardcode the secret token into your app!
Validate Yardman payloads
When your secret token is set, Yardman uses it to create a hash signature with
each payload. This hash signature is passed along with each request in the
headers as X-Yardman-Signature
.
The goal is to compute a hash using your SECRET_TOKEN
, and ensure that the
hash from Yardman matches. Yardman uses a HMAC hexdigest to compute the hash, so
you could have your server to look a little like this:
require 'sinatra'
require 'json/ext'
post '/payloads' do
ensure_signature
request.body.rewind
payload_body = request.body.read
verify_signature(payload_body)
doc = JSON.parse(payload_body)
"I got some JSON: #{doc.inspect}"
end
def ensure_signature
return halt 400, 'Missing payload signature!' unless request.env['HTTP_X_YARDMAN_SIGNATURE']
end
def verify_signature(payload_body)
signature = 'sha1=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), ENV['SECRET_TOKEN'], payload_body)
return halt 422, 'Invalid payload signature!' unless Rack::Utils.secure_compare(signature, request.env['HTTP_X_YARDMAN_SIGNATURE'])
end
Your programming language and server implementations may differ from this code. Hence, there are a couple of very important things to point out:
- no matter which implementation you use, the hash signature starts with
sha1=
, using the key of your secret token and your payload body; - using a plain
==
operator is not advised; a method likesecure_compare
performs a constant time string comparison, which renders it safe from certain timing attacks against regular equality operators.