skip to content

Seguridad de una API sobre SAP: un JWT válido no basta

11 min read

Las cinco capas de seguridad de una API B2B sobre SAP en AWS: Cognito con client credentials, autorizador en API Gateway, autorización por objeto, protección de la disponibilidad del ERP con concurrencia reservada y un usuario RFC de mínimo privilegio.

Asegurar una API B2B que expone datos de SAP no es validar un token. Es responder cinco preguntas distintas en cinco capas distintas: quién llama, qué operaciones puede usar, si el objeto que pide le pertenece, cuánta carga puede trasladar al ERP y qué puede ejecutar la credencial técnica dentro de SAP. Un JWT válido solo responde la primera. La falla más frecuente en APIs reales vive en la tercera: OWASP la sitúa en el primer lugar de su Top 10 de seguridad de APIs de 2023 como Broken Object Level Authorization, el cliente autenticado que pide la bodega de otro cliente y la recibe. En una arquitectura sobre AWS, esas cinco capas se reparten entre Amazon Cognito con el flujo client credentials, un autorizador en API Gateway, la verificación de pertenencia en la capa de negocio, la concurrencia reservada de Lambda y un usuario RFC de mínimo privilegio en SAP ECC.

Este artículo es la mitad de seguridad de SAP ECC como API, donde explico por qué la API debe exponer capacidades de negocio y no el ERP.

1 · Identidad 2 · Autorización de API 3 · Autorización por objeto 4 · Disponibilidad 5 · Mínimo privilegio Cognito, client credentials: ¿quién llama? Autorizador: alcance del token y cliente activo Lambda de negocio: ¿la bodega es de este cliente? Cuotas, concurrencia reservada, caché, circuit breaker Usuario RFC tipo Sistema con S_RFC restringido 401 403 404 429 · 503 límite final
Cinco capas, cada una con una pregunta distinta y una forma distinta de decir que no. Ninguna asume que la anterior funcionó.

¿Por qué un JWT válido no basta?

Un JWT firmado por el grupo de usuarios de Cognito prueba una sola cosa: que el emisor lo entregó a un cliente registrado y que todavía no ha expirado. No dice nada sobre la bodega que ese cliente está pidiendo.

GET /v1/inventory/BODEGA-A
Authorization: Bearer eyJ...      → 200: la bodega es del cliente
 
GET /v1/inventory/BODEGA-B
Authorization: Bearer eyJ...      → 404: mismo token válido, bodega ajena

Las dos peticiones llevan el mismo token, con la misma firma, el mismo alcance y la misma expiración. Para cualquier validación de token son idénticas. La diferencia está en el dato, y solo puede verificarla un componente que conozca la relación entre clientes y bodegas.

Sobre el código de respuesta: 403 es honesto, pero confirma que BODEGA-B existe. 404 no le revela nada a quien esté enumerando identificadores. Uso 404 para recursos de otros clientes; lo importante es decidirlo una vez y aplicarlo igual en todos los endpoints.

¿Cómo se autentica un sistema, no una persona?

Los clientes B2B no consultan inventario desde un navegador. Lo consulta su ERP o su e-commerce, sin nadie delante. El flujo de OAuth 2.0 que corresponde a ese caso es client credentials, y Amazon Cognito lo soporta de forma nativa para comunicación máquina a máquina.

Las tres piezas en Cognito

  1. Un servidor de recursos que declara los alcances de la API. Con el identificador b2b, los alcances quedan como b2b/inventory.read y b2b/prices.read.
  2. Un cliente de aplicación por cada empresa cliente, con secreto y habilitado solo para los alcances que esa empresa contrató.
  3. Un dominio del grupo de usuarios, que publica el endpoint de token.

El sistema del cliente solicita el token así:

POST https://auth.ejemplo.com/oauth2/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)
 
grant_type=client_credentials&scope=b2b/inventory.read

Y lo envía como Bearer en cada llamada a la API hasta que expira.

Dos decisiones que parecen menores

Un cliente de aplicación por empresa, nunca uno compartido. Es lo que permite revocar a una empresa sin afectar a las demás, asignar alcances distintos y atribuir cada petición a alguien concreto.

Reutilizar el token hasta su expiración. Pedir un token nuevo por cada consulta multiplica las llamadas al endpoint de token, que tiene cuotas y costo, sin ganar nada en seguridad. Conviene dejarlo escrito en la guía de integración, porque es el error más habitual del lado del cliente.

