Vérification des signatures de webhook
Vérifiez les événements qu'Affirm envoie aux points de terminaison de votre webhook.
Affirm signe les événements webhook qu'il envoie à vos points de terminaison. Pour ce faire, nous incluons une signature dans l'en-tête "Affirm-Signature" de chaque événement. Cela vous permet de vérifier que les événements ont été envoyés par Affirm, et non par un tiers. Vous pouvez vérifier les signatures manuellement en utilisant votre propre solution.
Une signature webhook ne peut être configurée que par Affirm. Veuillez communiquer avec Affirm via le widget d'assistance.
Empêcher les attaques en replay
Une attaque par replay est lorsqu'un attaquant intercepte une charge utile valide et sa signature, puis les retransmet. Pour atténuer de telles attaques, Affirm inclut un horodatage dans l'en-tête Affirm-Signature. Étant donné que cet horodatage fait partie de la charge utile signée, il est également vérifié par la signature, donc un attaquant ne peut pas modifier l’horodatage sans invalider la signature. Si la signature est valide mais que l’horodatage est trop ancien, vous pouvez demander à votre application de rejeter la charge utile.
Tolérance d'horodatage
Nous vous recommandons de définir une tolérance par défaut (par exemple cinq minutes) entre l'horodatage et l'heure actuelle, et d'utiliser le protocole NTP (Network Time Protocol), afin de vous assurer que l'horloge de votre serveur est précise et se synchronise avec l'heure des serveurs d'Affirm.
Affirm génère l'horodatage et la signature chaque fois que nous envoyons un événement à votre point de terminaison. Si Affirm tente de relancer un événement (par exemple, votre point de terminaison a précédemment répondu avec un code de statut non-2xx), nous générons une nouvelle signature et un nouvel horodatage pour la nouvelle tentative de livraison.
Vérification manuelle des signatures
L'en-tête X-Affirm-Signature contient un horodatage et une ou plusieurs signatures. L'horodatage est préfixé par t=
, et chaque signature est préfixée par un schéma. Les schémas commencent par v
, suivi d'un entier. Actuellement, le seul schéma de signature valide est v0
.
x-affirm-signature:
t=1582267948,
v0=c5e024fb3c0d16efd329094f45fc8c1b00b89b6a648731f83a8f7115143ee9e123a0e19a8edd4be5d40274401dd6700b77dc4954efb8d93ac68611ad5d3e6446
Vous effectuez la vérification en fournissant la charge utile du webhook, l'en-tête X-Affirm-Signature
et le secret du point de terminaison. Si la vérification échoue, vous pouvez renvoyer une erreur.
Assurez-vous de consulter nos outils recommandés pour commencer.
- Pour gérer le webhook via HTTPS, nous vous recommandons d'utiliser cet outil plus spécifiquement.
- Vous pouvez trouver un exemple de code de signature webhook fonctionnel sur notre espace de codepen.
/*
Couple of things to set up. Those will also changes based on environment, production vs sandbox.
- The Authorization header is not useful, and no used as the secret.
- Get merchant private key. This is actually the secret used for encryption.
- Get the x-affirm-signature header from the webhook.
- Make sure to pick-up the body.
We use the CryptoJS library for encryption in this example. Please find the appropriate library based on your programming language.
Webhook signature validation supported:
- x-affirm-signature
*/
// Can be found in you Affirm dashboard (prod vs sandbox)
let private_key = "A3aut6z2VemhGHPgYF6uBFqczAm4VyyJ";
// Can be found in the webhook payload header
let x_affirm_signature = "t=1597184450,v0=f22309810ee2fc8f7f0ff41e0b1ceb74de98b5077385882e8f93c5d0f5ff86684e38c45531b3d34f07d5dd13a2e7c2c44ddb71d4e67e9a0b781a5976d18e0d42";
// Can be found in the payload (e.g Affirm only supports XML) of the Webhook
let body = "checkout_token=N8R79PUSKRP2UNAJ&created=2020-08-11T22%3A20%3A48.961423&email_address=john.doe%40affirm.com&event=opened&event_timestamp=2020-08-11T22%3A20%3A50.247581&total=60000";
const details = parseHeader(x_affirm_signature, "v0");
console.log(details);
if (!details || details.timestamp === -1) {
try {
throw new Error("Unable to extract timestamp and signature from header")
console.log("Unable to extract timestamp and signature from header")
} catch (e) {
console.log(e.name, e.message);
}
}
if (!details || details.signature === -1) {
try {
throw new Error("No signature found with expected scheme")
console.log("No signature found with expected scheme")
} catch (e) {
console.log(e.name, e.message);
}
}
// This is where the magic happens.
let payload = details.timestamp + "." + body;
let expectedSignature = CryptoJS.HmacSHA512(payload, private_key).toString(CryptoJS.enc.Hex);
console.log(expectedSignature);
if(expectedSignature == details.signature) {
console.log("Signature match with x-affirm-signature");
}
function parseHeader(header, scheme) {
if (typeof header !== 'string') {
return null;
}
return header.split(',').reduce(
(accum, item) => {
const kv = item.split('=');
if (kv[0] === 't') {
accum.timestamp = kv[1];
}
if (kv[0] === scheme) {
accum.signature = kv[1];
}
return accum;
},
{
timestamp: -1,
signature: -1,
}
);
}
Affirm génère des signatures à l'aide d'un code d'authentification par message (HMAC) basé sur le hachage avec SHA-512. Pour empêcher les attaques de rétrogradation, vous devez ignorer tous les schémas qui ne sont pas v0
.
Étape 1 : Extraire l'horodatage et les signatures de l'en-tête
Divisez l'en-tête, en utilisant le caractère,
comme séparateur, pour obtenir une liste d'éléments. Divisez ensuite chaque élément en utilisant le caractère =
comme séparateur pour obtenir un préfixe et une paire de valeurs.
La valeur du préfixe t
correspond à l'horodatage, et v1
correspond à la ou aux signatures. Vous pouvez supprimer tous les autres éléments.
Étape 2 : Préparer la chaîne signed_payload
Vous y parvenez en concaténant:
- L'horodatage, c.-à-d. la valeur de "t" (en tant que chaîne).
- Le caractère
.
. - La charge utile brute réelle ( par exemple. le corps de la demande)
Pour les événements de paiement, les requêtes envoyées par Affirm au point de terminaison de votre webhook sont accompagnées du
content-type
application/x-www-form-urlencoded
et de la versionapplication/x-www-form-urlencoded
des données du champ du corps. Les événements de préqualification sont envoyés avec la versioncontent-type
application/json
et une versionJSON
des données du champ du corps.
Étape 3 : Déterminer la signature attendue
Calculez un HMAC avec la fonction de hachage SHA512. Utilisez le secret de signature du point de terminaison (votre clé privée) comme clé et utilisez le chaîne signed_payload
comme message.
Étape 4 : Comparer les signatures
Comparez la ou les signatures dans l'en-tête à la signature attendue. Si une signature correspond, calculez la différence entre l'horodatage actuel et l'horodatage reçu, puis décidez si la différence est dans le seuil de votre tolérance.
Pour vous protéger contre les attaques temporelles, utilisez une comparaison de chaînes constantes pour comparer la signature attendue à chacune des signatures reçues.
Mis à jour 6 months ago