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.
¿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 ajenaLas 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
- Un servidor de recursos que declara los alcances de la API. Con el identificador
b2b, los alcances quedan comob2b/inventory.readyb2b/prices.read. - Un cliente de aplicación por cada empresa cliente, con secreto y habilitado solo para los alcances que esa empresa contrató.
- 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.readY 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:
| Mecanismo | Qué controla | Lo que hay que saber |
|---|---|---|
| Planes de uso de API Gateway | Tasa y cuota por cliente | Requieren 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étodo | Tasa total hacia la API | Protegen el conjunto; no distinguen entre clientes |
| Regla basada en tasa de AWS WAF | Ráfagas por IP u otra clave de agregación | Se asocia a APIs REST; las APIs HTTP no admiten WAF directamente |
| Concurrencia reservada de la Lambda | Ejecuciones simultáneas de la capa de negocio | Es el techo duro: fija el máximo de llamadas RFC simultáneas que la API puede lanzar contra SAP |
| Caché con TTL corto | Consultas repetidas sobre la misma bodega | Decisión de negocio: cuánta antigüedad tolera el stock. Se declara en el campo asOf |
| Timeout y circuit breaker | Comportamiento cuando SAP se degrada | Timeout 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 elrequestId. - 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
| Amenaza | Control | Dónde vive |
|---|---|---|
| Un sistema desconocido llama a la API | Client credentials con un cliente de aplicación por empresa | Cognito |
| Un cliente usa una operación que no contrató | Alcances exigidos por endpoint | Autorizador |
| Un cliente revocado sigue llamando | Estado del cliente verificado en cada petición, con TTL de caché acotado | Autorizador y DynamoDB |
| Un cliente consulta la bodega de otro | Verificación de pertenencia antes de llamar a SAP | Lambda de negocio |
| Un cliente satura el ERP | Cuotas, concurrencia reservada, caché y circuit breaker | API Gateway y Lambda |
| La API se usa como puerta a otras funciones de SAP | Endpoints cerrados y S_RFC restringido al grupo de funciones | Capa de negocio y SAP |
| El tráfico RFC es interceptado | Conectividad privada; SNC si se exige cifrado de extremo a extremo | Red |
| Se filtra la credencial de SAP | Usuario tipo Sistema, mínimo privilegio, Secrets Manager con rotación | SAP y AWS |
| Una consulta no se puede atribuir | Registros de acceso con cliente y requestId correlacionado | API 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.