Securing Callbacks through Hashing

These settings are related to the technical integration of BitLabs.

1445

Hashing is really important to make the callbacks safe! Please do not use S2S callbacks without validating the hash.

In order to make callbacks safe, we are we append a "&hash=[HASH] at the end of each callback. This is an important feature for you to make sure that the callback, which you use as a base to reward your users, is coming from BitLabs and is not fake. This hash consists of a HEX encoded SHA1 HMAC. The whole URL is hashed with the secret key of the App. By checking the hash, you can be sure that the postback was actually sent by us and that it is legit.

Example

Inputs:
Callback URL: "https://publisher.com/complete?uid=8cc877ee-af19-488d-b28d-216fb866b996&val=500"

App Secret: "JLOIAUNMHFli7ZJOQVEzm98rzqnm9"

Hash: "dbcd6bb8ca677344592842a52b4fca9bec36cd4b"

Output (Final Callback Sent to the Publisher)
https://publisher.com/complete?uid=8cc877ee-af19-488d-b28d-216fb866b996&val=500&hash=dbcd6bb8ca677344592842a52b4fca9bec36cd4b

Hashing Code Examples

These code samples are meant to give you an idea of how to validate hashes. It can also be used as an example of how to handle callbacks in general.

const crypto = require('crypto');

function checkHash(url, secretKey) {
  const splitUrl = url.split('&hash=');
  const hmac = crypto.createHmac('sha1', secretKey);
  hmac.write(splitUrl[0]);
  hmac.end();
  return splitUrl[1] === hmac.read().toString('hex');
}

const secretKey = '';
const url = 'http://url.com?param1=foo&param2=bar&hash=3171f6b78e06cadcec4c9c3b15f858b8400e8738';
console.log(checkHash(url, secretKey));
<?php
// This has to be your App's secret key that you can find on the Dashboard
$secret_key = "";
// Get the currently active http protocol
$protocol = isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] === "on" ? "https" : "http";
// Build the full callback URL
// Example: https://url.com?param1=foo&param2=bar&hash=3171f6b78e06cadcec4c9c3b15f858b8400e8738
$url = "$protocol://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
// Save all query parameters of the callback into the $params array
$url_components = parse_url($url);
parse_str($url_components["query"], $params);
// Get the callback URL without the "hash" query parameter
// Example: https://url.com?param1=foo&param2=bar
$url_val = substr($url, 0, -strlen("&hash=$params[hash]"));
// Generate a hash from the complete callback URL without the "hash" query parameter
$hash = hash_hmac("sha1", $url_val, $secret_key);

//Check if the generated hash is the same as the "hash" query parameter
if ($params["hash"] === $hash) {
  echo "valid";
} else {
  echo "invalid";
}
using System;
using System.Web;
using System.Text;
using System.Security.Cryptography;
using System.Collections.Specialized;

public class Program {
  public static void Main() {
    string secretKey = "";
    string url = "http://url.com?param1=foo&param2=bar&hash=3171f6b78e06cadcec4c9c3b15f858b8400e8738";
    if (IsURLHashValid(url, secretKey)) {
      Console.WriteLine("valid");
    } else {
      Console.WriteLine("invalid");
    }
  }
  
  public static bool IsURLHashValid(string url, string secretKey) {
    Uri uri = new Uri(url);
    NameValueCollection queryString = HttpUtility.ParseQueryString(uri.Query);
    string urlHash = queryString.Get("hash");
    queryString.Remove("hash");
    string baseURL = uri.GetLeftPart(UriPartial.Authority);
    string urlValue = queryString.Count > 0 ? String.Format("{0}?{1}", baseURL, queryString) : baseURL;
    return urlHash == CreateHash(urlValue, secretKey);
  }
  
  public static string CreateHash(string urlValue, string secretKey) {
    HMACSHA1 hmac = new HMACSHA1(Encoding.UTF8.GetBytes(secretKey));
    byte[] buffer = Encoding.UTF8.GetBytes(urlValue);
    return BitConverter.ToString(hmac.ComputeHash(buffer)).Replace("-", "").ToLower();
  }
}