Valida la firma del Webhook

Verifica que los eventos sean enviados por Fintoc

Fintoc firma cada evento enviado a tus webhook endpoints con el header Fintoc-Signature. Este header permite verificar que los eventos recibidos hayan sido enviados por Fintoc, y no por una tercera parte.

Fintoc genera un secreto que es entregado por única vez al momento de registrar un Webhook Endpoint. Este secreto es necesario para validar la firma entregada por Fintoc en cada evento.

Validar Firma

Cada evento envíado por Fintoc incluye el header Fintoc-Signature. La estructura del header es la misma para todos los eventos. Contiene un timestamp denotado por el valor de t, y una firma, denotada por el valor de v1. Un ejemplo del header Fintoc-Signature que puedes recibir junto a un evento es el siguiente:

t=1620870928,v1=4df951e02db34a3f333bccad26d207993e9b14d78ac77cec026091991f567f6d

Las firmas enviadas por Fintoc son códigos HMAC, generados al firmar el BODY del request con la función de hash SHA-256, en el momento indicado por el timestamp y utilizando el secreto del Webhook Endpoint que recibe el evento como key. Para validar la firma del evento debes:

Extraer el timestamp y la firma del header.

Separa el valor del header en una lista utilizando el carácter , como separador. Luego, separa cada elemento de la lista utilizando el separador = para obtener un par prefijo-valor. Finalmente, obtén los valores correspondientes de cada prefijo.

# using flask request
header_value = request.headers.get('Fintoc-Signature')
timestamp, event_signature = [x.split('=')[1] for x in header_value.split(',')]

Construir el mensaje que se firmará

El mensaje a firmar se compone al concatenar el valor del timestamp, el carácter . y el BODY del request.

import json

# using flask request
message = '{}.{}'.format(timestamp, request.get_data().decode("utf-8"))

Asegúrate de usar el body de la request crudo. Diferentes librerías pueden representar un JSON de distintas maneras. Un ejemplo del valor que debería tomar message sería el siguiente:

'1626102791.{"id":"evt_DyzYBwdC07ao5MqG","type":"link.credentials_changed","mode":"test","created_at":"2021-07-12T15:11:09.875Z","data":{"id":"link_00000000","mode":"test","active":true,"object":"link","status":"active","accounts":null,"username":"416148503","holder_id":"416148503","created_at":"2021-06-24T00:00:00.000Z","link_token":null,"holder_type":"individual","institution":{"id":"cl_banco_bbva","name":"Banco BBVA","country":"cl"}},"object":"event"}'

Generar tu firma

Firma el mensaje utilizando la función SHA-256 y el secreto del webhook endpoint como llave.

import hmac
from hashlib import sha256

encoded_secret = YOUR_WEBHOOK_SECRET.encode('utf-8')
encoded_message = message.encode('utf-8')
hmac_object = hmac.new(encoded_secret, msg=encoded_message, digestmod=sha256)
signature = hmac_object.hexdigest()

Comparar las firmas

Finalmente, compara la firma presente en el header con la obtenida al firmar el evento. Si ambas firmas coinciden, entonces el evento ha sido enviado por Fintoc.

import hmac

valid_signature = hmac.compare_digest(signature, event_signature)

Previene un ataque de reinyección

Para evitar un ataque de reproducción de mensajes, recomendamos que utilices una marca de tiempo. Al recibir y obtener el timestamp de un evento, verifica que este se encuentre dentro de tu rango de tolerancia. De no ser así, puedes descartar el evento.