¿Dónde se decide qué puede ver cada cliente?

La autorización se reparte en dos lugares, y el reparto no es arbitrario: depende de si la pregunta necesita conocer el recurso concreto.

Qué valida el autorizador

El autorizador de API Gateway responde las preguntas que no dependen del recurso: si el token tiene firma válida y no ha expirado, si trae el alcance que exige el endpoint y si pertenece a un cliente activo. Esta última requiere estado, así que el autorizador es una Lambda que consulta una tabla (DynamoDB basta) indexada por el client_id del token, y devuelve en su contexto el identificador interno del cliente.

Qué valida la capa de negocio

La pregunta «¿esta bodega es de este cliente?» la responde la Lambda que atiende el caso de uso, justo antes de llamar a SAP:

def handler(event, context):
    # En una API REST, el contexto del autorizador llega en requestContext.authorizer.
    # En una API HTTP, la ruta es requestContext.authorizer.lambda.
    cliente = event["requestContext"]["authorizer"]["cliente"]
    bodega = event["pathParameters"]["warehouse"]
 
    if bodega not in bodegas_del_cliente(cliente):
        return {"statusCode": 404}
 
    return consultar_stock(bodega)

Hay dos razones para no resolver esta verificación en el autorizador. La primera es el caché: API Gateway guarda la política que devuelve el autorizador indexada por token y la reutiliza en las llamadas siguientes con ese mismo token, aunque vayan a otro recurso. Una política construida para una bodega concreta termina denegando lo que debía permitir o permitiendo lo que debía denegar, según cómo se haya escrito. La segunda es estructural: la verificación viaja con el caso de uso. Quien agregue mañana GET /v1/prices/{warehouse} no depende de recordar una regla que vive en otro componente.

¿Cómo se revoca a un cliente sin esperar a que expire su token?

Un access token emitido es válido hasta su expiración, y quien lo valida no le pregunta a nadie: comprueba la firma y la fecha. Eliminar el cliente de aplicación en Cognito impide emitir tokens nuevos, pero no invalida los que ya circulan, que en Cognito pueden durar entre cinco minutos y un día.

La revocación efectiva es, por tanto, un cambio de dato: marcar al cliente como inactivo en la tabla que consulta el autorizador. El tiempo máximo hasta que el cambio surte efecto es el TTL del caché del autorizador. Por eso ese TTL no es un parámetro de rendimiento que se sube para ahorrar invocaciones: es el tiempo de revocación, y debe acordarse con el área de seguridad.

¿Qué protege a SAP de un cliente que hace cien peticiones por segundo?

Aquí el problema deja de ser de seguridad y pasa a ser de disponibilidad. Sin límites, cien peticiones por segundo contra la API se convierten en cien llamadas RFC por segundo contra el ERP. En SAP ECC, cada llamada RFC síncrona ocupa un proceso de trabajo de diálogo, el mismo recurso finito que usan los usuarios internos para facturar y despachar. Un cliente externo entusiasta puede degradar la operación interna sin que ningún control de seguridad lo detecte, porque todas sus peticiones están autorizadas.

Ningún mecanismo resuelve esto solo:

MecanismoQué controlaLo que hay que saber
Planes de uso de API GatewayTasa y cuota por clienteRequieren una clave de API por cliente. AWS los describe como best effort: no son un límite garantizado, y la clave de API no autentica a nadie
Límites por etapa o por métodoTasa total hacia la APIProtegen el conjunto; no distinguen entre clientes
Regla basada en tasa de AWS WAFRáfagas por IP u otra clave de agregaciónSe asocia a APIs REST; las APIs HTTP no admiten WAF directamente
Concurrencia reservada de la LambdaEjecuciones simultáneas de la capa de negocioEs el techo duro: fija el máximo de llamadas RFC simultáneas que la API puede lanzar contra SAP
Caché con TTL cortoConsultas repetidas sobre la misma bodegaDecisión de negocio: cuánta antigüedad tolera el stock. Se declara en el campo asOf
Timeout y circuit breakerComportamiento cuando SAP se degradaTimeout por debajo del límite de integración de API Gateway (29 s por defecto); tras varios fallos seguidos, responder 503 sin llamar a SAP

El control que elegiría si solo pudiera tener uno

Si tuviera que quedarme con una sola fila, sería la concurrencia reservada. Los planes de uso reparten la capacidad entre clientes; la concurrencia reservada decide cuánta capacidad existe. Se dimensiona desde SAP hacia afuera: cuántos procesos de diálogo pueden dedicarse a esta integración sin afectar la operación. Ese número, y no la demanda esperada de los clientes, fija el tope.

