Tech

Caso práctico de stress testing en nuestra tecnología de biometría

Así como un refrescante jugo de pomelos con miel y jengibre nos da la suficiente energía para aguantar las cargas del día a día, nosotros preparamos nuestros servicios e infraestructura con pruebas de carga, escalabilidad y resiliencia.

En este artículo queremos compartirte nuestra experiencia sobre Identity, nuestro producto para validación de identidad de usuarios. Se trata de nuestro sistema de seguridad biométrica que da de alta clientes y usuarios durante la apertura y activación de productos financieros powered by Pomelo. 

👉 Spoiler alert: sumamos un ejemplo práctico súper techie para detallar tooodo nuestro proceso. Pero si tu estilo es más TLDR (too long; didn’t read), tienes un resumen al final del blog post! 👈

Puntapié inicial

La tecnología biométrica en cuestión hará que el cliente/usuario del banco pase por una serie de filtros de validación de biometría facial, cuya magia interna se apoya en algoritmos que analizan si la persona demuestra estar viva. Además, chequea si el mapa de su rostro coincide en las tomas fotográficas que el sistema le vaya pidiendo en el transcurso del onboarding. Por ejemplo, una foto de cerca, una foto de lejos, una foto de su DNI o Cédula de identidad.

La API del SDK (Software Development Kit) expone algunos endpoints que son para generar el token de seguridad de la sesión de onboarding. También para decodificar las fotos capturadas en el proceso de biometría ya que se codifican en base64. 

El SDK cuenta con 2 algoritmos que miden indicadores de FAR/FRR (False Accept Rate for Spoofs y False Rejection Rate):

  • session-token: genera un token uuid por cada sesión de onboarding;
  • liveness-3d: el componente Liveness 2D-3D mide la efectividad de la biometría para identificar y verificar un humano vivo con FAR/FRR. Esto es, cuando te tomas la foto/selfie de tu rostro.
  • enrollment-3d: es el mismo proceso de liveness-3d, prueba que la persona esté viva. Pero además guarda en base de datos un registro de pruebas de vida que servirán como punto de comparación para futuros checks de liveness. Acá también registra un parámetro adicional externalDatabaseRefID para indexar la biometría en base de datos.

Validaciones de seguridad

Las validaciones de seguridad cuentan con márgenes de error y de sensibilidad. Por lo que cada implementador debe configurar hasta qué grado de umbral tolerar la inconsistencia de cierto patrón.

Match-3d-2d: Componente biométrico que analiza si los Facemap 3D alcanzan mínimo 1/125,000,000 FAR. Y como tal que provengan de humanos vivos. Ejemplo, tu selfie y la foto del DNI o cédula de identidad.

¿Por qué probar el rendimiento de SDK?

Por lo general, al emitir una tarjeta o un producto mediante correo masivo a los clientes, todos querrán darse de alta al mismo tiempo. Es por eso que el proceso de conversión, encriptación, desencriptación, comparación y registro de pruebas de vida exitosas y fallidas requiere un alto desempeño a nivel de infraestructura para poder brindar alta disponibilidad.

Para crear un capacity planning y un plan de mitigación de riesgos en ambientes de producción -realmente- necesitas métricas de rendimiento a nivel de API, Servicio, Cluster e Instancia.

Elementos clave que reciben alta concurrencia de uso nuestro sistema de biometría

  1. El HTTP Server es un actor importante. Por ejemplo, uno basado en Java, necesita tener una JVM optimizada.
  2. El componente de calibración de imágenes de la selfie y la foto del DNI.

Desafío

En resumen, hacer pruebas de carga sin ser baneados por el servidor SDK. Y, además de esto, pegarle al corazón del SDK.

El primer reto es simular pruebas de carga a una tecnología que por naturaleza rechaza peticiones de agentes tipo bot, crawlers, wrappers, DDoS, y herramientas de performance testing. Por tanto hay que hacerle creer al sistema que somos personas vivas.

El segundo reto es enviar peticiones al SDK de manera concurrente y sin estresar la máquina generadora de load testing. Lo cual generaría cuellos de botella, y al sistema bajo prueba no le haríamos mayor impacto.

Y, bien, cuál es nuestro objetivo final?

Pegarle al SDK con cargas de request por segundo y ver cómo reaccionan el balanceo del http-server propio de SDK, el componente de biometría y la infraestructura que lo sostiene.

Recursos a la mano

