Separación de contratistas: dos niveles en lugar de uno
Cuando varias partes comparten una plataforma —el personal, los subcontratistas e incluso los contratistas de la competencia—, la autenticación por sí sola no basta para aislarlas. A continuación explicamos cómo planteamos crear esa separación de una forma que realmente sea eficaz.
La situación
Cada vez son más las organizaciones con las que trabajamos que utilizan plataformas compartidas en las que varias partes operan con los mismos datos de activos subyacentes. Una empresa de servicios públicos puede contar con equipos internos, un contratista principal de obras y dos o tres subcontratistas, todos ellos conectados a la misma red. Un ayuntamiento puede disponer de su propio personal, además de empresas de ingeniería externas. Es habitual —y cada vez más inevitable— que partes que compiten entre sí acaben trabajando en la misma aplicación.
La pregunta obvia es cómo se evita que esas partes vean el trabajo de las demás. La primera respuesta obvia es «dar a cada una de ellas un nombre de usuario». Eso permite la autenticación, que es necesaria pero no suficiente. La autenticación le dice al sistema quién eres. No le dice al sistema qué puedes ver. Sin una segunda capa de seguridad, cualquiera que inicie sesión lo ve todo.
Los planteamientos que no se sostienen del todo
Hay cuatro enfoques habituales, y cada uno tiene su lugar. Además, todos ellos tienen limitaciones que conviene reconocer con franqueza.
Implementaciones con inquilinos independientes. Proporciona a cada contratista su propia copia de la aplicación, su propia base de datos y su propio entorno. Se trata de un aislamiento real y hay casos en los que es la solución adecuada —normalmente cuando las partes no comparten nada y nunca van a colaborar—. Sin embargo, el coste aumenta rápidamente y va en contra de la razón de ser de la plataforma si el objetivo principal era la colaboración, la generación de informes agregados o una única fuente de información. Si la mayor parte de los datos se comparten y solo una pequeña parte es confidencial, los inquilinos separados son una exageración.
Ocultación solo en la interfaz de usuario. El atajo más habitual. Ocultar el botón, omitir la opción del menú, filtrar la vista de lista. Esto no es más que una medida de seguridad de fachada. Cualquiera que disponga de las herramientas de desarrollo del navegador, una solicitud directa a la API o una exportación bien dirigida puede recuperar los datos «ocultos». El registro sigue existiendo, sigue circulando por la red y sigue apareciendo en las exportaciones masivas. Evita que los usuarios honestos se topen con cosas que no deben ver; pero no detiene a nadie que realmente esté buscando.
Filtrado en la capa de aplicación, punto por punto. Un verdadero avance: aplicar el filtro en el código siempre que se consultan datos. Esto funciona... hasta que deja de hacerlo. La seguridad reside ahora en docenas de puntos de consulta. Cada nueva función es una posible fuga. Cada refactorización es una oportunidad para olvidarse de una. Este enfoque suele ser correcto en el momento de su lanzamiento, pero tiende a perder su eficacia a medida que crece el código.
Exportación a espacios de trabajo independientes. Copia los datos que necesita cada contratista a un espacio de trabajo al que solo él tenga acceso. Se trata de un entorno verdaderamente aislado, pero la copia queda desactualizada en el momento mismo en que se crea. Ahora te enfrentas a un problema de sincronización en lugar de a un problema de visibilidad, y los trabajadores sobre el terreno acaban viendo la versión de ayer del trabajo de hoy.
Ninguna de estas opciones es incorrecta en todas las situaciones. Son incorrectas como respuesta general para el acceso de múltiples usuarios en una plataforma compartida.
El modelo de dos capas
Nuestro enfoque divide la autorización en dos capas independientes que se combinan.
Los derechos de administrador son permisivos. Describen lo que un usuario puede hacer: crear una orden de trabajo, editar un informe, eliminar un artículo de stock. Sin un derecho de administrador, por defecto se pueden ver los datos, pero no modificarlos.
Las restricciones de roles son restrictivas. Describen lo que un usuario no puede ver ni modificar en absoluto. Cada restricción especifica un modelo (puntos, informes, asignaciones), un campo de ese modelo (propietario, estado, categoría), una comparación y un valor. Los registros que coincidan con estos criterios quedan ocultos para los miembros del rol en las operaciones especificadas —leer, editar, crear, eliminar— de forma independiente.
Estas capas no interfieren entre sí. Un trabajador de campo puede tener permiso para editar informes, mientras que una restricción de rol oculta cualquier informe que no haya redactado él mismo, de modo que puede editar, pero solo los suyos propios. El permiso de administrador otorga la capacidad; la restricción de rol limita el alcance. Ninguna de las dos capas necesita conocer la existencia de la otra, y esa independencia es la principal razón por la que el modelo sigue siendo válido con el paso del tiempo.
Por qué es importante la aplicación de reglas a nivel de campo en el lado del servidor
Las restricciones de roles se aplican en el servidor, antes de que los datos salgan de la base de datos. Esto es lo que realmente importa en la práctica:
- El filtro se aplica a todas las rutas de consulta: páginas de detalle, vistas de lista, llamadas a la API, exportaciones masivas y mosaicos de mapas.
- Un usuario que introduzca en la URL un ID de registro que conozca obtendrá un error 404 Not Found, ya que, para él, el registro realmente no existe.
- Una captura de pantalla de otro usuario no sirve de nada; los datos no se cargarán cuando el usuario con restricciones intente acceder a ellos.
- Las nuevas funciones heredan la aplicación de restricciones automáticamente, ya que pasan por la misma capa de datos del servidor.
El ocultamiento a nivel de interfaz de usuario no supera ninguna de estas pruebas. El filtrado a nivel de aplicación solo las supera si el desarrollador se acuerda de aplicar el filtro en cada nuevo punto de consulta. Las restricciones a nivel de campo aplicadas en el servidor hacen que la pregunta pase de ser «¿nos hemos acordado?» a «¿se ajustaban los datos a la regla?», que es precisamente la pregunta que queremos que responda el sistema.
Un ejemplo resuelto
Dos contratistas que trabajan en el mismo proyecto de instalación de fibra óptica. Ambos utilizan el mismo mapa, ambos utilizan las mismas capas base compartidas y ambos elaboran informes sobre su propio trabajo.
Crea un rol llamado Contratista A con una única restricción:
- Modelo: Point
- Campo: owner
- Comparación: =
- Valor de filtro: Contratista B
- Permisos bloqueados: leer, editar, crear, eliminar
Añade a los usuarios del contratista A como miembros. Haz lo mismo con el contratista B. Esa es toda la configuración.
Los equipos del contratista A ahora ven sus propios puntos y las capas compartidas, pero nada del contratista B. Si abren una lista de puntos, los registros del contratista B no aparecen en ella. Si exportan a CSV, la exportación se filtra. Si adivinan el identificador numérico de un punto del contratista B y lo pegan en la URL, reciben un error 404 Not Found. Al contratista B le ocurre exactamente lo contrario. Ninguna de las partes sabe cuánto trabajo ha realizado la otra, dónde se encuentra ni cuándo se actualizó.
Las infraestructuras básicas compartidas —rutas de cables, postes, conductos— siguen siendo visibles para ambas partes, ya que no se les aplica ninguna restricción. La colaboración se mantiene cuando se desea; el aislamiento se impone cuando es necesario. La plataforma no tiene que elegir entre ambas opciones.
Cuándo sigue siendo mejor optar por inquilinos independientes
El modelo de dos capas no es una solución universal. Si dos partes no comparten nada, nunca colaboran, nunca elaboran un informe conjunto y tienen motivos normativos para operar en infraestructuras físicamente separadas, entonces la opción más sensata es optar por inquilinos independientes. Lo que sustituye el modelo de dos capas es el caso mucho más habitual en el que las partes comparten la mayor parte de la plataforma y necesitan ocultar una parte específica. Ese es el caso en el que los inquilinos independientes suponen un gasto innecesario, dificultan la elaboración de informes y ralentizan el trabajo, y en el que el ocultamiento limitado a la interfaz de usuario presenta fugas de información.
El criterio que aplicamos es sencillo: si a las partes les resultaría útil ver las mismas capas base, generar los mismos informes y trabajar en el mismo mapa, entonces deben estar en la misma plataforma —con una separación real que se aplique en el nivel en el que realmente es viable.
Resumen
- El trabajo entre múltiples partes en plataformas compartidas es cada vez más habitual en los sectores de los servicios públicos, las telecomunicaciones y las administraciones municipales, y cada vez incluye a partes que compiten entre sí.
- La autenticación por sí sola no aísla a los usuarios; solo los identifica. Cualquiera que inicie sesión sigue teniendo acceso a todo, a menos que se añada una segunda capa de seguridad.
- Las implementaciones con inquilinos separados ofrecen un aislamiento real, pero van en contra del objetivo de una plataforma compartida cuando las partes necesitan colaborar con la mayor parte de los datos.
- Ocultar solo la interfaz de usuario es una farsa de seguridad: los datos siguen ahí y se pueden recuperar a través de herramientas de desarrollo, la API o exportaciones.
- El filtrado en la capa de aplicación es mejor, pero tiende a perder precisión a medida que crece el código base y se añaden nuevas funciones.
- El modelo de dos capas separa la autorización en derechos de administrador permisivos (lo que se puede hacer) y restricciones de roles restrictivas (lo que no se puede ver), que se aplican en el servidor campo por campo.
- La aplicación a nivel de campo en el lado del servidor significa que los datos restringidos realmente no existen desde la perspectiva del usuario restringido: ni en la interfaz de usuario, ni en la API, ni en las exportaciones, ni en una URL adivinada.
- La configuración que funciona es un único rol por contratista, y el resultado es el aislamiento donde se requiere, conservando las capas compartidas donde se desea la colaboración.
- Los inquilinos separados siguen teniendo su lugar cuando las partes no comparten nada; el modelo de dos capas es la mejor respuesta para el caso mucho más común en el que las partes comparten la mayoría de las cosas y necesitan ocultar una parte.