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
- Lee el cuerpo como bytes, antes de que tu framework lo interprete como JSON.
- 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. - Calcula la firma esperada.
- Separa
X-Fital-Signaturepor comas y compara cadav1=en tiempo constante. Basta que una coincida. - Ignora las versiones que no conozcas, como
v2=: una versión nueva llegará junto av1=.
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.
# 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
fiEn 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:
| Caso | Qué cambiar |
|---|---|
| Firma de otro secreto | Una letra del secreto |
| Cuerpo alterado | Un espacio al final del cuerpo |
| Timestamp vencido | Adelanta el reloj más de 300 segundos |
| Timestamp no numérico | Agrega 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")daNaNy 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.