FITALAPI

Verificar la firma

Comprueba que cada entrega viene de FITAL, con muestras que corren tal cual.

Antes de confiar en una entrega, comprueba su firma con el secreto de ese destino. Si no coincide o está vencida, responde 400 y no proceses nada.

Qué se firma

La firma es HMAC-SHA256 hexadecimal, con el secreto del destino como clave, sobre el timestamp, un punto y el cuerpo crudo:

hmac_sha256(secreto, "<X-Fital-Timestamp>.<cuerpo crudo>")

El secreto es el que viste al crear el destino o al rotarlo. Úsalo tal como lo recibiste, como texto.

Paso a paso

  1. Lee el cuerpo como bytes, antes de que tu framework lo interprete como JSON.
  2. Lee X-Fital-Timestamp. Rechaza la entrega si trae algo que no sean dígitos, o si difiere de tu reloj por más de 5 minutos en cualquier sentido.
  3. Calcula la firma esperada.
  4. Separa X-Fital-Signature por comas y compara cada v1= en tiempo constante. Basta que una coincida.
  5. Ignora las versiones que no conozcas, como v2=: una versión nueva llegará junto a v1=.

Cada intento lleva su propio timestamp y su propia firma. Durante una rotación llegan dos firmas v1=: ver Rotar el secreto.

Vector de prueba

Estas muestras corren tal cual y responden aceptar. Todos sus datos son ficticios y están publicados a propósito: el secreto no pertenece a ningún destino y no sirve en ningún ambiente. Sustituye los datos por los de tu entrega para usarlas.

webhook-verificar.sh
# Vector público: todos los datos son ficticios.
CUERPO='{"id":"evt_5mPqW2xZr8TnC1vH7kLdS4bJ","type":"payment.completed","sequence":3}'
TS='1793800000'                   # X-Fital-Timestamp
FIRMAS='v1=e056165e0bbfa2c675a224a926a2cb2bf184f3cfbfc53a12fc9e7f0f6f2dddfe'
SECRETO_FICTICIO=a6906be5688f523ee1111208ff109c902394503a725418caf3a33c2f22a0a671
AHORA=1793800010             # en tu receptor: AHORA=$(date +%s)

ESPERADA=$(printf '%s' "$TS.$CUERPO" | openssl dgst -sha256 -hmac "$SECRETO_FICTICIO" -r | cut -d' ' -f1)
# Sólo dígitos: otra cosa no pasa por la ventana.
if printf '%s' "$TS" | grep -Eq '^[0-9]+$' && [ "$((AHORA > TS ? AHORA - TS : TS - AHORA))" -le 300 ]; then
  case ",$FIRMAS," in
    *",v1=$ESPERADA,"*) echo aceptar ;;
    *) echo rechazar ;;
  esac
else
  echo rechazar
fi

En la variante con openssl, la comparación no es en tiempo constante: úsala para depurar, no en tu receptor.

Comprueba los rechazos

Tu receptor debe rechazar estos casos. Cambia un dato del vector y confirma que responde rechazar:

CasoQué cambiar
Firma de otro secretoUna letra del secreto
Cuerpo alteradoUn espacio al final del cuerpo
Timestamp vencidoAdelanta el reloj más de 300 segundos
Timestamp no numéricoAgrega una letra al timestamp

fital-webhook-test.sh envía esos casos a tu receptor en tu máquina, además de un timestamp en el futuro, y termina con un código distinto de 0 si alguno no se rechaza: ver el inicio rápido.

Errores comunes

  • Firmar el JSON interpretado o vuelto a serializar. Cambia los bytes.
  • Convertir el secreto a bytes desde hexadecimal. Va como texto, tal como lo recibiste.
  • Comparar con ==. Usa una comparación en tiempo constante.
  • Convertir el timestamp a número sin validarlo. Number("abc") da NaN y se salta la ventana, y (int) recorta lo que sigue a los dígitos.
  • Mirar la ventana en un solo sentido. Un timestamp en el futuro también se rechaza.
  • Tomar sólo la primera firma. Durante una rotación pueden llegar dos.

Qué sigue

Catálogo de eventos.

Ver esta página en Markdown

En esta página