10 Consejos para tu API Security

Uno de los conceptos a los que como desarrolladores deberíamos prestar más atención es la seguridad de nuestros desarrollos y/o diseños pero, por desgracia, con frecuencia es uno de los aspectos que dejamos de lado, aplazándolo para el final en el mejor de los casos o ignorándolo en ocasiones, pensando que no es nuestra labor.

Como sin duda, como profesionales responsables, jamás deberíamos olvidarnos de este aspecto, vamos a ver unos cuantos consejos sobre qué tener en cuenta cuando estamos hablando del desarrollo y diseño de APIs.

10 no es un número arbitrario, sino que hace relación directa con la la nueva lista de amenazas para APIs de OWASP

1. Broken Object Level Authorization

No es extraño ver APIs con endpoints que manejan identificadores de objetos. Por ejemplo:

{endpoint_url}/api/user/info

nos puede devolver algo del estilo

{
"_id": 12,
"name": "Carter",
"lastname": "Hall",
"email": "myemail@me.com",
…
}

Si alguien ha logrado capturar nuestra autorización para este servicio y obtiene este dato, rápidamente puede ver que si hay un campo _id con valor 12, puede que lo haya con valor 13, 14,…. Y puede usar esta información para intentar usar otros servicios y no solo ya obtener información de otros usuarios, sino incluso tratar de modificarla.

¿Qué podemos hacer?

No utilizar identificadores que se puedan adivinar (mejor usar uuids), implementar controles de seguridad que aseguren que el usuario que está llamando a un servicio sea el dueño de los datos que está obteniendo y no exponer ids internos sino usar otros que no se puedan adivinar y luego mapearlos internamente podrían ayudarnos a estar protegidos ante esta amenaza.

2. Broken Authentication

Los mecanismos de autenticación a menudo se implementan incorrectamente, lo que permite a los atacantes comprometer los tokens de autenticación o explotar fallos de implementación para asumir las identidades de otros usuarios de manera temporal o permanente.

Access tokens, especialmente si nos los da un un servidor oauth por ejemplo, no deberían ser mecanismos de autenticación. No deberían ser pruebas de autenticación, porque por una razón u otra, ese token podría tenerlo otra persona y eso no debería asegurar que quien lo tiene, es quien se supone que debe.

Esta situación por ejemplo puede ser problemática si un único token te da acceso a todas las APIs de tu sistema. Lo que deberíamos hacer es restringir qué es lo que podemos hacer con un token y por ejemplo, en un mundo oauth esto lo podemos conseguir mediantes el uso de scopes. Pero esto tampoco es suficiente, porque los scopes dan cierta protección en lo que respecta a las operaciones, pero no a los datos a los que podemos acceder, y esto solo lo conseguiremos con mecanismos que autorización más detallados que contemplen a qué datos se tiene permiso acceder. Además, es buena idea asegurarnos de que los tokens tengan una vida corta, de manera que si se ven comprometidos, al poco tiempo dejen de ser válidos.

3. Excessive Data Exposure

Es típico ver cómo, con el fin de obtener implementaciones genéricas que nos ofrezcan reutilización, exponemos el máximo de datos relacionados con un recurso sin pararnos a pensar en la sensibilidad de los mismos, dejando en manos de los clientes que consumen el API la tarea de filtrar cuales deben ser mostrados. Un caso extremo sería en el que estuviésemos devolviendo una contraseña para un recurso “Usuario”, pero ilustra bastante bien a lo que nos referimos.

4. Lack of Resources & Rate Limiting

En muchas ocasiones nuestra APIs carecen de mecanismos que limiten el consumo que un cliente puede hacer de un API o de un recurso de la misma. Esta restricción podría ser a un usuario o cliente, asignada al API completa o a un subconjunto de la misma (recursos/verbos) y su ausencia no impacta únicamente en el rendimiento del servidor que habrá detrás del API, sino que habilita ataques como pudiera ser un “Denial of Service”(DoS) o incluso ataques por fuerza bruta con el fin de obtener algún tipo de autentificación.

5. Broken Function Level Authorization

Un ejemplo de esto es el mezclar dentro de la misma API tareas destinadas a ser ejecutadas por usuarios y otras por administradores. Seguramente unas estén pensadas para ser llamadas desde el exterior y otras desde el interior, pero están dentro del mismo API. Y esto puede ser un problema, porque alguien de manera malintencionada, puede tratar de averiguar ciertas cosas para empezar a ejecutar lo que no debe.

