Security
How do you ensure that the Webhooks you recieve are from Mention Me, and have not been tampered with?
Whenever you create a new Webhook to your external system we strongly recommend that you:
- Choose a Secret
- Use the Secret in your external system to validate any incoming data and ensure it's authenticity.
If you follow both of these principles, you're already well on your way to securing your external systems from any bad actors attempting to deliver malicious Webhook data.
How It Works
Whenever a new Webhook is setup with a Secret, all events which are delivered to the external system will be accompanied by a HTTP Header with the key X-MentionMe-Signature
, and a value which will look something like sha256=<signature here>
(note that it will always begin with sha256=
followed by the signature value).
This signature can be used to:
- Validate that the Sender (i.e. Mention Me) knows the Secret which was entered when setting up the Webhook.
- Validate that the body of the Webhook (examples) have not been intercepted by a bad actor, and tampered with, after they have left the Mention Me platform.
It is the job of the external system to validate this signature and reject any Webhooks which do not match this signature.
Validation
We strongly advise using a secure comparison function rather than a standard string comparison (e.g.
==
), and to fully test the implementation before deploying to production.
The process of validating the signature of a Webhook is simple - you should:
- Extract the request body from the HTTP POST request.
- Retrieve the Secret value in your external system (which must be the same value as was provided when setting up the Webhook). We recommend that the Secret be stored securely as an Environment Variable.
- Generate the SHA256 hash, where the data is the request body, and the key is the Secret.
- Securely compare the resulting SHA256 hash against the one provided in the request headers.
- If the comparison determines that the hashes are equal, the Webhook's authenticity has been confirmed and can be processed. However, if this is not the case, the Webhook should be rejected.
Many programming languages support secure comparison of SHA265 hashes. Below are some examples which can be used as a starting point.
JavaScript / Node.js
const crypto = require('crypto');
const validateSignature = (request, secretToken) => {
const body = request.body;
const hmac = crypto.createHmac('sha256', secretToken);
hmac.update(body);
const signature = `sha256=${hmac.digest('hex')}`;
const mentionMeSignature = request.headers['X-MentionMe-Signature'];
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(mentionMeSignature));
}
PHP
function validateSignature(Request $request, string $secretToken) {
$signature = "sha256=" . hash_hmac("sha256", $request->getBody(), $secretToken);
return hash_equals($signature, $request->headers->get("X-MentionMe-Signature"));
}
Python
import hashlib
import hmac
def validate_signature(request, secret_token):
body = request.body.encode('utf-8')
hmac_obj = hmac.new(secret_token.encode('utf-8'), body, hashlib.sha256)
signature = 'sha256=' + hmac_obj.hexdigest()
mentionme_signature = request.headers['X-MentionMe-Signature']
return hmac.compare_digest(signature.encode('utf-8'), mentionme_signature.encode('utf-8'))
Ruby
require 'openssl'
def validate_signature(request, secret_token)
body = request.body
hmac = OpenSSL::HMAC.new(secret_token, OpenSSL::Digest.new('sha256'))
hmac.update(body)
signature = "sha256=#{hmac.hexdigest}"
mentionme_signature = request.headers['X-MentionMe-Signature']
return OpenSSL::SecureCompare.compare(signature, mentionme_signature)
end
Updated about 13 hours ago