Reintentos: menos es más

Un reintento automático contra un SAP degradado no recupera la petición: duplica la carga en el peor momento posible. La capa de negocio no reintenta en cascada. Responde 429 o 503 con una cabecera Retry-After y deja que el cliente decida cuándo volver.

¿Cómo llega la Lambda a SAP sin exponerlo?

La red

La Lambda de negocio se conecta a subredes privadas de una VPC con ruta hacia la red corporativa, por VPN site-to-site o Direct Connect. Su grupo de seguridad solo permite salida hacia el gateway RFC de SAP, que escucha en el puerto 33NN, donde NN es el número de instancia (3300 para la instancia 00). Del lado corporativo, el firewall acepta ese puerto únicamente desde esas subredes.

Un detalle que suele pasarse por alto: RFC no cifra el tráfico por sí mismo. La VPN lo cifra en el tramo que recorre internet, y Direct Connect no lo cifra por defecto. Si la política exige cifrado de extremo a extremo hasta el servidor de SAP, la respuesta es SNC (Secure Network Communications), no la VPN.

La credencial

La última capa es la que debe resistir aunque todas las anteriores fallen. El usuario con el que la Lambda se conecta a SAP es de tipo Sistema, de modo que no puede iniciar sesión en diálogo. Su rol autoriza el objeto S_RFC únicamente para el grupo de funciones que contiene las funciones expuestas, más las autorizaciones de lectura que esas funciones necesiten. Nada más.

Con eso, si alguien obtuviera la credencial, o si un error en la capa de negocio permitiera construir una llamada no prevista, el daño posible queda acotado por el propio SAP a lo que la empresa decidió exponer. La credencial vive en AWS Secrets Manager, con rotación, y la Lambda la obtiene en tiempo de ejecución: nunca en variables de entorno en texto plano ni en el código.

¿Cómo se audita quién consultó qué?

Hay una asimetría que conviene resolver antes de que la plantee auditoría: para SAP, todas las consultas las hizo el mismo usuario técnico. El registro de auditoría del ERP no puede distinguir a un cliente de otro, porque nunca lo supo. La atribución tiene que vivir en AWS:

  • Registros de acceso de API Gateway con el identificador de cliente que devuelve el autorizador ($context.authorizer.cliente), la ruta, el código de respuesta y el requestId.
  • Registros estructurados de la Lambda con el mismo requestId, la bodega consultada y la función SAP invocada.
  • CloudTrail para los cambios de configuración: quién modificó un plan de uso, un cliente de aplicación de Cognito o los permisos de la tabla de clientes.

Con el requestId como hilo conductor, una pregunta como «¿quién consultó la bodega B el martes?» se responde con una consulta, no con una reconstrucción.

Resumen para una revisión de seguridad

AmenazaControlDónde vive
Un sistema desconocido llama a la APIClient credentials con un cliente de aplicación por empresaCognito
Un cliente usa una operación que no contratóAlcances exigidos por endpointAutorizador
Un cliente revocado sigue llamandoEstado del cliente verificado en cada petición, con TTL de caché acotadoAutorizador y DynamoDB
Un cliente consulta la bodega de otroVerificación de pertenencia antes de llamar a SAPLambda de negocio
Un cliente satura el ERPCuotas, concurrencia reservada, caché y circuit breakerAPI Gateway y Lambda
La API se usa como puerta a otras funciones de SAPEndpoints cerrados y S_RFC restringido al grupo de funcionesCapa de negocio y SAP
El tráfico RFC es interceptadoConectividad privada; SNC si se exige cifrado de extremo a extremoRed
Se filtra la credencial de SAPUsuario tipo Sistema, mínimo privilegio, Secrets Manager con rotaciónSAP y AWS
Una consulta no se puede atribuirRegistros de acceso con cliente y requestId correlacionadoAPI Gateway y Lambda

Ningún control de esta tabla es suficiente por sí solo, y esa es precisamente la idea. Seguridad por capas no significa acumular mecanismos: significa que cada capa responde una pregunta distinta y asume que la anterior puede fallar. Es la misma disciplina de convertir cada decisión en una consecuencia verificable que aplico cuando diseño plataformas empresariales en AWS.


Si estás preparando la defensa de una integración con SAP ante un comité de seguridad y quieres contrastar el diseño, puedes escribirme por LinkedIn.