Como en una receta de cocina, siempre se necesitan varios elementos para comenzar: 

  1. Ambiente de pruebas en lo posible homologado a Producción – en este caso contamos con un AWS ECS. Para nuestro caso usamos instancias de tipo c6i.2xlarge
  2.  Una herramienta de load testing versátil y con un footprint lo más liviano posible en tu máquina de generación de usuarios virtuales. Para nuestro caso hemos usado K6.
  3. Postman
  4. New Relic como herramienta Logs, Métricas y Trazas.

Propuesta

Hay dos maneras de medir el performance del SDK de la tecnología de biometría: 

  1. Simulando llamados hacia el front end, y este se encarga de pegarle al backend;
  2. Saltándose el front y pegarle directo a los endpoints de SDK.

La propuesta y manera más sencilla para lograr nuestro objetivo es la número 2. Los endpoints que proponemos probar en este artículo son los siguientes, ya que permitieron lograr el objetivo planteado:

  • /session-token
  • /liveness-3d
  • /enrollment-3d

Es momento de ponerse techies 

El SDK recibe peticiones desde dispositivos Android, iOS, Mac, WIndows y Linux. Me enfocaré solamente en simular peticiones desde un Mac, como si me quisiera dar de alta desde el PC.

Nota: La mayoría de tecnologías de biometría facial manejan el mismo esquema de cliente-servidor que exponemos acá.

  1. Desde el browser debes activar las “Developer Tools” para analizar el tráfico en la pestaña “Network”. Una vez activado, abre la URL de sesion-identity, por ejemplo: “https://identity-stage.yoursite.com/iss-29hc40KvFEGFw8VkSBwmvbMywot”
  1. Una vez inicies tu onboarding, acepta términos y condiciones, y acepta el proceso de captura de foto/selfie.
  1. Abrir el request /session-token en Pestaña “Network” de las “developer tools” y vamos a extraer los siguientes parámetros del Header-Request
    1. User-Agent
    2. X-Device-Key
    3. X-User-Agent

Nos interesa traernos los valores X-Device-Key y el X-User-Agent. Los cuales vamos a insertar en nuestro script con k6 más adelante…

Ejemplo:

'X-Device-Key': 'duw7grMW0E0aQXhm2xWaknFpXLJZn1E3',
'X-User-Agent':'faceTechnology|sdk|web|identity-stage.yoursite.com|duw7grMW0E0aQXhm2xWaknFpXLJZn1E3|adeb284a-72a8-4da0-bb9d-23dd8874b0f1|MacIntel10_15_7|9.4.7|en-US|en|528bc9bc-7f93-4bf3-9726-7524a38e1229'

4. En el request /liveness, vamos a la sección Request o Solicitud. Y extraemos el payload de biometric_data, la biometría de tu selfie codificada base64:

 

Como podemos observar, el payload a nivel de front tiene la forma:

​​{
  "biometric_provider": "biometricTec",
  "biometric_data": {
    "metadata": {
      "face_scan": "yyyyyyy",
      "low_quality_audit_trail_image": "xxxxxxyyyyy/",
      "session_id": "23nb123-"
    "selfie": "xxxxxx",
    }
  }
}

5. Apoyado en la colección de Test Postman que nos provee nuestro proveedor del SDK de biometría, encontramos que el request POST /liveness3d tiene un body payload con menos campos. Y esos son los que vamos a usar en nuestro script.

6. Cruzando el request payload capturado por “developer tools” con el request payload de TestSuite Postman del proveedor SDK, vamos a armar nuestro request /liveness así:

{
    	"faceScan": "acá va el valor de face_scan",
"auditTrailImage": "acá va el valor de selfie",
    	"lowQualityAuditTrailImage": "acá va el lowQualityAuditTrailImage"
}

7. Resulta que /liveness y /enrollment son el mismo request y contesta la misma API, entonces en /enrollment necesitaremos  adicionar un campo más en el Payload llamado externalDatabaseRefID cuyo valor es un string único por sesión – en mi caso uso el mismo valor de session_id.

8. “session_id”, se puede extraer del Json Response al llamar el endpoint  {{SERVER_BASE_URL}}/session-token

Friendly reminder: El parámetro X-Device-Key,  lo sacamos mediante Developer Tools en el request /session-token cuando aceptas los términos y condiciones.

El session_id se corresponde con el “tid” que nos arroja el elemento “$callData.tid” equivalente a b14c3737-5cce-499a-a7db-5a65123b18d5

9. Ahora que ya tenemos el session_id, debemos armar nuestro header X-User-Agent:

facetechnolgy|sdk|web|identity-stage.yoursite.com||adeb284a-72a8-4da0-bb9d-23dd8874b0f1|MacIntel 10_15_7|9.4.7|en-US|en|b14c3737-5cce-499a-a7db-5a65123b18d5

Si quieres probar simulando Android o iOS, solo cambia el valor, por ejemplo: facetechnolgy|sdk|{{Android}}|identity-stage.yoursite….

10. Ya que se tiene entendido cómo se arman el  X-Device-Key y el X-User-Agent, deberás ponerlos en los header request para /liveness-3d y /enrollment

Poniendo todo en k6

En k6 tenemos la facilidad de construir scripts por separado y luego poder importarlos desde un main.js, en nuestro caso decidí hacerlo así:

1. Crear una función para cada endpoint a invocar, esto es /session-token, /liveness, /enrollment

2. Prepare un escenario de cargas enfocado en ramping-arrival-rate, donde K6 te permite setear la cantidad de RPS que necesites según tus necesidades. En este ejemplo k6 hará 2 iteraciones por segundo,( target :2 / timeUnit 1s) por 120 segundos, con pausas de 60 segundos.

Cada iteración se compone de 3 request al SDK :

1. /session-token    =>  2. /liveness    =>   3./enrollment

Por tanto la tasa de hits serán 2*(3 hits)/sec, permitiéndonos alcanzar un throughput en el SDK entre 3 y 6 hits/sec, ya que tendremos think times de por medio que alteren un poco el ritmo de concurrencia.

3. Luego armamos nuestra main default function, que integra los módulos importados.

Nota: externadlDabaseRefID lo cargo con un valor cualquiera, en este caso reuso el session_id, el cual también es nuestro token para autenticarnos en cada request

Función liveness3d.js

Pueden ver que antes de iniciar la función, cargamos el archivo que contiene el payload, el cual le deberemos insertar el externalDatabaseRFID y el session_id.

Función enrollment.js

Es básicamente la misma funcionalidad de Liveness-3d pero le agregamos el externadlDabaseRefID

Nota: Observen que en cada script se carga un archivo .json el cual tiene el payload de cada endpoint, y cuyo tamaño es mayor a 300 Mb dado el cifrado de imágenes de la captura biométrica.

Función TokenSession.js

Esta función es el primer request que hace la iteración, donde obtendremos el session_id del elemento  “callData”:

{
       "tid": "b14c3737-5cce-499a-a7db-5a65123b18d5",
	…..

Resultados

Recordemos que el objetivo final era, por un lado, evitar rechazos del agente de seguridad del lado de SDK a raíz de que le estamos enviando peticiones concurrentes con parámetros de biometría repetidos. Por otro lado, estimular el servicio que demanda infraestructura.

Ambos retos fueron logrados. Tenemos métricas de performance recolectadas por K6 a nivel de cliente, en este caso un Mac Intel M1, y también tenemos métricas de Cloudwatch, recolectadas por New Relic:

A nivel de logs, filtrando por “request started” podemos observar la cantidad de requests por segundo que logramos impactar en los endpoints, ver el timestamp:

¡Llegamos al final! 

Como prometimos, aquí está el resumen más breve y sin tanto código 😀 

Sí, es posible hacer pruebas de carga a la infraestructura que soporte el SDK de biometría y conocer su comportamiento con tráfico virtual. A pesar de que no estábamos simulando un escenario real ya que no involucramos a Match-3d por restricciones primitivas en la seguridad del SDK de biometría. Pero, al generar tráfico sobre liveness3d y enrollment se obtienen las “four golden metrics” que con seguridad te dan insights de lo que nuestra implementación del SDK es capaz de soportar. O de lo contrario, te dicen si debes considerar crear tu propia implementación de http-server que balancee las cargas hacia el SDK. Pero eso depende sobre todo de tu negocio y tu estimado de clientes concurrentes.

Vemos también que con la ayuda de K6 es fácil diseñar scripts que automaticen las peticiones concurrentes al sistema objetivo. Y, al mismo tiempo, permitirle a cualquier desarrollador un entendimiento de la lógica de los scripts.

…y bueno, otro servicio más de nuestro stack, que pasa por las respectivas pruebas de carga para brindar el mejor rendimiento a nuestros clientes.

Esperamos que les haya gustado y les sea útil para su producto o negocio! Siempre testeamos tecnología a la velocidad de Pomelo y, por supuesto, van a poder leerlo en el blog!

Comments are closed.