Por ejemplo, en un entorno REST, si conocemos el endpoint:

GET {endpoint_url}/api/user/123

es fácil de imaginar que exista

DELETE {endpoint_url}/api/user/123

Agentes malintencionados pueden averiguar qué paths están potencialmente disponibles para acceder a información que debería estar oculta.

Para evitar esto, deberíamos detectar qué verbos no pueden ser usados según la especificación del contrato del API, rechazar por sistema los que no están descritos en el mismo y, como siempre, detectar que el usuario tiene los permisos apropiados.

6. Mass Assignment

Aquí hablamos de que no es buena idea hacer una vinculación directa entre el modelo de datos que exista y los datos proporcionados por el cliente. Sin el correcto filtrado de propiedades, esto puede provocar que las propiedades de los objetos sean fácilmente adivinables, que se puedan explorar otros endpoints del API, acceder a documentación o añadir propiedades adicionales a los objetos en un payload que permita modificar propiedades que no deba ser modificables.

Por ejemplo, en un caso extremo, donde:

GET {endpoint_url}/api/user/info

nos devuelva


{
"name": "Carter",
"lastname": "Hall",
"email": "myemail@me.com",
"is_admin": false,
…
}

donde se puede ver cómo el modelo de datos está vinculado, tal vez alguien de manera malintencionada, trate de modificar esta información consiguiendo permisos:

PUT {endpoint_url}/api/user/info

con un payload


{
"name": "Carter",
"lastname": "Hall",
"email": "myemail@me.com",
"is_admin": true,
…
}

En este caso, aunque sea imposible que alguien acabe logrando saber que existe ese campo "is_admin", el API debería chequear este payload y reconocerlo como incorrecto.

7. Security Misconfiguration

Haciendo el símil con una cadena, se suele decir que tu seguridad será tan fuerte como lo será el más débil de sus componentes, y es por eso que debemos prestar atención a todos.

Debemos detectar cuanto antes vulnerabilidades en las librerías que usamos y actuar ante ellas (ojo con las dependencias de node, que puede hacer que directamente no sepas qué estás usando, por ejemplo).

Para este tipo de cosas, trata de limitar las dependencias externas, usa librerías bien conocidas y seguras, comprueba de manera continua que su sistema sigue siendo seguro tras cambios que realices en él y, por normal general, no trates de crear tu propio stack de seguridad, uso uno bien conocido y robusto.

Tengamos cuidado con el uso de configuraciones por defecto que puedan no aportar la seguridad que necesitemos y mucho cuidado con mensajes de error en los que expongamos demasiada información que luego puede ir en nuestro perjuicio.

8. Injection

También debemos tener cuidado con este tipico riegos de las aplicaiciones web. Ya no hablamos simplemente de la clásica inyección de SQL, la cual a día de hoy es menor habitual en gran medida por el uso de ORMs y el incremento del uso de noSQL, sino también de inyección también de código remoto o noSQL también.

Lo solución para esto vuelve a ser la validación de la entradas, cumpliendo las normas correctas y evitando dar de paso a este tipo de inyecciones.

9. Improper Assets Management

Este riesgo viene a hablarnos de tener un correcto gobierno de las APIs que tenemos en nuestras compañías. Necesitamos saber qué tenemos para saber qué debemos securizar y cómo. Nos ayudará a evitar tener versiones obsoletas y/o que ya no se usan expuestas y que puedan ser un problema de seguridad.

Todas las APIs deberían estar correctamente documentadas(hablamos de OpenAPI o de RAML, por ejemplo) y también de tener todo lo más automatizado posible donde el término de DevSecOps aparece como algo importante.

10. Insufficient Logging & Monitoring

Un logueo y/o monitorización insuficiente puede probar el reaccionar muy lento ante brechas de seguridad, permitiendo a atacantes aprovecharlas por demasiado tiempo.

Pero no solo eso, sino que también debemos proteger nuestros logs, que no dejan de ser una fuente de información muy sabrosa para cualquiera que pretenda hacer uso malintencionado de nuestros datos. Y ya de paso, no se lo pongamos fácil y tengamos cuidado con la información que escribimos en los logs: es mala idea escribir en ellos cosas como tokens, por ejemplo.

Todos estos problemas, debemos verlos como un conjunto, no como problemas separados. La mayoría están relacionados unos con otros y pensar en ellos de manera aislada puede hacer descuidar otros. La seguridad es un problema global que debe ser tratado globalmente.

 

Guía de Posibilidades Profesionales en el Ecosistema de Java