Verificar que los eventos sean enviados por Toku

Toku firma cada evento enviado a tus Webhook Endpoints con el campo Toku-Signature en el header del request POST al momento de notificar un evento. Esta es una firma que te permite verificar que es Toku y no un tercero quien envía la notificación.

Para verificar la firma, es necesario que poseas el secret relacionado al Webhook Endpoint, el cual se retorna al momento de su creación.

El header Toku-Signature contiene un timestamp y una firma, separados por una coma ,. El timestamp viene precedido de t=, y la firma viene precedida de s=. Un ejemplo de firma es:

't=1618960495,s=c896f1eb1438c706f4eb8b59d5453582b44a4cb442fd23ed9eb2690e1f9213b7'

Las firmas enviadas por Toku son códigos HMAC, generados al firmar el id del request con la función de hash SHA-256, en el momento indicado por el timestamp. Se utiliza como key de la función de hash el secret del Webhook Endpoint.

A continuación, explicamos los pasos a llevar a cabo para validar la firma del evento. Utilizamos como ejemplo el siguiente evento:

{
    "id": "evt_MOnNVXKNYDCZXzI9slA3smhASQmuRleM",
    "event_type": "payment_method.attached",
    "payment_method": {
        "id": "pm_9tN0ZtjUDjS1qi8qZQ3uJHJbwtcXYH9d",
        "customer": "cus_lq1wGjwgFyqQm4ACZx0QjE84qKm8fffa",
        "gateway": "transbank_oneclick",
        "card_type": "Visa",
        "card_number": "XXXXXXXXXXXX6623",
        "status": "chargeable"
    }
}

Los ejemplos de código utilizan el lenguaje Python.

Extraer el timestamp y la firma desde el 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.

header_value = request.headers.get('Toku-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 campo id, el cual se encuentra en el body del request.

message = '{}.{}'.format(timestamp, event_id)

En el caso del ejemplo, el mensaje sería:

'1618960495.evt_MOnNVXKNYDCZXzI9slA3smhASQmuRleM'

Generar tu firma

Firma el mensaje utilizando la función SHA-256 y el secret de tu Webhook Endpoint como llave.

import hmac
from hashlib import sha256

encoded_secret = YOUR_WEBHOOK_ENDPOINT_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 'Toku-Signature' presente en el header con la obtenida al firmar el evento. Si ambas firmas coinciden, entonces el evento ha sido enviado por Toku.

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.