Securing Your Callback
Once your server is configured to receive callbacks, it'll listen for any request sent to the endpoint you configured. In order to secure your data, you will want to make sure the callbacks are coming from Fazz in order to avoid attackers sending malformed data. The easiest way to verify the callbacks is to validate the signature that is sent along with the request.
1. Retrieve the signing secret from your dashboard
Once you have saved your application's callback end-point, we will provide you with a "Signing Secret" for each end-point.
2. Calculate and compare HMAC Signature
a. Fazz gives you signature in the header of each callback
b. You can verify the signature to see if it did indeed came from Fazz
c. Calculate the signature by using the Signing Secret and callback payload body with HMAC-SHA256 algorithm
d. Use the calculated signature in (c) to match with (a)
Example:
# NOTE: It's recommended for you to put the secret in a secret file or environment variable.
# We are showing it directly in the code for illustration purposes only.
SIGNING_SECRET = "ss_5572cf13d099"
post '/callback' do
request.body.rewind
payload_body = request.body.read
verify_signature!(request.headers["Xfers-Signature"], payload_body)
puts "Got a valid request"
end
def verify_signature!(signature, payload_body)
generated_signature = OpenSSL::HMAC.hexdigest(
"SHA256",
SIGNING_SECRET,
payload_body,
)
raise "Invalid signature!" unless Rack::Utils.secure_compare(signature, generated_signature)
end
using System;
using System.Security.Cryptography;
using System.Text;
class Program
{
static void Main() {
string key = "ss_5572cf13d099";
string payload = "{\"id\":\"contract_12345678\"}";
using (HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key)))
{
byte[] data = hmac.ComputeHash(Encoding.UTF8.GetBytes(payload));
// Create a new Stringbuilder to collect the bytes
// and create a string.
var sBuilder = new StringBuilder();
// Loop through each byte of the hashed data
// and format each one as a hexadecimal string.
for (int i = 0; i < data.Length; i++)
{
sBuilder.Append(data[i].ToString("x2"));
}
// Return the hexadecimal string.
Console.WriteLine(sBuilder.ToString());
}
}
}
const express = require('express');
const crypto = require('crypto');
const bodyParser = require('body-parser');
const app = express();
const SIGNING_SECRET = "ss_5572cf13d099";
app.use(bodyParser.text());
app.post('/callback', (req, res) => {
const payloadBody = req.body;
const signature = req.headers["x-xfers-signature"];
verifySignature(signature, payloadBody);
console.log("Got a valid request");
res.status(200).send("OK");
});
function verifySignature(signature, payloadBody) {
const generatedSignature = crypto.createHmac('sha256', SIGNING_SECRET)
.update(payloadBody)
.digest('hex');
if (signature !== generatedSignature) {
throw new Error("Invalid signature!");
}
}
const PORT = 3000; // You can choose your desired port
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Updated about 1 year ago